【问题标题】:How to change urls order in Scrapy spider?如何更改 Scrapy spider 中的 url 顺序?
【发布时间】:2017-09-19 07:52:00
【问题描述】:

我正在从数千个网页中获取更新。可以有多个具有相同域的页面。我已将 DOWNLOAD_DELAY 设置为 1 秒,以免服务器过载。

Spider 运行良好,但如果同一域的 100 个 url 彼此相邻,它会减慢爬网速度,因为 Spider 必须在每次请求后等待 1 秒。

是否可以让它抓取具有不同域的下一个 url,这样蜘蛛就不必等待?

例如:

CONCURRENT_REQUESTS = 3
DOWNLOAD_DELAY = 1

网址:A.com/1 ,A.com/2 ,A.com/3 ,A.com/4 ,B.com/1 ,B.com/2 ,B.com/3

Spider 将开始抓取前三个 url。由于下载延迟,至少需要三秒钟。但如果它处理 B.com/1 而不是 A.com/2(例如),它会更快。

class MainSpider(scrapy.Spider):
    ...

    def __init__(self, scraping_round, frequencies=None):
        super(MainSpider, self).__init__())
        ...

    def start_requests(self):
        for url in self.urls:
            yield scrapy.Request(url=url, callback=self.parse, errback=self.err, dont_filter=True)

也许我应该重新排序urls 列表。

【问题讨论】:

    标签: python python-2.7 scrapy web-crawler


    【解决方案1】:

    肯定是的,重新排序计划请求列表会有所帮助。可以这样做:

    import random
    
    class MainSpider(scrapy.Spider):
        # ....
    
        def start_requests(self):
            random.shuffle(self.urls)
            for url in self.urls:
                yield scrapy.Request(url=url, callback=self.parse, errback=self.err, dont_filter=True)
    

    不幸的是,对随后在抓取时创建的请求进行重新排序更加困难,但也许这已经有所帮助。

    另一个修复:大幅增加 CONCURRENT_REQUESTS。

    如果您想并行抓取多个域,scrapy 文档建议将 CONCURRENT_REQUESTS 至少设置为 100:

    https://doc.scrapy.org/en/latest/topics/broad-crawls.html#increase-concurrency

    原因/详解

    根据scrapy/core/downloader.py, engine.py, scraper.py and scrapy/core/downloader/handlers/http11.py 的源代码,scrapy 似乎从调度程序中填充了多达 CONCURRENT_REQUESTS 的处理队列,并在处理链的下游检查域以观察 CONCURRENT_REQUESTS_PER_DOMAIN。

    如果调度程序连续包含一组针对同一域的请求,您可能会将针对同一域的多个请求拉入处理队列,从而有效地阻止其他域的处理。如果 CONCURRENT_REQUESTS 如您的示例所示非常低,则尤其可能发生这种情况。

    这是此处描述的已知问题:https://github.com/scrapy/scrapy/issues/2474

    替代解决方案

    比将 CONCURRENT_REQUESTS 增加到一个非常高的值更好的解决方案是使用 https://github.com/scrapinghub/frontera 作为爬行边界......这基本上是按照您的建议进行的:重新排序计划的请求以获得最佳处理。

    【讨论】:

    • 感谢您的回答。我已将并发请求设置为 100,但问题是 3 作为一个简单的示例。但是我可以有 2000 个来自同一域的 url 在列表中彼此相邻,所以 100 对我没有帮助。我可能会使用随机入队或按域对 url 进行分组。 Frontera 似乎很好,但这不是矫枉过正吗?或者如果你愿意,你能在这里发布一个frontera和我的蜘蛛的例子吗?
    • 是的,如果你有很多来自同一个域的 url 彼此相邻,那么将它们随机化会很有帮助。在您的网址列表上调用 random.shuffle 应该可以解决问题(docs.python.org/2/library/random.html#random.shuffle)。
    • Frontera 将有助于挤出最后一点并行性,但随机化列表可能足以满足您的目的。
    • 随机化是首选,但按域对 url 进行分组可能是最好且非常简单的选择。我会创建一个字典,其中域作为键,url 列表作为值。然后我会从这些列表中一个一个弹出来创建一个列表。从第一组、第二组、第三组中弹出 url ......并重复直到所有组为空。这可能不是一个理想的解决方案,但它似乎非常有效。
    • 视情况而定。如果同域 url 组的大小几乎相同,那么您的方法将比随机化好一点(前提是 CONCURRENT_REQUESTS 真的很高)。但是如果有很多只有很少 url 的域和很少有很多 url 的域,那么随机化会导致更平衡的调度。
    【解决方案2】:

    DOWNLOAD_DELAY 设置应用于每个网站。

    下载程序在从同一网站下载连续页面之前应等待的时间(以秒为单位)。这可用于限制爬取速度以避免对服务器造成太大影响。

    来自文档:https://doc.scrapy.org/en/latest/topics/settings.html#download-delay

    所以你想要的应该默认工作。当蜘蛛启动时,它会立即将start_urls 中的每个url 排队,然后对延迟等进行排序。

    【讨论】:

    • 好的,如果我理解:如果 url 列表是 A.com/1, A.com/2, A,com/3, A.com/4, B.com/1, B .com/2 和 CONCURENT_REQUESTS = 3 和 DOWNLOAD_DELAY = 1,它应该从 A.com/1 和 B.com/1 “并行”开始,然后在一秒钟后 A.com/2 + B.com/2。这是真的吗?我不想等到 A.com 被爬取后再爬取 B.com。
    • 是的,A.com 和 B.com 将没有共享延迟,因此它们会一起并行。
    • 不,不会。 A.com/1、A.com/2、A.com/3 将填满所有 3 个处理槽,因为它们将首先从调度程序中拉出。另见我的回答。这是一个已知问题:github.com/scrapy/scrapy/issues/2474
    猜你喜欢
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多