Scrapy error:User timeout caused connection failure

Scrapy error:User timeout caused connection failure

我正在使用 scrapy 抓取阿迪达斯网站:http://www.adidas.com/us/men-shoes。 但是总是报错:

User timeout caused connection failure: Getting http://www.adidas.com/us/men-shoes took longer than 180.0 seconds..

重试5次完全失败

我可以在 chrome 上访问 url 但它在 scrapy 上不起作用。
我试过使用自定义用户代理并模拟 header 请求,但它仍然不起作用。

下面是我的代码:

import scrapy


class AdidasSpider(scrapy.Spider):
    name = "adidas"

    def start_requests(self):

        urls = ['http://www.adidas.com/us/men-shoes']

        headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "en-US,en;q=0.9",
            "Cache-Control": "max-age=0",
            "Connection": "keep-alive",
            "Host": "www.adidas.com",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
        }

        for url in urls:
            yield scrapy.Request(url, self.parse, headers=headers)

    def parse(self, response):
        yield(response.body)

Scrapy 日志:

{'downloader/exception_count': 1,
 'downloader/exception_type_count/twisted.web._newclient.ResponseNeverReceived': 1,
 'downloader/request_bytes': 224,
 'downloader/request_count': 1,
 'downloader/request_method_count/GET': 1,
 'finish_reason': 'shutdown',
 'finish_time': datetime.datetime(2018, 1, 25, 10, 59, 35, 57000),
 'log_count/DEBUG': 2,
 'log_count/INFO': 9,
 'retry/count': 1,
 'retry/reason_count/twisted.web._newclient.ResponseNeverReceived': 1,
 'scheduler/dequeued': 1,
 'scheduler/dequeued/memory': 1,
 'scheduler/enqueued': 2,
 'scheduler/enqueued/memory': 2,
 'start_time': datetime.datetime(2018, 1, 25, 10, 58, 39, 550000)}

更新

在使用 fiddler 查看请求 headers 并进行一些测试后,我发现了导致问题的原因。 Scrapy 默认发送 Connection: close header,因此我没有从阿迪达斯网站收到任何回复。

通过发出相同的请求但没有 Connection: close header 对 fiddler 进行测试后,我得到了正确的响应。现在的问题是如何删除 Connection: close header?

我尝试使用 curl 访问网站,但连接挂起。

curl -v -L http://www.adidas.com/us/men-shoes

所以我跳进了浏览器的调试器,发现请求中有一个 Cookie header。因此,我从 header 复制了整个值并将其粘贴到 curl --headers 命令中。

curl -v -L -H 'Cookie:<cookie value here>' http://www.adidas.com/us/men-shoes

现在 HTML 内容 returns。因此,网站有时会设置访问网站其余部分所需的 cookie。不幸的是,我不确定在何处或如何以编程方式获取 cookie。如果您弄清楚了,请告诉我们。希望这有帮助。

更新

看起来有一些方法可以在 Scrapy 中使用持久的 session 数据(即 cookies)(直到现在我才不得不使用它 :))。看看this answer and this doc。我想也许该网站正在重定向设置 cookie 的请求,但事实并非如此。所以修复起来应该是比较简单的问题。

好吧,至少你应该使用你写的 headers,将 'headers=headers' 添加到你的 scrapy.Request。但是,即使在我尝试 yield scrapy.Request(url, self.parse, headers=headers)

之后它仍然无法正常工作

所以接下来我将 settings.py 中的 User-Agent 更改为您的 headers 中的 User-Agent,即 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" 和没有使用你在 scrapy.Request 中写的 headers,它起作用了。

也许 headers 有问题。但我很确定这与 cookie 无关。

使用您的代码,第一个连接对我来说工作正常 - 它使用您提供的 headers 并获得正确的响应。我修改了您的 parse 方法以跟踪产品链接并从收到的页面打印 <title> 标签的内容,并且效果也很好。示例日志和打印输出如下。我怀疑您是因为请求过多而速度变慢了。

2018-01-27 16:48:23 [scrapy.core.engine] INFO: Spider opened
2018-01-27 16:48:23 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2018-01-27 16:48:23 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2018-01-27 16:48:24 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.adidas.com/us/men-shoes> (referer: None)
2018-01-27 16:48:25 [scrapy.dupefilters] DEBUG: Filtered duplicate request: <GET http://www.adidas.com/> - no more duplicates will be shown (see DUPEFILTER_DEBUG to show all duplicates)
2018-01-27 16:48:25 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (301) to <GET https://www.adidas.com/us/alphabounce-beyond-shoes/DB1126.html> from <GET http://www.adidas.com/us/alphabounce-beyond-shoes/DB1126.html>
2018-01-27 16:48:25 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (301) to <GET https://www.adidas.com/us/ultraboost-laceless-shoes/BB6137.html> from <GET http://www.adidas.com/us/ultraboost-laceless-shoes/BB6137.html>

<snipped a bunch>

2018-01-27 16:48:26 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.adidas.com/us/> (referer: http://www.adidas.com/us/men-shoes)
2018-01-27 16:48:26 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.adidas.com/us/nmd_cs2-primeknit-shoes/BY3012.html> (referer: http://www.adidas.com/us/men-shoes)
adidas Alphabounce Beyond Shoes - White | adidas US
adidas UA&SONS NMD R2 Shoes - Grey | adidas US
adidas NMD_C2 Shoes - Brown | adidas US
adidas NMD_CS2 Primeknit Shoes - Grey | adidas US
adidas NMD_Racer Primeknit Shoes - Black | adidas US
adidas Official Website | adidas US
adidas NMD_CS2 Primeknit Shoes - Black | adidas US
2018-01-27 16:48:26 [scrapy.core.engine] INFO: Closing spider (finished)
2018-01-27 16:48:26 [scrapy.statscollectors] INFO: Dumping Scrapy stats:

您可以使用此工具 https://curl.trillworks.com/

  • 从 Chrome
  • 获取 curl 命令
  • 运行 转换后的 python 代码(我通过请求从您的 URL 得到了响应 200)
  • 为您的 scrapy.Request
  • 复制 headers 和 cookie

因为 scrapy 不允许您编辑 Connection: close header。我使用 scrapy-splash 而不是使用 splash 发出请求。

现在 Connection: close header 可以被覆盖,现在一切正常。缺点是现在网页必须在我从启动响应中加载所有资产,速度较慢但有效。

Scrapy 应该添加选项来编辑它们的默认 Connection: close header。它是硬编码在库中的,不能轻易覆盖。

下面是我的工作代码:

headers = {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
        "Host": "www.adidas.com",
        "Connection": "keep-alive",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
    }

    def start_requests(self):
        url = "http://www.adidas.com/us/men-shoes?sz=120&start=0"
        yield SplashRequest(url, self.parse, headers=self.headers)