虽然淘宝的页面数据是通过 Ajax 获取的,也就是可以通过开发者模式直接找到它请求数据的接口,并且发现返回的数据是 json 的格式;但是这里需要注意的是这些 Ajax 接口的参数比较复杂,包含了加密密钥,因此如果想自己构造 Ajax 参数是比较困难的。
既然那么困难搞到数据,那么有没有办法使抓取的成本第一点呢?当然有,一种是使用 特定的数据接口 ;另一种就是使用 selenium了,selenium 有个特点就是 可见即所得。
那么接下来我们开始观察页面效果图:
经过效果图的观察,我们不难发现规律:我们要加载商品列表的节点;页面跳转时通过页面输入框再点击“确定”按钮进行跳转,并且只需判断当前高亮的页码数是当前的页码数即可。
一、首先导入相应的模块:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from urllib.parse import quote
from pyquery import PyQuery as pq
二、加载及跳转逻辑的代码函数如下:
browser = webdriver.Chrome() # 浏览器对象
wait = WebDriverWait(browser, 10) # 加载等待最大时间
KEYWORD = "华为荣耀10"
def index_page(page):
print("正在抓取第 ", page, "页...")
try:
url = r'https://s.taobao.com/search?q='+ quote(KEYWORD)
browser.get(url)
if page > 1: # 当页码大于 1 的时候则进行跳转
# 节点加载出来,传入定位元组,如(By.ID, 'p')
input_word = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > input'))) # 输入页码框
# 节点可点击
submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager div.form > span.btn.J_Submit'))) # 页码跳转"确定"按钮
input_word.clear() # 清空编辑框
input_word.send_keys(page) # 将页码填充到输入框
submit.click() # 点击"确定"按钮
# 某个节点文本包含某文字
wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page))) # 翻页按钮列表选项数字
# 节点加载出来,传入定位元组,如(By.ID, 'p')
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-itemlist .items .item'))) # 渲染商品所在列表选项
# 解析数据
get_products()
except TimeoutException as e:
index_page(page)
到这里,你可以直接运行该函数看是否能成功加载源码,这里我不做演示;那么接下来在观察我们所要加载信息(商品图片、名称、价格、购买人数、店铺名称、店铺地址)的页面节点:
三、观察商品节点之后解析代码如下(由于截图大小有限,其它标签自行观察):
# 解析函数-提取商品信息
def get_products():
html = browser.page_source
doc = pq(html)
items = doc('#mainsrp-itemlist .items .item').items()
for item in items:
product = {
'image':item.find('.pic .img').attr('data-src'),
'price':item.find('.price').text(),
'deal':item.find('.deal-cnt').text(),
'title':item.find('.title').text(),
'shop':item.find('.shop').text(),
'location':item.find('.location').text()
}
print(product)
四、编写主函数并调用:
def main():
#page = 1 # 据观察总共有 100 页
for page in range(1, 11):
index_page(page)
if __name__ == '__main__':
main()
运行部分效果图如下:
如果我们不想爬取的时候有浏览器弹出,那么有两种方式可以做到:1- 使用 Chrome Headless 模式 (这个模式前面一章有介绍并使用过);2- 对接 PhantomJS
五、首先看第一种 -> 直接将 webdriver 的声明修改为:
#browser = webdriver.Chrome() # 浏览器对象
chrome_options = webdriver.ChromeOptions() # 获取 ChromeOptions 对象
chrome_options.add_argument('--headless') # 添加 headless 参数
browser = webdriver.Chrome(chrome_options=chrome_options) # 初始化 Chrome 对象
六、第二种 -> 同理,直接将 webdriver 的声明修改为:
path = r"E:\\KaiFaSoftware\\MyPython\\MyPhantomjs\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe"
# browser = webdriver.PhantomJS(executable_path=path)
# 我们还可以通过命令行配置,设置缓存和禁用图片加载功能,进一步提高爬取效率
SERVICE_ARGS = ['--load-images=false','--disk-cache=true']
browser = webdriver.PhantomJS(executable_path=path,service_args=SERVICE_ARGS)
这里值得注意的是:如果配置了环境变量则不需要 path 指定路径了,还有提供一个 PhantomJS 的下载地址 -> http://phantomjs.org/download.html
最后的话非常感谢崔老师的思路,嘿嘿,相信通过本次实战同学们应该学到了不少了;本次实战到此为止,感恩一切。。。 *^_^*