蒋振飞的博客 - Selenium模拟浏览器抓取淘宝美食信息   
正在加载蒋振飞的博客...
V3.0
蒋振飞的博客

Selenium模拟浏览器抓取淘宝美食信息

发布时间: 2018年08月05日 发布人: 蒋振飞 热度: 899 ℃ 评论数: 0

声明:此篇文章主要是观看静觅教学视频后做的笔记,原教程地址:https://cuiqingcai.com/

    淘宝页面比较复杂,含有各种请求参数和加密参数,如果直接请求或者分析Ajax将会非常繁琐。Selenium是一个自动化测试工具,可以驱动浏览器去完成各种工作,比如模拟点击、输入和下拉等多种功能,这样我们只需关心操作,不需要关心后台发生了怎么样的请求下面对具体操作步骤进行详述。

实现流程

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

2.分析页码并翻页
    得到商品页码数,模拟翻页,得到后续页面的商品列表。

3.分析提取商品内容
    利用PyQuery分析源码,解析得到商品列表。

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

具体实现

1.声明一个browser用来操作
    这里的wait是在后面的判断元素是否出现时使用,第二个参数为等待最长时间,超过该值则抛出异常。

#创建一个浏览器对象
browser = webdriver.Chrome()

#十秒内寻找元素失败,将报出TimeoutException异常
wait = WebDriverWait(browser, 10)

2.声明好之后就需要进行打开网页、进行搜索的操作

def search():
    """
        作用:通过关键字搜索得到搜索内容第一页产品信息,并返回搜索内容共计页码
    """
    print('正在搜索')

    try: 
        browser.get('https://www.taobao.com')
        print(browser.page_source)
        
        # 获取input输入框对象
        input = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "#q"))  
        )#

        # 获取button提交对象
        submit = wait.until(
            EC.element_to_be_clickable(
                (By.CSS_SELECTOR, "#J_TSearchForm > div.search-button > button")
            )
        )

        # KEYWORD是config.py文件中定义的搜索关键字,可修改
        input.send_keys(KEYWORD)

        # 点击button提交按钮
        submit.click()

        # 通过CSS_SELECTOR选择器获取total页码
        total_page = wait.until(
            EC.presence_of_element_located(
                (By.CSS_SELECTOR, "#mainsrp-pager > div > div > div > div.total")
            )  
        )

        # 解析页面内容,并将数据保存至MongoDB中
        get_products()
        return total_page.text

    except TimeoutException:

        # 出现超时访问就重新调用一次search
        return search()

3.第一个页面操作之后,我们需要进行翻页操作,如下

def next_page(page_number):
    """
        作用:作人为翻页功能,并得到该页产品信息
        page_number:循环的页码,将页码写入input框中
    """
    print('正在翻页', page_number)
    
    try:
        # 获取input输入框对象
        input = wait.until(
            EC.presence_of_element_located(
                (By.CSS_SELECTOR, "#mainsrp-pager > div > div > div > div.form > input")
            )  
        )

        # 获取button提交对象
        submit = wait.until(
            EC.element_to_be_clickable(
                (By.CSS_SELECTOR, "div > div > div > div.form > span.btn.J_Submit")
            )
        )

        # 将input框清空
        input.clear()

        # 向input框中输入页码
        input.send_keys(page_number)

        # 点击button提交按钮
        submit.click()

        wait.until(
            # 验证输入框内的页码是否为当前高亮页码,如果是,则继续执行
            # text参数是否在该span元素内,此处需要注意text的参数位置
            EC.text_to_be_present_in_element(
                (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_number)
            )
        )
        
        # 获取产品信息并保存至MongoDB
        get_products()

    except TimeoutException:
        # 超时异常,则继续解析该页
        next_page(page_number)

4.写完搜索操作和翻页操作后,我们需要完成对每个页面的商品信息获取功能

def get_products():

    """
        作用:通过页面源代码,将产品信息保存至MongoDB中
    """
    # 通过CSS_SELECTOR选择器,验证页面中是否存在产品对象
    wait.until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "#mainsrp-itemlist .items .item"))
    )

    # 拿到页面源码
    html = browser.page_source
    
    # 通过pyquery找到产品对象
    doc = pq(html)
    items = doc('#mainsrp-itemlist .items .item').items()

    # 遍历每个产品对象,组建数据内容
    for item in items:
        product = {
            'image': item.find('.pic .img').attr('src'),

            'price': item.find('.price').text(),

            'deal': item.find('.deal-cnt').text()[:-3],

            'title': item.find('.title').text(),

            'shop': item.find('.shop').text(),

            'location': item.find('.location').text()
        }
        # print(product)

        # 将组建好的每个产品信息保存至MongoDB
        save_to_mongo(product)

5.获取信息之后则需要对信息进行存储,将数据库关键参数定义在config.py文件中

#!/usr/bin/env python
# coding=utf-8

# 本地的数据库
MONGO_URL = 'localhost'

# 数据库的名称
MONGO_DB = 'taobao'

# 数据库的表名
MONGO_TABLE = 'products'

6.将组建好的每个产品信息保存至MongoDB

# 创建一个MongoDB对象
client = pymongo.MongoClient(MONGO_URL)

# 创建一个数据库,注意是[]而不是()
db = client[MONGO_DB]

def save_to_mongo(result):
    """
        作用:将信息保存至MongoDB中
    """
    try:
        if db[MONGO_TABLE].insert(result):
            print('Successfully save to MongoDB', result)
            
    except Exception:
        print('Failed save to MongoDB', result)

7.爬取调度器

def TbMeishi_Spider():
    """
        作用:淘宝美食爬取调度器
    """
    try:
        total_page = search()

        # 通过正则将search返回的total页码内容解析成数字形式
        pattern = re.compile('(\d+)')
        total_page = int(pattern.search(total_page).group(1))
        # print(total_page)

        # 遍历页码,传入next_page函数中,做整站爬取
        for i in range(2, total_page + 1):
            next_page(i)
            
    except Exception:
        print('有错误产生...')
        
    finally:
        browser.close()

8.PhantomJS

    代码完成到这里已经可以结束了,但是使用Selenium模拟会发现每次运行程序都会打开一个浏览器,然后自动操作,可以最直接地观察到代码的运行,但是假如不想弹出浏览器的话,可以使用PhantomJS来实现一个无界面的webkit浏览器。虽然没有界面,但dom渲染、js运行、网络访问、canvas/svg绘制等功能都很完备,在页面抓取、页面输出、自动化测试等方面有广泛的应用。下载PhantomJS地址:http://phantomjs.org/download.html,下载完成后解压缩,将PhantomJS添加到环境变量中即可。

    在config.py文件中加上

# 关闭load-images的功能,加快运行速度,默认开启,开启disk-cache缓存,默认关闭
SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']
# 无界面浏览器,将browser改为
browser = webdriver.PhantomJS(service_args=SERVICE_ARGS)

# 设置PlantomJS的窗口大小,可能会影响内容下载
browser.set_window_size(1400, 900)

总结思考

1.目标网址不匹配
    一开始使用显示等待访问,通过CSS_SELECTOR的id选择器找到网页元素,可最后程序都执行异常,每次都是TimeoutExceptions的异常错误。经过反复排查,才发现淘宝网有很多个站点。
    我在程序中使用的是:http://www.taobao.com
    但是在进行网页分析的时候随意用百度搜了一个淘宝站点,它的站点是:https://uland.taobao.com/sem/tbsearch
    两个站点地址不一样,网页元素也不一样,自己竟然会犯这种错误,应牢记,正确的选择元素应该是下面这几行。

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

input = WebDriverWait(browser, 10).until(
    EC.presence_of_element_located((By.ID, 'q'))
)

button = WebDriverWait(browser, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search'))
)

2.text参数是否在该span元素内,此处需要注意text的参数位置

# 验证text参数是否与选择器选择的元素文本内容一致
text_to_be_present_in_element(locator, text_)

3.完整源码地址:https://github.com/XiaoFei-97/TbMeishi_Spider

打赏 蒋振飞

取消

感谢您的支持,我会继续努力的!

扫码支持
一分也是爱     一块不嫌多

点击 支付宝 或 微信 打赏蒋振飞

打开支付宝扫一扫,即可进行扫码打赏哦

评论列表