特别声明:
- 供交流学习使用,不得用作商业用途。
- 如有违规侵权,请联系删除。
一、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()一定秒数,让网页加载完毕,否则可能会找不到元素
-
- WebDriverWait对象(更推荐)
此方法可以显式等待网页加载
显式等待可以自定义等待的条件,用于更加复杂的页面等待条件
- 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文件中找到更复杂的对象。
-
- 使用pyquery库
类似JQuery
定位元素
此方法适合用作返回网页元素进行下一步处理
- 使用pyquery库
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()
操作元素
相应的,我们有:
-
- 直接用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() #点击登录按钮
-
- WebDriverWait对象的操作(更推荐)
也是可以显示等待加载时间
- 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('树莓派')
运行结果:
问题
- 淘宝模拟登录:
- 一开始我以为淘宝点击搜索商品是都会自动跳转到登录页面的,然后就想用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)
- 有时候用正则表达式匹配淘宝的页码,total = int(re.compile('(\d+)').search(total).group(1))会匹配失败,不知道究竟是加载不完全还是淘宝的一些反爬措施,有空再研究下
- 当爬取到一定页数的时候,淘宝会检查到页面的异常,然后就会卡在验证页面,出现手动也无法通过验证的情况,所以应该要多准备几个代理和IP,使用scrapy框架来爬取会更加方便。
改进
- 由于淘宝对Selenium的反爬做得比较好,所以登录需要手动扫码登录,如果需要模拟登录请参考以下链接:
- python模拟登陆淘宝(更新版)
- 如果需要更新MongoDB数据库,请参考:
- MongoDB 更新文档
- Mongodb数据更新命令(update、save)
- python 更新 MongoDB
- python与mongodb的交互 增删改差
-
- 账号和密码可以存储到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网络爬虫开发实战