【问题标题】:Unable to make my script stop when some urls are scraped抓取某些网址时无法使我的脚本停止
【发布时间】:2019-09-11 12:12:04
【问题描述】:

我在 scrapy 中创建了一个脚本来解析 start_urls 中列出的不同站点的标题。该脚本完美地完成了它的工作。

我现在想做的是让我的脚本在解析两个 url 后停止,不管有多少个 url。

到目前为止,我已经尝试过:

import scrapy
from scrapy.crawler import CrawlerProcess

class TitleSpider(scrapy.Spider):
    name = "title_bot"
    start_urls = ["https://www.google.com/","https://www.yahoo.com/","https://www.bing.com/"]

    def parse(self, response):
        yield {'title':response.css('title::text').get()}

if __name__ == "__main__":
    c = CrawlerProcess({
        'USER_AGENT': 'Mozilla/5.0', 
    })
    c.crawl(TitleSpider)
    c.start()

当列出的两个 url 被抓取时,如何让我的脚本停止?

【问题讨论】:

  • 哪两个?序列中的第一个?
  • 任何两个都可以@DirtyBit。
  • 我不熟悉scrapy。你如何阻止蜘蛛?

标签: python python-3.x web-scraping scrapy


【解决方案1】:

目前我看到唯一一种立即停止此脚本的方法 - 使用 os._exit 强制退出功能:

import os
import scrapy
from scrapy.crawler import CrawlerProcess

class TitleSpider(scrapy.Spider):
    name = "title_bot"
    start_urls = ["https://www.google.com/","https://www.yahoo.com/","https://www.bing.com/"]
    item_counter =0

    def parse(self, response):
        yield {'title':response.css('title::text').get()}
        self.item_counter+=1
        print(self.item_counter)
        if self.item_counter >=2:
            self.crawler.stats.close_spider(self,"2 items")
            os._exit(0)

if __name__ == "__main__":
    c = CrawlerProcess({'USER_AGENT': 'Mozilla/5.0' })
    c.crawl(TitleSpider)
    c.start()

我尝试过的另一件事。
但我没有收到所需的结果(立即停止脚本在 start_urls 中只有 3 个 url 的 2 个抓取项目):

  1. CrawlerProcess 实例转移到蜘蛛设置并调用 CrawlerProcess.stop ,(reactor.stop) 等..和其他方法 来自parse 方法。
  2. 使用CloseSpider 扩展docs source ) 和以下CrawlerProcess 定义:

    c = CrawlerProcess({
        'USER_AGENT': 'Mozilla/5.0',
        'EXTENSIONS' : {
    
    'scrapy.extensions.closespider.CloseSpider': 500,
                        },
    "CLOSESPIDER_ITEMCOUNT":2 })
    
  3. CONCURRENT_REQUESTS 设置减少为1(使用raise CloseSpider parse 方法中的条件)。
    当应用程序抓取 2 个项目时 到达带有raise ClosesSpider 的代码行 - 已经是第三个请求 在另一个线程中开始。
    如果使用常规方式停止 蜘蛛,应用程序将处于活动状态,直到它处理先前发送的 请求并处理他们的响应,然后才 - 它 关闭。

由于您的应用程序在 start_urls 中的 url 数量相对较少,因此应用程序会在到达 raise CloseSpider 之前很久就处理所有 url。

【讨论】:

  • 这似乎是我正在寻找的完美解决方案。我发现很难理解以下stats.close_spider(self,"2 items") 中的参数。你能在评论中澄清一下吗?谢谢。
  • 通常scrapy使用此函数在结束时打印统计数据(统计数据以INFO: Dumping Scrapy stats:开头的日志行)。此函数需要蜘蛛和原因作为参数。由于os._exit 的使用会立即停止进程,因此应用程序不会像通常的scrapy 那样打印统计数据。所以我在os._exit之前手动添加了这个函数调用self.crawler.stats.close_spider(self,"2 items")的用法是可选的。
【解决方案2】:

正如 Gallaecio 所提议的,您可以添加一个计数器,但这里的不同之处在于您在 if 语句之后导出一个项目。这样,它几乎总是会导出 2 个项目。

import scrapy
from scrapy.crawler import CrawlerProcess
from scrapy.exceptions import CloseSpider


class TitleSpider(scrapy.Spider):
    name = "title_bot"
    start_urls = ["https://www.google.com/", "https://www.yahoo.com/", "https://www.bing.com/"]
    item_limit = 2

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.counter = 0

    def parse(self, response):
        self.counter += 1
        if self.counter > self.item_limit:
            raise CloseSpider

        yield {'title': response.css('title::text').get()}

为什么几乎总是这样?你可能会问。它与parse 方法中的竞争条件有关。

假设self.counter 当前等于1,这意味着预计会再导出一项。但是现在 Scrapy 同时收到两个响应,并为它们调用parse 方法。如果运行parse 方法的两个线程将同时增加计数器,它们的self.counter 将等于3,因此都会引发CloseSpider 异常。

在这种情况下(这种情况不太可能发生,但仍有可能发生),spider 将只导出一项。

【讨论】:

    【解决方案3】:

    https://stackoverflow.com/a/38331733/939364之上构造,你可以在你的蜘蛛的构造函数中定义一个计数器,并使用parse增加它,当它达到2时提高CloseSpider

    import scrapy
    from scrapy.crawler import CrawlerProcess
    from scrapy.exceptions import CloseSpider  # 1. Import CloseSpider
    
    class TitleSpider(scrapy.Spider):
        name = "title_bot"
        start_urls = ["https://www.google.com/","https://www.yahoo.com/","https://www.bing.com/"]
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.counter = 0  # 2. Define a self.counter property
    
        def parse(self, response):
            yield {'title':response.css('title::text').get()}
            self.counter += 1  # 3. Increase the count on each parsed URL
            if self.counter >= 2:
                raise CloseSpider  # 4. Raise CloseSpider after 2 URLs are parsed
    
    if __name__ == "__main__":
        c = CrawlerProcess({
            'USER_AGENT': 'Mozilla/5.0', 
        })
        c.crawl(TitleSpider)
        c.start()
    

    我不能 100% 确定它会阻止解析第三个 URL,因为我认为 CloseSpider 会阻止新请求的开始,但会等待开始的请求完成。

    如果你想防止超过2个项目被刮掉,你可以编辑parseself.counter > 2时不产生项目。

    【讨论】:

    • 我试过你的脚本,但真的没有帮助。我在start_urls 中放了 6 个 url,它们都被相应地解析。您的导入中有错字。注意用from scrapy.exceptions import CloseSpider 替换它。感谢您的意见@Gallaecio。
    • @MITHU 根据我在代码后面的段落,我认为这是预期的 6 个 URL。提升 CloseSpider 后,我相信在蜘蛛停止之前可能会收到多达CONCURRENT_REQUESTS-1 的响应。例如,尝试设置CONCURRENT_REQUESTS = 2
    • 我将 CONCURRENT_REQUESTS 设置为 2 但这仍然无法解决问题。我在六个网址 @Gallaecio 上获得了所有六个标题。
    【解决方案4】:

    枚举做得很好。架构上的一些变化和

    for cnt, url in enumerate(start_urls):
        if cnt > 1:
            break
        else:
            parse(url)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多