【问题标题】:Unable to force a script to retry for five times unless a 200 status in between除非中间有 200 状态,否则无法强制脚本重试五次
【发布时间】:2020-12-22 14:04:13
【问题描述】:

我使用scrapy 创建了一个脚本,它能够递归地重试列表中的某些链接,即使这些链接无效并得到404 响应。我在meta 中使用了dont_filter=True'handle_httpstatus_list': [404] 来实现当前行为。我现在要做的是让脚本做同样的事情 5 次,除非两者之间有 200 status。我将"max_retry_times":5 包含在meta 中,考虑到它最多会继续重试五次,但它只是无限重试。

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

import scrapy
from bs4 import BeautifulSoup
from scrapy.crawler import CrawlerProcess

class StackoverflowSpider(scrapy.Spider):
    name = "stackoverflow"
    start_urls = [
        "https://stackoverflow.com/questions/taggedweb-scraping",
        "https://stackoverflow.com/questions/taggedweb-scraping"
    ]

    def start_requests(self):
        for start_url in self.start_urls:
            yield scrapy.Request(start_url,callback=self.parse,meta={"start_url":start_url,'handle_httpstatus_list': [404],"max_retry_times":5},dont_filter=True)

    def parse(self,response):
        if response.meta.get("start_url"):
            start_url = response.meta.get("start_url")

        soup = BeautifulSoup(response.text,'lxml')
        if soup.select(".summary .question-hyperlink"):
            for item in soup.select(".summary .question-hyperlink"):
                title_link = response.urljoin(item.get("href"))
                print(title_link)

        else:
            print("++++++++++"*20) # to be sure about the recursion
            yield scrapy.Request(start_url,meta={"start_url":start_url,'handle_httpstatus_list': [404],"max_retry_times":5},dont_filter=True,callback=self.parse)
            
if __name__ == "__main__":
    c = CrawlerProcess({
        'USER_AGENT':'Mozilla/5.0',
    })
    c.crawl(StackoverflowSpider)
    c.start()

如何让脚本最多重试五次?

注意:列表中有多个相同的 url。我不想踢出重复的链接。我想让scrapy使用所有的url。

【问题讨论】:

  • 这里只是评论,你用scrapy的时候不需要bs4。你可以简单地response.css(".summary .question-hyperlink::attr(href)").getall()Check Scrapy docs for more info on selectors
  • 感谢@Thiago Curvelo 的指点。我已经知道了。

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


【解决方案1】:

我可以提出以下方向:
1. 将404 代码添加到RETRY_HTTP_CODES 设置,因为它默认不包含响应代码404

即使链接无效并得到 404 响应,也能够递归重试链接

class StackoverflowSpider(scrapy.Spider):
    name = "stackoverflow"
    custom_settings = {
        'RETRY_HTTP_CODES' : [500, 502, 503, 504, 522, 524, 408, 429 , 404],
        'RETRY_TIMES': 5 # usage of "max_retry_times" meta key is also valid
    }
....
  1. 带有dont_filter=True - scrapy 应用程序将访问以前访问过的页面。
    从代码中删除 dont_filter=True 应该可以解决无限循环问题

但它只是无限重试。

【讨论】:

  • 您的解决方案似乎工作正常。如果列表中有两个相同的链接怎么办?我不应该坚持使用dont_filter=True 以便scrapy 使用两个相同的链接吗?
  • 如果您在start_urls 中有两个相同的网址,并且如果在start_requests 方法中没有dont_filter=True - 将只调用第一个网址的请求,第二个网址将被过滤。重复
  • 3.保持dont_filter=True并添加DEPTH_LIMIT设置。在这种情况下,应用程序不会过滤重复链接,同时由于深度限制,它不会有无穷无尽的 lopp。
  • 这似乎很有希望@Georgiy。你能帮我遵守这种方法,因为它对我来说是陌生的吗?谢谢。
【解决方案2】:
import scrapy
from bs4 import BeautifulSoup
from scrapy.crawler import CrawlerProcess

class StackoverflowSpider(scrapy.Spider):
    name = "stackoverflow"
    start_urls = [
        "https://stackoverflow.com/questions/taggedweb-scraping",
        "https://stackoverflow.com/questions/taggedweb-scraping"
    ]

    def start_requests(self):
        for start_url in self.start_urls:
            yield scrapy.Request(start_url,callback=self.parse,meta={"request_count":0,'handle_httpstatus_list': [404],"max_retry_times":5},dont_filter=True)

    def parse(self,response):
        
        soup = BeautifulSoup(response.text,'lxml')
        if soup.select(".summary .question-hyperlink"):
            for item in soup.select(".summary .question-hyperlink"):
                title_link = response.urljoin(item.get("href"))
                print(title_link)

        else:
            request_count = response.meta.get("request_count")
            max_retry_times = response.meta.get("max_retry_times")
            if request_count < max_retry_times :
                start_url = response.url
                request_count += 1    
                yield scrapy.Request(start_url,meta={"request_count":request_count,'handle_httpstatus_list': [404],"max_retry_times":max_retry_times },dont_filter=True,callback=self.parse)
            
if __name__ == "__main__":
    c = CrawlerProcess({
        'USER_AGENT':'Mozilla/5.0',
    })
    c.crawl(StackoverflowSpider)
    c.start()

我想是的。如果我有错误,请告诉我您的意见。

问候!

【讨论】:

    【解决方案3】:

    我对scrapy一无所知,所以如果这个解决方案不起作用,我深表歉意。

    但对于一个简单的计数器,我发现 while 循环运行良好。它看起来像这样:

    x = 5
    while not x == 0:
        for start_url in self.start_urls:
                yield scrapy.Request(start_url,callback=self.parse,meta={"start_url":start_url,'handle_httpstatus_list': [404],"max_retry_times":5},dont_filter=True)
        # 200 status check goes here using an if statement that results in a break statement if true
        x -= 1
    

    因此您执行了您希望执行的操作,然后从您的计数器中减去 1。动作一次又一次地循环,你从你的计数器中减去一个。重复此过程,直到您的计数器达到零,此时您的 while 循环中断并且您停止循环通过您的代码。

    要添加对200 status 的检查,请在代码中添加一个 if 检查,恐怕我不确定该怎么做,并将其放在x -= 1 之前。如果200 statusTrue,则添加一个break 语句以退出while 循环。或者,如果您想稍后在代码中使用 x 计数器(例如,您正在运行多个不同的函数检查),您可以在 break 语句之前使用 x = 0

    再说一次,我对scrapy一无所知,所以如果这不是一个合适的解决方案,我深表歉意。

    【讨论】:

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