title: scrapy进阶开发date: 2018-06-13 16:08:15
tags: spider
Selenium
爬取动态网页
需要先安装selenium:
$ pip install selenium
需要注意的是selenium是模仿浏览器行为,需要下载driver来搭配。
在tools中新建脚本文件selenium_spider
from selenium import webdriver
browser = webdriver.Chrome(executable_path="E:\download\selenium\chromedriver.exe")
browser.get("https://guang.taobao.com/detail/index.htm?spm=a21bo.2017.2001.3.5af911d9RmltPj&uid=2758542224&sid=8094212425&scm=1007.15939.89001.100200300000000&pvid=40c54281-8e66-4601-84c9-6c4c03d60714&itemid=35109052504")
print(browser.page_source)
只需简单几行即可获得js动态加载后的页面内容。
之后只需要再通过Selector来获取具体内容。当然,selenium也有一些解析的方法,可以做一些特定的操作,但是提取内容还是Selector更好。
t_selector = Selector(text=browser.page_source)
print(t_selector.css(".testsetest h1::text").extract_first())
模拟登陆
知乎
使用selenium模拟登陆知乎:
browser.get("https://www.zhihu.com/signup?next=%2F")
browser.find_element_by_css_selector(".SignContainer-switch span").click()
browser.find_element_by_css_selector(".SignFlow-accountInputContainer div input[name='username']").send_keys("xxxxxxxxxxxxx")
browser.find_element_by_css_selector(".SignFlow-password div div input[name='password']").send_keys("xxxxxxxxxxxxx")
browser.find_element_by_css_selector(".SignFlow-submitButton").click()
send_keys():模拟输入
click():模拟点击
微博
使用selenium模拟登陆微博:
browser.maximize_window()
browser.get("https://www.weibo.com/")
time.sleep(15)
browser.find_element_by_css_selector("#loginname").send_keys("xxxxxxxxxxxxx")
browser.find_element_by_css_selector(".info_list.password input[node-type='password']").send_keys("xxxxxxxxxxxxxxxxxxxxxxx")
browser.find_element_by_css_selector(".info_list.login_btn a[node-type='submitBtn']").click()
需要注意其中的sleep和maxmize_window()
sleep:由于微博有跳转加载时间,需要停顿一段时间再进行输入操作。
maxmize_window():因为微博兼容性的原因,若半屏打开会导致登陆部分不在页面中加载,只有全屏才能加载。
模拟下拉页面
browser.get("https://www.oschina.net/blog")
time.sleep(5)
for i in range(10):
browser.execute_script("window.scrollTo(0, document.body.scrollHeight); var lenOfPage=document.body.scrollHeight; return lenOfPage")
time.sleep(3)
在python中可以直接写JavaScript代码,用browser的execute_script方法。
不加载图片
加载图片耗费大量的运行时间,设置不加载图片可以使我们的爬取更为迅速。
对chromedriver设置对应属性可以通过webdriver.ChromeOptions()来实现。
chrome_opt = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_opt.add_experimental_option("prefs", prefs)
browser = webdriver.Chrome(executable_path="E:\download\selenium\chromedriver.exe", chrome_options=chrome_opt)
browser.get("https://www.taobao.com")
无界面浏览器phantomjs
对于linux系统环境下,需要使用无界面浏览器。对于windows一般依旧采取chrome,因为性能更优。
弊端:多进程情况下phantomjs性能下降会很严重。
browser = webdriver.PhantomJS(executable_path="E:\download\phantomjs-2.1.1-windows\\bin\phantomjs.exe")
browser.get("https://chaoshi.detail.tmall.com/item.htm?spm=a3204.11641622.4538157102.2.6e237e44lGawqi&pos=2&acm=lb-zebra-355535-4093695.1003.1.3594048&id=41285559396&scm=1003.1.lb-zebra-355535-4093695.FF-hyhsfZA-725677994_B-370100_C-HB_D-41285559396_E-D_G-114.9-FF_41285559396_3594048")
print(browser.page_source)
# 关闭
browser.quit()
将selenium集成进scrapy
可以通过middlewares的方式将selenium集成到scrapy中:
from selenium import webdriver
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):
# 通过chrome请求动态网页
def process_request(self, request, spider):
if spider.name == "jobbole":
spider.browser.get(request.url)
import time
time.sleep(3)
print("访问:{0}".format(request.url))
return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding="utf-8", request=request)
其中,开启chromedriver要放置在对应的spider(jobbole.py)中:
from selenium import webdriver
from scrapy.xlib.pydispatch import dispatcher
from scrapy import signals
def __init__(self):
self.browser = webdriver.Chrome(executable_path="E:\download\selenium\chromedriver.exe")
super(JobboleSpider, self).__init__()
dispatcher.connect(self.spider_closed, signals.spider_closed)
并且,要设置当spider结束运行时关闭chromedriver:
def spider_closed(self, spider):
# 当爬虫退出的时候关闭chrome
print("spider closed")
self.browser.quit()
最终,要记得在setting中开启DOWNLOADER_MIDDLEWARES:
DOWNLOADER_MIDDLEWARES = {
'ArticleSpider.middlewares.JSPageMiddleware': 1
}
其他动态网页获取介绍
pyVirtualDisplay
运行在Linux系统中,可以实现无界面爬取,但效果不如chrome稳定。
from pyvirtualdisplay import Display
display = Display(visible=0, size=(800, 600))
display.start()
browser = webdriver.Chrome()
browser.get()
......
scrapy-splash
它自己运行一个servers,通过http请求的方式以js来执行。
运行在servers,支持分布式。
selenium grid
请求一个服务,通过api的方式发送请求。
splinter
操控浏览器的解决方案,和selenium很相像,是纯python代码。
scrapy暂停与重启
可以在spider运行暂停时进行记录,以保证下次开启spider从暂停位置继续。
需要在命令行中进行,因为其终止信号为Ctrl + c,而pycharm中终止并无法发出这一信号。
Windows:暂停信号:Ctrl + c Linux:kill -f main.py
在项目目录下进入虚拟环境后执行下面的语句:
$ scrapy crawl jobbole -s JOBDIR=job_info/001
需要在项目下新建存放信息的文件夹job_info
Telnet
可以实现对爬虫的监听:
先在windows功能中开启telent,然后在命令行中输入:
$ telent localhost 6023
通过est()命令来查看当前spider状态。
并且可以打印spider的诸多信息,如setting等。
数据收集
scrapy自带数据收集器,文档
以jobbole为例:
收集伯乐在线所有404的url以及404页面数
handle_httpstatus_list = [404]
def __init__(self):
self.fail_urls = []
在parse中可以进行判断:
if response.status == 404:
self.fail_urls.append(response.url)
self.crawler.stats.inc_value("failed_url")
信号
Scrapy广泛使用信号来通知特定事件发生的时间。您可以捕获Scrapy项目中的一些信号(例如使用扩展)来执行其他任务或扩展Scrapy以添加不提供的功能。
以之前的收集404的url为例:
handle_httpstatus_list = [404]
def __init__(self):
self.fail_urls = []
dispatcher.connect(self.handle_spider_closed, signals.spider_closed)
def handle_spider_closed(self, spider,reason):
self.crawler.ststs.set_value("failed_urls", ",".join(self.fail_urls))
pass
可以在spider关闭(closed)之后输出404的url信息。