【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的反爬做得比较好,所以登录需要手动扫码登录,如果需要模拟登录请参考以下链接:
  • python模拟登陆淘宝(更新版)
  1. 如果需要更新MongoDB数据库,请参考:
  • MongoDB 更新文档
  • Mongodb数据更新命令(update、save)
  • python 更新 MongoDB
  • python与mongodb的交互 增删改差
    1. 账号和密码可以存储到json或者pickle打包的配置文件里面,需要时回调,方便代码维护和信息安全

更多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框架爬取淘宝商品信息)