有条件的请支持慕课实战正版课程,本blog仅仅是归纳总结,自用。
理论上,控制流量,selenium完全模拟人类操作,是不会被反扒的,集成到scrapy之后,将大大发挥其作用。
一、准备知识
二、自定义中间件
- 注意这里的自定义中间件是
downloader Middleware
下载中间件,位于engine
与downloader
之间,为了方便我们选用edge浏览器做测试,其他selenium浏览器请参考官方文档:
from selenium import webdriver
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):
# 通过edge请求动态网页,代替scrapy的downloader
def process_request(self, request, spider):
if spider.name == "jobbole":
browser = webdriver.Edge(
executable_path='F:/PythonProjects/Scrapy_Job/JobSpider/tools/MicrosoftWebDriver.exe')
browser.get(request.url)
import time
time.sleep(3)
print ("访问:{0}".format(request.url))
#直接返回给spider,而非再传给downloader
return HtmlResponse(url=browser.current_url, body=browser.page_source, encoding="utf-8",
request=request)
- 但是每次请求网页都会新建一个浏览器,然后它会自动关闭,也太费时费力了。我们可以将它作为一个全局变量,在类初始化的时候初始化它,节约内存。我们重写init函数后:
from selenium import webdriver
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):
def __init__(self):
self.browser = webdriver.Edge(
executable_path='F:/PythonProjects/Scrapy_Job/JobSpider/tools/MicrosoftWebDriver.exe')
super(JSPageMiddleware,self).__init__()
# 通过edge请求动态网页,代替scrapy的downloader
def process_request(self, request, spider):
#判断该spider是否为我们的目标
if spider.name == "jobbole":
# browser = webdriver.Edge(
# executable_path='F:/PythonProjects/Scrapy_Job/JobSpider/tools/MicrosoftWebDriver.exe')
self.browser.get(request.url)
import time
time.sleep(3)
print ("访问:{0}".format(request.url))
#直接返回给spider,而非再传给downloader
return HtmlResponse(url=self.browser.current_url, body=self.browser.page_source, encoding="utf-8",
request=request)
-
现在,我们只会开启一个浏览器,省时省力!但是,又有一个问题,
spider closed
的时候,浏览器并不会自动关闭,因为我们的中间件middleware
里只有process_request
函数,只能处理过程。这就涉及到了scrapy
的另一个知识点。 -
我们发现
process_request
函数的参数中有spider
,那我们可以把浏览器的初始化放入单独的spider
里啊,这样就可以做到带selenium
的spider
的不带的分别管理,并且能多spider
开启多个browser
。 -
最终版的中间件:
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):
# 通过edge请求动态网页,代替scrapy的downloader
def process_request(self, request, spider):
#判断该spider是否为我们的目标
if spider.browser:
# browser = webdriver.Edge(
# executable_path='F:/PythonProjects/Scrapy_Job/JobSpider/tools/MicrosoftWebDriver.exe')
spider.browser.get(request.url)
import time
time.sleep(3)
print ("访问:{0}".format(request.url))
#直接返回给spider,而非再传给downloader
return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding="utf-8",
request=request)
-
Spider
类中加入以下2个函数,其中涉及到了信号量绑定的知识,和Django
中是类似的,因为毕竟scrapy
借鉴的就是Django
:
def __init__(self):
from selenium import webdriver
self.browser = webdriver.Edge(
executable_path='F:/PythonProjects/Scrapy_Job/JobSpider/tools/MicrosoftWebDriver.exe')
super(JobboleSpider, self).__init__()
from scrapy.xlib.pydispatch import dispatcher
from scrapy import signals
# 绑定信号量,当spider关闭时调用我们的函数
dispatcher.connect(self.spider_closed, signals.spider_closed)
def spider_closed(self, spider):
print 'spider closed'
self.browser.quit()
- 最后,当然别忘了在
settings
文件中配置我们的下载中间件:
DOWNLOADER_MIDDLEWARES = {
'JobSpider.middlewares.JSPageMiddleware': 1,
}
三、缺点
我们将scrapy
的异步下载改成了浏览器同步模式,大大降低了性能,为了实现selenium
也异步,需要重写scrapy
里的downloader
,必须要熟悉twist
异步框架,GitHub上就有许多插件,比如scrapy-phantomjs-downloader
网友评论
引入这个scrapy直接停止报错,说以已经弃用了