学习心得
从五月份到现在,学爬虫三个月了,也算对爬虫比较了解了。到目前为止,我最喜欢的还是Scrapy和模拟浏览器,虽然大家总是说模拟浏览器太慢,不过当你的爬虫对速度要求没那么高、对爬虫质量有要求、网站很复杂需要各种动态各种加载各种请求的时候,模拟浏览器就是最好(玩)的选择。
Python+Selenium+Chrome
就是我现在用的主流模拟浏览器方案,模拟浏览器提供了非常多的接口方法和属性以供操作,目前还是可以满足我的需求的。后面会考虑把最近做的微信公众号爬虫放上来讲解,其中验证码破解时,模拟浏览器就帮了大忙。
文末附有这阵子写的一些代码,完全可用,欢迎参考或提出更好的方法!
准备
- Pyhton3.6
pip install webdrive
- 对应版本的浏览器驱动,例如我用的是Chrome,就去官网上下载和自己机器上对应版本的驱动,然后将驱动放在环境变量中,这个网上有很多教程了,不是要讲的重点。
- PhantomJS是一个内置的小型无头浏览器内核(所谓无头就是运行时你看不到的),Python3.6目前使用会有警告,而且下载得去外网或者用anaconda,笔者一般不用。
代码分析
下面是我目前使用的模拟浏览器调用接口模块。
# -*- coding: utf-8 -*-
# This module is used to get a selenium browser,
# you can chose Firefox,Chrome,PhantomJS(em,maybe it's dead)
# you can also chose how to config img,head,ua,ip.
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import random
USER_AGENTS = [
'Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1',
'Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12',
'Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0',
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Navigator/9.0.0.6',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Maxthon/4.0.6.2000 Chrome/26.0.1410.43 Safari/537.1 ',
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0 ',
'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0; BIDUBrowser 2.x)',
'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko'
]
ips = []
def phantomjs_simu(image=False, ua=True, ip=False):
dcap = dict(DesiredCapabilities.PHANTOMJS)
if not image:
dcap["phantomjs.page.settings.loadImages"] = False # 禁止加载图片
if ua:
dcap["phantomjs.page.settings.userAgent"] = (random.choice(USER_AGENTS)) # 使用ua代理
if ip:
# 使用ip代理,未完善
pass
# 将配置信息生成的字典作为输入生成浏览器接口
js_drive = webdriver.PhantomJS(desired_capabilities=dcap)
return js_drive
def chrome_simu(head=False, image=False, ua=True, ip=False):
options = webdriver.ChromeOptions()
if not image:
prefs = {
'profile.default_content_setting_values': {
'images': 2
}
} # 禁止图片加载
options.add_experimental_option('prefs', prefs)
if not head:
# 配置为无头模式
options.add_argument('headless')
if ua:
options.add_argument('user-agent="%s"' % random.choice(USER_AGENTS))
if ip:
# ip代理,没有优质的代理所以一般没加
proxy = random.choice(ips)
options.add_argument('--proxy-server=%s' % proxy['ip'])
options.add_argument('lang=zh_CN.UTF-8')
ch_drive = webdriver.Chrome(chrome_options=options)
return ch_drive
def firefox_simu(head=False, image=False, ua=True, ip=False):
options = webdriver.FirefoxOptions()
if not head:
options.add_argument('-headless') # 无头模式
# 火狐没怎么用,所以配置也没完善,有需要的可以参照Chrome的自行补充
if not image:
pass
if ua:
pass
if ip:
pass
ff_drive = webdriver.Firefox(firefox_options=options)
return ff_drive
if __name__ == '__main__':
# 调用后就相当于获得了一个浏览器的控制权,可以用程序对浏览器进行控制
browser = chrome_simu(head=True, image=False, ua=True)
# 简单的使用示例
browser.get('http://www.baidu.com')
print(browser.current_url)
browser.close()
说说为什么我要设置上面4个配置?
- 首先
ua
代理和ip
代理都不用说,反爬基本意识; - 至于
head
这个参数可以选择浏览器是显式运行还是隐式运行,这对调试很有意义,你可以用显式调试代码,无误后在最终的工程中改为隐式,有利于运行速度; image
也好理解,就是选择图片加不加载,不加载可以提高运行速度,但在有验证码的页面且验证码图片的URL是动态的时候,就必须要显示图片,因为此时图片URL已经不能唯一确定一张图片了。
浏览器操作
下面记录几个webdrive用的比较频繁的属性和方法,以及几个实用的小技巧!
# attribute
browser.current_url # 当前浏览器的访问网址
browser.page_source # 页面的源代码
browser.window_handles # 当前浏览器的窗口控制数组
browser.current_window_handle # 当前窗口的控制
# method
browser.get(url) # 访问网站
browser.close() # 关闭网页
browser.quit() # 关闭浏览器
browser.back() # 网页后退
browser.set_page_load_timeout(10) # 设置页面加载超时
browser.set_script_timeout(10) # 设置脚本加载超时
browser.find_element_by_xpath('//div/../..') # 定位页面元素
browser.switch_to_window(browser.window_handles[i]) # 切换标签页
browser.execute_script('return ...') # 执行脚本
# tips
# 1.输入和点击提交
# 检查元素找到搜索框和提交键的xpath,可以拿百度试试
browser.find_element_by_xpath(xpath_input).send_keys(key_word)
time.sleep(2) # 建议不要瞬间输入瞬间搜索
browser.find_element_by_xpath(xpath_post).click()
# 2.定位并截图
# 当遇到动态验证码,想要把图片拿出来不能通过加载图片URL,因为再访问一次验证码已经变了,只能从页面截图
# 定位图片在网页上的位置和大小
img_element = id_drive.find_element_by_xpath('//div/../img')
img_location = img_element.location
img_size = img_element.size
rangle = (
int(img_location['x']), int(img_location['y']),
int(img_location['x'] + img_size['width']),
int(img_location['y'] + img_size['height'])
)
# 截图后根据图片在页面中的数据进行剪切
id_drive.save_screenshot('test.png')
image = Image.open('test.png').crop(rangle)
image.save('test2.png')
# 3.切换窗口
# 当有弹出新窗口的时候,就得注意浏览器操作是对哪个页面操作的问题了
handles = browser.window_handles
# 切换到新标签页进行操作
browser.switch_to_window(handles[1])
# 4.拉到底端
# 这是之前爬腾讯网时写的模拟鼠标下滑动作的方法,因为腾讯的页面都是下拉加载QAQ
# 下面代码的思路就是判断页面长度是否稳定了来判断是否完全加载
def RefreshScreen(browser):
js1 = 'return document.body.scrollHeight'
js2 = 'window.scrollTo(0, document.body.scrollHeight)'
js3 = 'window.scrollTo(0, document.body.scrollHeight/4)'
old_scroll_height = 0
new_scroll_height = 0
try_time = 0
load_time = 0
start = time.time()
init = True
while new_scroll_height >= old_scroll_height and try_time < 5:
print("new scroll_height: %d" % browser.execute_script(js1))
print("old scroll_height: %d" % old_scroll_height)
if new_scroll_height == old_scroll_height:
try_time += 1
else:
try_time = 0
load_time += 1
old_scroll_height = new_scroll_height
print("No change of scroll_height: %d" % try_time)
if init:
browser.execute_script(js3)
init = False
browser.execute_script(js2)
print("loading new data...\n")
time.sleep(1)
new_scroll_height = browser.execute_script(js1)
print("No more data!")
print("Loading times: %d" % load_time)
print("Loading time: %d" % (time.time()-start))
print("Loading length: %d" % new_scroll_height)
# 5.动态加载
# 有的时候还有这种现象,明明一些标签你在检查元素的时候检查到了,但当你获取页面源码里面却没有。
# 这是因为,有些页面的数据是动态加载出来的,像我这种没学过JS的怎么办?
# 使用模拟浏览器就可以跳过各种构造请求头的狗血操作,直接执行脚本来获取!
real_html = browser.execute_script('return document.documentElement.outerHTML')
目前在模拟浏览器这一块主要就用了这么些操作,后续再补充。
代码都是这阵子学了写了用在工程里的,环境Python3.6/Ubuntu16.04,欢迎参考,如有纰漏,评论区求怼。