最近受林老板之托需要爬取一些数据。想要简单实现,看到Pyspider好像不错,好的,就决定是你拉。
Pyspider介绍:http://www.pyspider.cn/,网上使用方法教程很多,不再细说。
这次需要爬取的任务大致情况如下:
需要爬取某网站试题以及答案
试题以及答案最终会在同一个页面上呈现
每次显示有限的题数,例如20道题目,做完提交后可以看答案
需要进行答案填写和提交,下次进入才会有新的题目出来
该网站最终链接是js异步请求生成的
我需要的爬取流程也很简单:
手工通过账号密码登录该网站,获取登录后的cookies信息
脚本通过cookies登录该网站
一路跳转到试题界面
填写试题,提交,查看题目和答案
解析html,将试题和答案保存到本地
虽然流程是按部就班的来,但是为了兴趣和成效,我先从最后一步走起(当然cookies登录必不可少):
百度了Pyspider的用法和PyQuery的用法,这里提供两个链接参考:
http://www.pyspider.cn/book/self.crawl-16.html
https://www.cnblogs.com/lei0213/p/7676254.html
很快就最终结果给弄出来了。输入试题和答案页面的链接,就会生成结果文件,一切顺利,果然信心满满(^_^)。
然后是自动进行爬取的跳转操作:
Pyspider已经支持了PhantomJS,所以要渲染带js的页面,或者实现点击操作都可以。
Pyspider的模拟点击是通过crawl的参数js_script实现的,例如:
self.crawl('http://www.example.org/', callback=self.callback,fetch_type='js', js_script='''
function() {
document.querySelectorAll('.btn-inner')[1].click();
}
''')
但是对于我需要爬取的网页却不行,听说PhantomJS存在假死情况,说法根据百度到有人的问题:https://segmentfault.com/q/1010000007814470/a-1020000007838103。
Pyspider的一般做法是获取点击跳转的URL,然后再进行crawl。
但是面对一些通过js跳转的网页,除非你能解析出点击后的js如何返回链接,由于js通常加密过,有一定难度,而且这样工作量太大了。
但老板那边催着要,先爬到要紧,想到了使用按键精灵获取最终页面的链接。
我使用的按键精灵:http://www.anjian.com/download.htm,使用很简单,想必一看就会,(ps:按键精灵用于玩网游也是不错的)
录制鼠标和键盘事件:
点击进入首页,从首页点击进入试题
进行提交,进入试题和答案页
复制页面链接
粘贴到Pyspider代码中
点击调试的run按钮
不断重复这个脚本,数据就爬下来了。
这个方法虽然简单,但是主要缺点也是很明显:
按键脚本难以重复使用,因为脚本的鼠标操作坐标是根据屏幕的,它根本不知道网页
不能后台进行,这台电脑就不能做其他事情了
效率慢
百度搜到参考文章:Pyspider使用Selenium+Chrome实现爬取js动态页面 https://www.jianshu.com/p/8d955deac99b ,也很简单,该大神的代码已经贴上,运行就可以了。
我这次需要的步骤也很明了了:
搭建Flask+Selenium+Chrome的后台服务
通过该后台服务处理页面模拟点击事件,返回最终页面
Pyspider访问该后台服务,获取最终页面,解析html获取数据
我在文章开头想好的流程1-4都可以在这个后台完成了。
通过Selenium操作浏览器进行页面跳转、填写答案、提交等操作都非常稳健,可以填写答案,代码完成后直接在Pyspider控制台让爬取任务running就可以了,比起按键精灵确实爽多了。
需要注意的是如果这个操作过长,Pyspider可能超时,对于过长的操作可能需要分次操作较好,就是Pyspider中多写几层crawl,后台多处理几次handle_post。
这里贴出我使用的跳转浏览器操作代码作参考,这段代码放在后台selenium_fetcher的handle_post中即可:
# 访问网页
browser.get('https://xxxx')
browser.find_element_by_css_selector("[class='btn btn-primary select-csk']").click()
sleep(1)
browser.find_element_by_css_selector(".sprite.sprite-expand.i-20").click()
sleep(1)
# 进入试题页面
for a in browser.find_elements_by_css_selector("[class='btn btn-round create-exercise']"):
if '13842' == a.get_attribute("data-keypoint-id"):
print(a.get_attribute("data-keypoint-id"))
a.click()
break
sleep(1)
# 交卷
browser.find_element_by_css_selector(".commit-exercise.last").click()
sleep(1)
try:
browser.find_element_by_css_selector(".btn.btn-paper.btn-paper-xlarge.submit").click()
except Exception as e:
print (e)
sleep(1)
# 进入答案页面
browser.find_element_by_partial_link_text('查看解析').click()
sleep(1)
设置cookies的代码,我的cookies直接在Pyspider中写好了,所以需要获取,然后再设置到selenium的浏览器中:
# 设置cookies
cookie_str = fetch['headers']['Cookies']
if fetch.get('headers') and cookie_str:
driver.delete_all_cookies()
for item in cookie_str.split('; '):
key = item.split('=')[0]
value = item.split('=')[1]
print(key+":"+value+'\n')
driver.add_cookie({'name': key, 'value': value})
selenium 进行设置cookie时出现 unable set cookie
解决方法:
需要先加载一个页面才能进行add_cookies操作;
add_cookies的参数写法:https://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.add_cookie
pyspider 多层的crawl时,有些页面不爬取
解决方法:
回调方法的age=0。因为如果回调方法有一定时间的话,在时间内不会重复处理相同的url请求。