selenium 高级用法--excepted_conditions和WebDriverWait
看了崔静觅大大的博客,这里算是写出自己对内容的一些理解还有一个实操的小项目:
selenium爬取淘宝商品
另外附上另一篇
selenium爬取京东商品
环境: python3, pymongo, selenium, Chromedrive,chrome浏览器, pyquery,mongo数据库
先说一下爬虫的思想:
对于淘宝来说使用了js进行加密数据,我们这里使用可见即可爬的工具selenium进行爬取,过程如下:
分析请求-> 找到目标信息-> 构造请求-> 提取信息-> 自动执行(翻页)
整个小demo有4个函数:
- save_to_mongo()
储存数据的接口 - get_products()
获取每页商品的信息 - index_page()
判断是否加载,执行完成后推送给页面分析
*main()
实现翻页功能
这里会详细介绍每行代码的意义,上代码:
import pymongo
from selenium import webdriver
from selenium.common.exceptions import TimeoutException # 超时错误
from selenium.webdriver.common.by import By # 选择方式的模块
from selenium.webdriver.support.wait import WebDriverWait # 显示等待
from selenium.webdriver.support import expected_conditions as EC # 条件支持
from urllib.parse import quote # 屏蔽特殊字符
from pyquery import PyQuery as pq # pyquery 查找分析节点
# 创建一个谷歌浏览器对象
browser = webdriver.Chrome()
# 设置等待时间最长为3s
wait = WebDriverWait(browser, 3)
# 搜索关键字,可以整合到config文件里
KEYWORDS = 'iphone'
# mongo的地址,这里可以使用远端的地址,实现分布式
MONGO_URL = 'localhost'
# 数据库名字
MONGO_DB = 'taobao'
# 数据库集合
MONGO_COLLECTION = 'products'
# 创建一个数据库对象
client = pymongo.MongoClient(MONGO_URL, 27017)
# 指定数据库名
db = client.taobao
# 指定爬取页码
MAX_PAGE = 100
def save_to_mongo(result):
'''
把结果保存起来
:param result: 解析过后的商品信息
:return:
'''
try:
if db[MONGO_COLLECTION].insert(result): # 这里使用了插入操作,更详细的应该是有去重回滚等操作这里不做演示
print('save result successfully')
except Exception:
print('fail to save')
def get_products():
'''
提取商品的数据
:return:
'''
html = browser.page_source # 提取渲染过后的源码
doc = pq(html) # 创建pyquery对象,用于css选择器来提取信息
items = doc('#mainsrp-itemlist .items .item').items()
for item in items:
# 提取商品的信息
product = {
'image': item.find('.pic .img').attr('data-src'),
'price': item.find('.price').text(),
'deal': item.find('.deal-cnt').text(),
'title': item.find('.title').text(),
'shop': item.find('.shop').text(),
'location': item.find('.location').text()
}
print(product)
# 传送给储存功能函数
save_to_mongo(product)
def index_page(page):
'''
抓取索引页
:param page:页码
:return:
'''
print('正在爬取第{}页...'.format(page))
url = 'https://s.taobao.com/search?q=' + quote(KEYWORDS)
browser.get(url)
print(page)
if page > 1:
print(page)
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > input'))) # 指定商品页面元素加载出来就继续往下执行,如果到时间没有加载会直接报错
print(input)
submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager div.form > span.btn.J_Submit'))) # 当选择的节点可以被点击的时候我们才继续往下走,超时报错
input.clear() # 清理输入框
input.send_keys(page) # 键入值
submit.click() # 点击操作
print('done')
wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,'#mainsrp-pager li.item.active > span'),str(page))) # 等待指定的文本出现在某一个节点
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-itemlist .items .item')))
get_products() # 等待指定的元素加载出来就可以继续往下执行否则报错
def main():
for i in range(1, MAX_PAGE + 1):
index_page(i)
if __name__ == '__main__':
main()
感兴趣的可以把这个在电脑上运行一下试试,看看会得到什么.
另外 Chrome从59版本开始,支持无界面模式 Headless 模式,启用方式:
chrome_options = wedriver.ChromeOptions()
chrome_options.add_argument('--headless')
browser = webdriver.Chrome(chrome_options=chrome_options)
感兴趣的同学可以把浏览器换成Firefox和PhantomJs试试,PhantomJs支持禁用图片加载功能,可以进一步提高爬虫效率
SERVICE_ARGS = ['--load-images=false','--disk-cache=true']
browser = webdriver.PhatomJs(service_args= SERVICE_ARGS)
整个例子到这里结束了,我们来看下里面关于selenium等待的和条件判断的两个方法
excepted_conditions
selenium.webdriver.support.expected_conditions(模块)
用于判断用户的传入的模块是否符合我们的需求,比如上面例子里写的,是否加载完成是否可见都可以作为判断标准,用于判断页面加载进度是否达到了我们的爬去需求.更多详细的方法看这里 第7点,关于等待的用法.
WebDriverWait
- 参数:
WebDriverWait(driver, timeout, poll_frequency, ignored_exceptions)
driver: 传入WebDriver实例,即我们上例中的driver
timeout: 超时时间,等待的最长时间(同时要考虑隐性等待时间)
poll_frequency: 调用until或until_not中的方法的间隔时间,默认是0.5秒
ignored_exceptions: 忽略的异常,如果在调用until或until_not的过程中抛出这个元组中的异常,
则不中断代码,继续等待,如果抛出的是这个元组外的异常,则中断代码,抛出异常。默认只有NoSuchElementException。
- 等待方式
.until(method, message)
until
method: 在等待期间,每隔一段时间(init中的poll_frequency)调用这个传入的方法,直到返回值不是False
message: 如果超时,抛出TimeoutException,将message传入异常
not_until
与上方信息操作相反
这里注意的一点就是method参数,这个参数一定是可以调用的,一定要有 call() 方法,否则会抛出异常
综上所写,那么完整的方式应该是下面的结构
WebDriverWait(driver, timeout, poll_frequency, ignored_exceptions).until(method, message)
具体的实现方式就像例子里一样, 等待配合判断来进行爬虫的操作可以,缩短爬虫的等待时间实现爬虫的效率的最大化,极大的提高爬虫的效率.
感谢崔静觅大大的教程!