Scrapy爬虫——Selenium集成到scrapy

作者: youyuge | 来源:发表于2017-10-03 22:26 被阅读364次

    有条件的请支持慕课实战正版课程,本blog仅仅是归纳总结,自用。

    理论上,控制流量,selenium完全模拟人类操作,是不会被反扒的,集成到scrapy之后,将大大发挥其作用。

    一、准备知识

    1. selenium官方英文document

    2. scrapy v1.0有关下载中间件的中文文档,有条件的可选英文

    二、自定义中间件

    • 注意这里的自定义中间件是downloader Middleware下载中间件,位于enginedownloader之间,为了方便我们选用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里啊,这样就可以做到带seleniumspider的不带的分别管理,并且能多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

    相关文章

      网友评论

      • 腿长袖子短:from scrapy.xlib.pydispatch import dispatcher
        引入这个scrapy直接停止报错,说以已经弃用了
      • 大诗兄_zl:请问有详细的参考吗,对中间件这里很费解

      本文标题:Scrapy爬虫——Selenium集成到scrapy

      本文链接:https://www.haomeiwen.com/subject/ipafyxtx.html