Python爬虫实战03:用Selenium模拟浏览器爬取淘宝美食

1 目标站点分析

淘宝页面信息很复杂的,含有各种请求参数和加密参数,如果直接请求或者分析Ajax请求的话会很繁琐。所以我们可以用Selenium来驱动浏览器模拟点击来爬取淘宝的信息。这样我们只要关系操作,不用关心后台发生了怎样的请求。

这样有个好处是:可以直接获取网页渲染后的源代码。输出 page_source 属性即可。

这样,我们就可以做到网页的动态爬取了。缺点是速度相比之下比较慢。

2 流程框架

  • 搜索关键词

利用Selenium驱动浏览器搜索关键字,得到查询结果后的商品列表。

  • 分析页码并翻页

得到商品页码数,模拟翻页,得到后续页面的商品列表。

  • 分析提取商品内容

利用PyQuery分析源码,解析得到商品列表。

  • 存储至MongoDB

将商品列表信息存储到数据库MongoDB。

3 爬虫实战

  1. 首先声明一个浏览器对象browser,有头或者无头。有头的是带界面的,每步操作你都可以看到。无头则是不带界面,在后台运行,速度较快,这里选择无头chrome,配置参数如下。
  2. 可能会出错的地方用try...except包含起来,保证程序的正常运行。如果发生错误,再次搜索。
  3. 然后进入淘宝首页,检查源代码,找到输入框的元素和搜索按钮的元素,可右键复制为css选择器。这里就可以输入文字,以及点击按钮。
  4. selenium之等待。防止元素还没加载完成就运行下一步而报错。 调用WebDriverWait(browser, 10) ,后面会频繁调用,所以这里改写为wait。关于selenium等待的 更多用法,点击文档。
# browser = webdriver.Chrome()  # 有头chrome
# 无头chrome 设置参数
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(chrome_options=chrome_options)

wait = WebDriverWait(browser, 10)  # 改写一下,后面会频繁应用
def search():
    """搜索商品信息"""
    print('正在保存第{}页'.format(1))
    try:
        browser.get('https://www.taobao.com')
        # 声明搜索框元素,并且设置等待,presence_of_element_located条件是判断已经加载出来
        input = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "#q"))  # 这里的css选择器,右键网页源代码复制可得
        )
        # 声明搜索按钮,等待,element_to_be_clickable判断按钮是可以点击的
        submit = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button'))
        )
        input.send_keys('美食')  # 输入文字
        submit.click()  # 点击按钮
        # total 为总页数 presence_of_element_located判断total元素已经加载完成
        total = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total'))
        )
        get_products()
        return total.text  # 返回总页数
    except TimeoutException:
        return search()  # 如果发生错误,再次搜索。

3.2 翻页 next_page(page_number)

  1. 在页码框中输入页码,点击按钮实现翻页。检查高亮元素是否在当前代码判断是否翻页成功,这里用到等待条件text_to_be_present_in_element
def next_page(page_number):
    """下一页 """
    print('正在保存第{}页'.format(page_number))
    try:
        input = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "#mainsrp-pager > div > div > div > div.form > input"))
            # 这里的css选择器,右键网页源代码复制可得
        )
        # 等待,判断按钮是可以点击的
        submit = wait.until(
            EC.element_to_be_clickable(
                (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit'))
        )
        input.clear()  # 清空输入框
        input.send_keys(page_number)  # 输入页码
        submit.click()
        # 判断高亮是否是当前页数,从而判断是否翻页成功
        wait.until(
            EC.text_to_be_present_in_element(
                (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'),
                str(page_number))
        )
        get_products()
    except TimeoutException:
        next_page(page_number)  # 若失败,在试一次

3.3 获取商品信息 get_products()

这里用chrome的工具分析出来的代码,右键复制的css选择器不知道为什么不行。所以用的是css选择器的另外一种写法。

def get_products():
    """获取商品信息"""
    # 判断items元素加载成功
    wait.until(
        EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item'))
    )
    # 解析,先取得网页源代码
    html = browser.page_source
    doc = pq(html)
    items = doc('#mainsrp-itemlist .items .item').items()  # items()返回一个生成器,生成每个item
    for item in items:
        product = {
            'image': item.find('.pic .img').attr('src'),
            'price': item.find('.price').text().replace('\n', ''),
            'deal': item.find('.deal-cnt').text()[:-3],
            'title': item.find('.title').text(),
            'shop': item.find('.shop').text(),
            'location': item.find('.location').text()
        }  # 构造字典,存储商品信息,准备存入数据库
        save_to_mongo(product)

3.4 保存到数据库 save_to_mongo(result)

新建一个配置文件 config.py,保存了连接mongodb所需的参数。

"""config.py"""
MONGO_URL = 'localhost'
MONGO_DB = 'toabao'
MONGO_TABLE = 'meishi'
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(result):
    """保存到数据库"""
    try:
        if db[MONGO_TABLE].insert(result):
            print('存储到数据库成功', result)
    except Exception:
        print("存储失败")               

3.5 主函数 main()

def main():
    try:
        total = search()
        total = int(re.compile('(\d+)').search(total).group(1))  # 强制转换为int类型
        for i in range(2, total + 1):
            next_page(i)
    except Exception:
        print('something wrong~!')
    finally:  # 保证浏览器关闭
        browser.close()

3.6 头文件

import re

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from pyquery import PyQuery as pq
from selenium.webdriver.chrome.options import Options
import pymongo

from config import *

3.7 运行

if __name__ == '__main__':
    main()

3.8 结果

Python爬虫实战03:用Selenium模拟浏览器爬取淘宝美食_第1张图片

3.9 注意

多用try…except…finally保证程序的正常运行。

使用Selenium中,记得在关键操作前加入等待判定,确保所需元素加载完成或者达到我们想要的状态。

你可能感兴趣的:(爬虫)