下载文件时Scrapy i / o阻止

时间:2016-08-06 05:41:18

标签: python scrapy

我使用Scrapy来扫描网页并下载一些文件。由于我得到的file_url将重定向到另一个url(302重定向)。所以我使用另一个方法handle_redirect来获取重定向的url。我像这样自定义filespipeline。

class MyFilesPipeline(FilesPipeline):

    def handle_redirect(self, file_url):
        response = requests.head(file_url)
        if response.status_code == 302:
            file_url = response.headers["Location"]
        return file_url

    def get_media_requests(self, item, info):
        redirect_url = self.handle_redirect(item["file_urls"][0])
        yield scrapy.Request(redirect_url)

    def item_completed(self, results, item, info):
        file_paths = [x['path'] for ok, x in results if ok]
        if not file_paths:
            raise DropItem("Item contains no images")
        item['file_urls'] = file_paths
        return item

使用上面的代码,我可以下载文件,但下载时进程被阻止,因此整个项目变得非常慢。

我在spider中尝试了另一个解决方案,先使用Requests get重定向url然后传递给另一个函数。并使用默认的filespipeline。

yield scrapy.Request(
            download_url[0],
            meta={
                "name": name,
                },
            dont_filter=True,
            callback=self.handle_redirect)

    def handle_redirect(self, response):
        logging.warning("respon %s" % response.meta)
        download_url = response.headers["Location"].decode("utf-8")

        return AppListItem(
            name=response.meta["name"],
            file_urls=[download_url],
            )

仍然阻止这个过程。

来自dos这里

Using the Files Pipeline

  

当项目到达FilesPipeline时,file_urls中的URL   字段计划使用标准Scrapy调度程序下载   和下载程序(这意味着调度程序和下载程序中间件   被重用),但具有更高的优先级,在其他之前处理它们   页面被刮掉了。该项目仍然“锁定”在该特定位置   管道阶段,直到文件完成下载(或失败   某种原因)

这是否意味着我可以在下载文件之前搜索下一个网址? (我没有在我的设置中设置download_delay)

修改

我最初添加了这个:

handle_httpstatus_list = [302]

因此我不会重定向到重定向的网址,我的第一个解决方案使用请求是因为我认为yield会像这样工作:

  1. 我抓取一个页面,保持收益回调,然后调用返回项目
  2. 该项目传递给管道,如果它遇到某些i / o,它将产生蜘蛛爬行下一页,就像普通的异步i / o一样。
  3. 或者我必须等待下载的文件才能抓取下一页?这是Scrapy的缺点吗?我不了解的第二部分是如何计算抓取页面的速度。例如,一个完整页面的3s,默认并发度为16.我猜@neverlastn使用16/2/3得到2.5 pages / s.Doesn&conurrency 16意味着我可以同时处理16个请求吗?那么速度应该是16页/秒?如果我错了,请纠正。

    EDIT2

    感谢您的回答,我理解现在如何计算,但我仍然不理解第二部分。在302我首先遇到这个问题。 Error 302 Downloading File in Scrapy 我有一个像

    的网址
    http://example.com/first
    

    将使用302并重定向到

    http://example.com/second
    

    但是Scrapy没有自动重定向到第二个,并且无法下载有线的文件。从这里的代码Scrapy-redirect和dos RedirectMiddleware指出scrapy应该默认处理重定向。这就是为什么我做了一些技巧并尝试修复它。我的第三个解决方案将尝试使用这样的Celery

    class MyFilesPipeline(FilesPipeline):
        @app.task
        def handle_redirect(self, file_url):
            response = requests.head(file_url)
            if response.status_code == 302:
                file_url = response.headers["Location"]
            return file_url
    
        def get_media_requests(self, item, info):
            redirect_url = self.handle_redirect.delay(item["file_urls"][0])
            yield scrapy.Request(redirect_url)
    
        def item_completed(self, results, item, info):
            file_paths = [x['path'] for ok, x in results if ok]
            if not file_paths:
                raise DropItem("Item contains no images")
            item['file_urls'] = file_paths
            return item
    

    由于我已经有很多蜘蛛,我不想使用第二种解决方案来覆盖它们。所以我在管道中处理它们,这个解决方案会更好吗?

2 个答案:

答案 0 :(得分:4)

您使用同步/阻止的requests API。这意味着您将并发(CONCURRENT_REQUESTS_PER_DOMAIN)从(默认情况下)8转换为有效。它似乎支配了你的延迟。你第二次尝试的那个很好的技巧。这不使用requests因此它与使用requests相比应该更快(不是吗?)现在,当然,你添加额外的延迟...如果你的第一个(HTML)请求需要1s和第二个(图像)请求2s,总体而言,你有一个完整的页面3s。如果默认并发值为16,则表示您将以2.5页/秒的速度进行爬网。当您的重定向失败并且您没有抓取图像时,该过程将采用aprox。 1s即8页/秒。所以你可能会看到3倍的减速。一种解决方案可能是通过增加CONCURRENT_REQUESTS_PER_DOMAIN和/或CONCURRENT_REQUESTS来允许并行运行的并发请求数量的3倍。如果您现在从带宽有限和/或延迟增加的地方运行此功能,则另一种解决方案可能是从靠近托管图像服务器的区域的云服务器运行它(例如EC2 US East)。

修改

“小法则”可以更好地理解表现。第一个CONCURRENT_REQUESTS_PER_DOMAINCONCURRENT_REQUESTS通常并行工作。 CONCURRENT_REQUESTS_PER_DOMAIN = 8默认情况下,我猜你通常从单个域下载,因此你的实际并发限制是8.并发级别(即8)不是每秒,但它是一个固定的数字,就像说“那个烤箱最多可烤8个羊角面包”。你的羊角面包烘烤的速度有多快(这是网络响应时间),你感兴趣的指标是它们的比例,即8个羊角面包可以平行烘烤/每个羊角面包3秒=我将烘焙2.5个羊角面包/秒。 / p>

enter image description here

在302,我不确定你究竟要做什么。我想你只是跟着它们 - 只是你手动完成它。我认为scrapy会在扩展允许的代码时为您执行此操作。 FilesPipeline可能无法获得handle_httpstatus_list的值,但全局设置HTTPERROR_ALLOWED_CODES也会影响FilesPipeline

无论如何,requests是一个糟糕的选择,因为它会阻止=绝对非常糟糕的表现。 yield scrapy Request将“让他们不在路上”(现在),但你会再次“遇到他们”,因为他们使用相同的资源,调度程序和下载程序来实现下载。这意味着他们很可能会降低你的表现......这是一件好事。我知道你在这里需要快速抓取,但是scrapy希望你能够意识到你正在做什么以及何时设置并发限制,例如: 8或16,您相信scrapy不会以高于该速率的方式“淹没”您的目标站点。 Scrapy将采取悲观的假设,即由同一服务器/域服务的媒体文件是其Web服务器(而不是某些CDN)的流量,并将应用相同的限制以保护目标站点和您。否则,想象一下恰好有1000个图像的页面。如果你以某种方式“免费”获得1000次下载,你将并行地向服务器发出8000个请求,并发性设置为8 - 这不是一件好事。

如果您想“免费”获得一些下载,即那些不符合并发限制的下载,您可以使用treq。这是Twisted框架的请求包。 Here如何在管道中使用它。使用它来攻击我拥有的API或Web服务器,而不是第三方服务器,我会感觉更舒服。

答案 1 :(得分:0)

警告:有更好的免费黑客解决方案

将此添加到设置:MEDIA_ALLOW_REDIRECTS = True

https://doc.scrapy.org/en/latest/topics/media-pipeline.html#allowing-redirections

请注意,在item_completed results中,您将使用旧的未重定向URL。 file_path也未重定向request。因此,将根据未重定向的数据计算文件名。如果要添加重定向信息,则可能应该在文件管道中实现自己的media_to_download方法,并在response.meta中加入results,因为它应包含重定向信息:

https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#module-scrapy.downloadermiddlewares.redirect