【问题标题】:Scrapy not calling any other function after "__init__"Scrapy 在“__init__”之后不调用任何其他函数
【发布时间】:2017-03-09 13:40:26
【问题描述】:

操作系统:Ubuntu 16.04 堆栈 - Scrapy 1.0.3 + Selenium 我对scrapy很陌生,这听起来很基础,但是在我的蜘蛛中,只有“init”被执行。之后的任何代码/函数都不会被调用,蜘蛛就会停止。

class CancerForumSpider(scrapy.Spider):
    name = "mainpage_spider"
    allowed_domains = ["cancerforums.net"]
    start_urls = [
        "http://www.cancerforums.net/forums/14-Prostate-Cancer-Forum"
    ]

    def __init__(self,*args,**kwargs):
        self.browser=webdriver.Firefox()
        self.browser.get("http://www.cancerforums.net/forums/14-Prostate-Cancer-Forum")
        print "----------------Going to sleep------------------"
        time.sleep(5)
        # self.parse()

    def __exit__(self):
        print "------------Exiting----------"
        self.browser.quit()

    def parse(self,response):
        print "----------------Inside Parse------------------"
        print "------------Exiting----------"
        self.browser.quit()

蜘蛛获取浏览器对象,打印“Going to sleep”然后停止。它不会进入 parse 函数。

以下是运行日志的内容:

----------------内部初始化---------------- ----------------去睡觉了------------------

【问题讨论】:

  • 嗯...self.parse() 被注释掉了,所以我不希望它被调用...
  • self.parse 应该由蜘蛛本身在这里调用。你能提供运行日志吗?你可以通过scrapy crawl myspider 2>&1 > output.log
  • 用运行日志和终端输出更新了问题。

标签: python python-2.7 selenium scrapy scrapy-spider


【解决方案1】:

您需要解决或注意一些问题:

  1. 您没有在 __init__ 方法期间调用 super(),因此不会发生任何继承类初始化。 Scrapy 不会做任何事情(比如调用它的 parse() 方法),因为这一切都在 scrapy.Spider 中设置。

  2. 修复上述问题后,您的 parse() 方法将被 Scrapy 调用,但不会在您的 Selenium 获取的网页上运行。它将对此一无所知,并将重新获取 url(基于start_urls)。这两个来源很可能会有所不同(通常是巨大的)。

  3. 您将按照您的方式使用 Selenium 绕过 Scrapy 的几乎所有功能。 Selenium 的所有get() 都将在 Scrapy 框架之外执行。不会应用中间件(cookie、限制、过滤等),也不会使用您期望的数据填充任何预期/创建的对象(如 requestresponse)。

在解决所有这些问题之前,您应该考虑几个更好的选择/替代方案:

  • 创建一个处理所有“Selenium”相关功能的下载器中间件。让它在 request 对象到达下载器之前拦截它们,填充新的 response 对象并将它们返回以供蜘蛛处理。
    这不是最佳选择,因为您正在有效地创建自己的下载器,并短路 Scrapy。您必须重新实现下载器通常考虑的任何所需设置的处理,并使它们与 Selenium 一起使用。
  • 抛弃 Selenium 并使用 Splash HTTP 和 scrapy-splash 中间件来处理 Javascript。
  • 完全抛弃 Scrapy,只使用 Selenium 和 BeautifulSoup。

【讨论】:

    【解决方案2】:

    当您必须抓取大量页面时,Scrapy 非常有用。当您需要在 JS 加载后获得 DOM 源时,Selenium 通常可用于抓取。如果这是你的情况,有两种主要方法可以结合 Selenium 和 Scrapy。一种是编写一个下载处理程序,比如你可以找到here

    代码如下:

    # encoding: utf-8
    from __future__ import unicode_literals
    
    from scrapy import signals
    from scrapy.signalmanager import SignalManager
    from scrapy.responsetypes import responsetypes
    from scrapy.xlib.pydispatch import dispatcher
    from selenium import webdriver
    from six.moves import queue
    from twisted.internet import defer, threads
    from twisted.python.failure import Failure
    
    
    class PhantomJSDownloadHandler(object):
    
        def __init__(self, settings):
            self.options = settings.get('PHANTOMJS_OPTIONS', {})
    
            max_run = settings.get('PHANTOMJS_MAXRUN', 10)
            self.sem = defer.DeferredSemaphore(max_run)
            self.queue = queue.LifoQueue(max_run)
    
            SignalManager(dispatcher.Any).connect(self._close, signal=signals.spider_closed)
    
        def download_request(self, request, spider):
            """use semaphore to guard a phantomjs pool"""
            return self.sem.run(self._wait_request, request, spider)
    
        def _wait_request(self, request, spider):
            try:
                driver = self.queue.get_nowait()
            except queue.Empty:
                driver = webdriver.PhantomJS(**self.options)
    
            driver.get(request.url)
            # ghostdriver won't response when switch window until page is loaded
            dfd = threads.deferToThread(lambda: driver.switch_to.window(driver.current_window_handle))
            dfd.addCallback(self._response, driver, spider)
            return dfd
    
        def _response(self, _, driver, spider):
            body = driver.execute_script("return document.documentElement.innerHTML")
            if body.startswith("<head></head>"):  # cannot access response header in Selenium
                body = driver.execute_script("return document.documentElement.textContent")
            url = driver.current_url
            respcls = responsetypes.from_args(url=url, body=body[:100].encode('utf8'))
            resp = respcls(url=url, body=body, encoding="utf-8")
    
            response_failed = getattr(spider, "response_failed", None)
            if response_failed and callable(response_failed) and response_failed(resp, driver):
                driver.close()
                return defer.fail(Failure())
            else:
                self.queue.put(driver)
                return defer.succeed(resp)
    
        def _close(self):
            while not self.queue.empty():
                driver = self.queue.get_nowait()
                driver.close()
    

    假设您的刮板被称为“刮板”。如果您将提到的代码放在“scraper”文件夹根目录下名为 handlers.py 的文件中,那么您可以添加到您的 settings.py:

    DOWNLOAD_HANDLERS = {
        'http': 'scraper.handlers.PhantomJSDownloadHandler',
        'https': 'scraper.handlers.PhantomJSDownloadHandler',
    }
    

    另一种方法是编写下载中间件,如here 所述。下载中间件的缺点是会阻止一些关键功能开箱即用,例如缓存和重试。

    在任何情况下,在 Scrapy 蜘蛛的 init 处启动 Selenium webdriver 并不是通常的方法。

    【讨论】:

    • 感谢详细的回复。这让很多事情变得更加清晰。
    猜你喜欢
    • 1970-01-01
    • 2020-09-16
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多