美文网首页Python
【Selenium框架2】Selenium框架爬取淘宝商品信息

【Selenium框架2】Selenium框架爬取淘宝商品信息

作者: Geekero | 来源:发表于2020-09-16 16:07 被阅读0次

    特别声明:

    1. 供交流学习使用,不得用作商业用途。
    2. 如有违规侵权,请联系删除。

    一、Selenium框架介绍

    Selenium 是一个用于浏览器自动化测试的框架,可以用来爬取任何网页上看到的数据。
    简单地说,就是模拟一个人所有的网页操作的行为

    Selenium的下载与安装:

    • 安装:在终端输入 pip install selenium
    • 下载:下载Chromedriver,解压后放在…\Google\Chrome\Application\(右击Chrome图标,打开文件所在文件夹)
    • 环境变量:将该目录添加至环境变量:右键点击我的电脑----->属性--->高级系统设置---->环境变量------>在path路径下添加上文中…\Google\Chrome\Application\路径

    使用代码测试:
    Selenium的简单使用:

    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    driver = webdriver.Chrome()
    driver.get('http://www.baidu.com')
    
    elem = driver.find_element_by_xpath('//*[@id = "kw"]')  #查找输入框
    elem.send_keys('Python Selenium',Keys.ENTER)  #模拟点击回车
    print(driver.page_source)
    
    模拟百度搜索
    • 遇到的问题:报错显示缺少参数"Python3 Selenium自动化测试赋值出现:WebDriverException: Message: unknown error: call function result missing 'value'",发现的Chromedriver版本要跟当前Chrome版本一致才可以

    Chrome版本对应Chromedriver下载地址

    Selenium的优缺点:

    • 优点:Selenium可以爬取任何网页的任何内容,因为它是通过浏览器访问的方式进行数据的爬取,没有网站会拒绝浏览器的访问。 但是淘宝和知乎会对Selenium有反爬机制,需要进行伪装。
    • 缺点:时间以及内存消耗太大,可以开启无头模式headless或者PhantomJS webdriver缓解这个问题

    二、Selenium的使用

    跟人操作网页一样,都是需要查找和操作网页元素

    查找元素

    XXX表示用CSS、id、name、identifier、XPath定位、 超链接、DOM、 CSS selector进行定位。更多定位方式
    PS: 对于网页的定位路径,只需要在谷歌浏览器中,鼠标放到对应元素--右键--检查--在右边的网页源码相应位置--右键--复制--选择相应的路径定位方法

    • 1.直接用webdriver对象的查找
    driver.find_element_by_XXX() 
    查找符合条件的单个元素 
    driver.find_elements_by_XXX() 
    查找符合条件的一组元素
    

    此方法通常需要前面编写time.sleep()一定秒数,让网页加载完毕,否则可能会找不到元素

      1. WebDriverWait对象(更推荐)
        此方法可以显式等待网页加载
        显式等待可以自定义等待的条件,用于更加复杂的页面等待条件
    from selenium import webdriver
    from selenium.webdriver.common.by import By 
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    driver = webdriver.Chrome()
    driver.get('填入网站的url')
    wait = WebDriverWait(driver, 10)  构建WebDriverWait对象,10秒加载时间
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'CSS选择器定位路径')))  # 很多有经验的selenium用户推荐CSS定位方式,因为它比XPath更快。而且可以在HTML文件中找到更复杂的对象。
    
      1. 使用pyquery库
        类似JQuery
        定位元素
        此方法适合用作返回网页元素进行下一步处理
    from selenium import webdriver
    from pyquery import PyQuery as pq
    
    
    driver = webdriver.Chrome()
    driver.get('填入网站的url')
    html = driver.page_source()
    doc = pq(html)
    #pyquery(browser.page_source)就相当于requests.get获取的内容
    items = doc('CSS选择器路径').items()
    

    操作元素

    相应的,我们有:

      1. 直接用webdriver对象的操作
    from selenium import webdriver
    
    account = browser.find_element_by_xpath('//*[@id="TPL_username_1"]')
    account.clear()
    account.send_keys("用户名")
    passwd = browser.find_element_by_xpath('//*[@id="TPL_password_1"]')
    passwd.send_keys("密码")
    submit = browser.find_element_by_xpath('//*[@id="J_SubmitStatic"]')
    submit.click()  #点击登录按钮
    
      1. WebDriverWait对象的操作(更推荐)
        也是可以显示等待加载时间
    from selenium import webdriver
    from selenium.webdriver.common.by import By 
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC 
    
    input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input')))
    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_num)  # 输入输入框内容
    submit.click()      # 点击确定按钮
    

    三、Selenium项目实战(爬取淘宝商品)

    思路:


    爬取淘宝思路
    #! /usr/bin/env python
    # -*- coding:utf-8 -*-
    # Use for get infomation from taobao by ChromeDriver
    # Author:Robin; Created in 20190831
    from selenium import webdriver
    from selenium.webdriver.common.by import By 
    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 import ChromeOptions
    #from selenium.webdriver.common.keys import Keys
    #from selenium.webdriver import ActionChains
    from pyquery import PyQuery as pq 
    from pymongo import MongoClient
    #import random
    import time
    import re
    
    browser = webdriver.Chrome()  #创建webdriver对象
    wait = WebDriverWait(browser, 10) 
    
    #进入淘宝网,输入商品名称,返回页面
    def search(good):
        try:
            browser.get('https://www.taobao.com/')
            input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#q')))  # 搜索输入框
            submit = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button')))  # 搜索点击按
            keys = '{}'.format(good)
            input.send_keys(keys) #输入商品名
            submit.click()      #点击搜索
            total = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total')))
            get_products(good)
            return total.text   #总页数
        except TimeoutException:
            #login(browser)
            search(good)
            
    #跳转到下一页
    def next_page(page_num, good):
        print('正在爬取第{}页'.format(page_num))
        try:
            input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input')))     #页码输入框
            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_num)   #输入页码
            submit.click()   # 点击确定
            time.sleep(10)  #防止爬取过快
            wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_num)))  #检查是否跳转到正确页数
            get_products(good)
        except TimeoutException:
            next_page(page_num, good)
    #得到淘宝商品信息
    def get_products(good):
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item')))
        html = browser.page_source
        doc = pq(html)  # pyquery(browser.page_source)就相当于requests.get获取的内容, 构建PyQuery对象
        items = doc('#mainsrp-itemlist .items .item').items()    #获取所有的‘#mainsrp-itemlist .items .item’选择出来的内容下的各项
        for item in items:   # 每个item变量都是一个PyQuery对象,然后再调用它的find()方法,传入CSS选择器,就可以获取单个商品的特定内容了。
            products = {
                '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(products)
            save_to_mongo(products, good)
    # 保存数据到MongoDB
    def save_to_mongo(result, good):
        client = MongoClient('mongodb://localhost:27017')
        db = client.taobao
        set_name = '{}'.format(good)
        goods_set = db[set_name]   # 创建以商品名命名的数据集
        try:
            if goods_set.insert(result):  # 插入到MongoDB数据库相应数据集
                print("存储到MONGODB成功",result)
        except Exception:
            print("存储到MONGODB失败",result)
    
    def main(good):
        print('正在爬取第1页...')
        total = search(good)
        total = int(re.compile('(\d+)').search(total).group(1))
        for i in range(2, total+1):
            next_page(i, good)
        browser.close() #把浏览器关掉
    
    
    if __name__ == '__main__':
        main('树莓派')
    

    运行结果:

    运行结果
    数据库展示

    问题

    1. 淘宝模拟登录:
    • 一开始我以为淘宝点击搜索商品是都会自动跳转到登录页面的,然后就想用Selenium模拟人的登录,然后再利用selenium.webdriver的ActionChains的click_and_hold(slider).perform()click_and_hold(slider).perform() 方法,模拟人操作时候先快后慢的特点
    • 后面才发现淘宝识别的是浏览器发送的一些参数判定是Selenium还是普通浏览器
    • 但是感觉这个代码挺有意思的,或许以后也有用,也拿出来分享下
    from selenium import webdriver
    from selenium.webdriver.common.by import By 
    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 import ChromeOptions
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver import ActionChains
    from pyquery import PyQuery as pq 
    from pymongo import MongoClient
    import random
    import time
    import re
    
    ##选用开发者模式,创建一个浏览器对象,可避免被淘宝检测到是selenium模拟浏览器
    option = ChromeOptions()
    option.add_argument('--proxy-serve=127.0.0.1:8080')
    option.add_experimental_option('excludeSwitches',['enable-automation']) 
    #chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])#禁止打印日志 跟上面只能选一个
    browser = webdriver.Chrome(options=option)
    browser = webdriver.Chrome()
    wait = WebDriverWait(browser, 10)
    
    # 滑块移动轨迹计算:使得滑块先快后慢
    def get_track(distance):
        track = []
        current = 0
        mid = distance * (1/2)
        t = 2
        v = 0
        a = 4
        a1 = -4
        while current < distance:
            #滑条前1/2的部分加速滑动
            if current < mid:
                move = v * t + 1/2 * a *t * t   #物理上匀加速运动计算某一时间内的位移: s = v*t + (1/2)a*t^2
                current += move                 
                track.append(round(move))
                v = v + a*t                     # 加速运动物体某一时刻的瞬时速度         
            else:
                #滑条后1/2的部分加速度减慢
                move = v * t + 1/2 * a1 *t *t
                current += move
                track.append(round(move))
                v = v + a1*t
    
        return track
    
    def drag(length, xpath):
        if browser.find_element_by_xpath("{}".format(xpath)):
            slider = browser.find_element_by_xpath("{}".format(xpath))   #找到滑动按钮  
            track = get_track(length)  #模拟运动轨迹,速度先快后慢
            ActionChains(browser).click_and_hold(slider).perform()          # 按住滑块滑动
            for x in track: 
                ActionChains(browser).drag_and_drop_by_offset(slider, xoffset=x, yoffset=random.randint(1,3)).perform()
            ActionChains(browser).release().perform()
        else:
            pass
        
    # Selenium控制的情况下搜索商品,淘宝会自动跳转到登录界面
    def login(browser):
        try:
            button = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_Quick2Static')))
            button.click()
            account = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#TPL_username_1')))
            passwd = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#TPL_password_1')))
            submit = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_SubmitStatic')))
            account.clear()
            account.send_keys('用户名')
            passwd.send_keys('密码')
            submit.click()
    
            try:
                change_login = browser.find_element_by_id('J_Quick2Static')   #点击密码登录按钮,选择用密码方式登录
                time.sleep(3)
                change_login.click()     
            except  Exception:
                pass
    
            time.sleep(3)
            account = browser.find_element_by_xpath('//*[@id="TPL_username_1"]')
            account.clear()
            account.send_keys('用户名’)
            passwd = browser.find_element_by_xpath('//*[@id="TPL_password_1"]')
            passwd.send_keys('密码')
            
            try:
                drag(700, "//*[@id='nc_1_n1z']")
            except Exception:
                pass
    
            submit = browser.find_element_by_xpath('//*[@id="J_SubmitStatic"]')
            submit.click()  #点击登录按钮
    
            try:
                index_page = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '//*[@id="J_SiteNavHome"]/div/a/span')))
                index_page.click()
            except  Exception:
                login(browser)
    
        except TimeoutException:
            login(browser)
    
    if __name__ == '__main__':
        login(browser)
    
    1. 有时候用正则表达式匹配淘宝的页码,total = int(re.compile('(\d+)').search(total).group(1))会匹配失败,不知道究竟是加载不完全还是淘宝的一些反爬措施,有空再研究下
    2. 当爬取到一定页数的时候,淘宝会检查到页面的异常,然后就会卡在验证页面,出现手动也无法通过验证的情况,所以应该要多准备几个代理和IP,使用scrapy框架来爬取会更加方便。

    改进

    1. 由于淘宝对Selenium的反爬做得比较好,所以登录需要手动扫码登录,如果需要模拟登录请参考以下链接:
    1. 如果需要更新MongoDB数据库,请参考:

    更多Selenium技巧扩展阅读:

    可以用于掩饰Selenium,防止被识别出
    设置headless和关闭图片缓存,可以减少机器负荷,提高性能
    - chrome配置
    设置无头浏览器PhantomJS和关闭图片缓存
    - Windows下PhantomJS的安装和使用
    - Python3网络爬虫开发实战 1.2.5-PhantomJS的安装
    - Python爬虫利器四之PhantomJS的用法
    Selenium更多定位用法:
    - 菜鸟学自动化测试(五)-----selenium命令之定位页面元素
    更多CSS选择器用法:(PS:这又是一个大坑)
    - 【Selenium专题】元素定位之CssSelector
    - Selectors Level 3官方文档
    其他参考教程:
    - 网络爬虫(python项目)
    - 爬虫实践---Selenium-抓取淘宝搜索商品信息
    - (九)使用Selenium+Chrome/PhantomJS(模拟浏览器)抓取淘宝商品美食信息|Python3网络爬虫开发实战

    相关文章

      网友评论

        本文标题:【Selenium框架2】Selenium框架爬取淘宝商品信息

        本文链接:https://www.haomeiwen.com/subject/nuchyktx.html