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

特别声明:

  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网络爬虫开发实战

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355