Python - ステータスが200に達しない限り、スクリプトに5回の再試行を強制できません

okwaves2024-01-25  11

scrapy を使用して、リンクが無効で 404 応答が返された場合でも、リストから一部のリンクを再帰的に再試行できるスクリプトを作成しました。現在の動作を実現するために、メタ内で dont_filter=True と 'handle_httpstatus_list': [404] を使用しました。私が今やろうとしているのは、間にステータスが 200 でない限り、スクリプトに同じことを 5 回実行させることです。最大 5 回は再試行を続けますが、無限に再試行するだけであるという事実を考慮して、メタ内に "max_retry_times":5 を含めました。

これまでに試してみました:

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()

スクリプトが最大 5 回再試行し続けるようにするにはどうすればよいですか?

注: リストには同一の URL が複数あります。重複リンクを排除したくありません。スクレイピーにすべてを使用させたいのですが、URL。

1

ここでコメントします。scrapy を使用する場合は bs4 は必要ありません。単純に、response.css(".summary .question-hyperlink::attr(href)").getall() を実行することもできます。セレクターの詳細については、Scrapy のドキュメントを確認してください。

– チアゴ・クルヴェロ

2020 年 9 月 8 日 3:09

@Thiago Curvelo さん、ご指摘ありがとうございます。それについてはすでに承知しています。

– ミス

2020 年 9 月 8 日 6:01



------------------------

次のような方向性を提案できます。 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
    }
....
dont_filter=True の場合 - Scrapy アプリケーションは以前にアクセスしたページにアクセスします。 コードから dont_filter=True を削除すると、無限ループの問題が解決されるはずです

ただし、無限に再試行されるだけです。

4

あなたのソリューションは正しく機能しているようです。リスト内にそのような同一のリンクが 2 つある場合はどうなるでしょうか? Scrapy が両方の同一のリンクを使用するように、dont_filter=True を使用しないほうがよいでしょうか?

– ミス

2020 年 9 月 5 日 5:54

start_urls に 2 つの同一の URL があり、start_re に dont_filter=True がない場合クエスト メソッド - 最初の URL へのリクエストのみが呼び出され、2 番目の URL は重複としてフィルターされます。

– ジョージイ

2020 年 9 月 5 日 15:28

3. dont_filter=True を維持し、DEPTH_LIMIT 設定を追加します。この場合、アプリケーションは重複リンクをフィルタリングせず、同時に深さ制限による無限のロップも発生しません。

– ジョージイ

2020 年 9 月 7 日 14:55

@Georgiy は有望ですね。このアプローチは私にとって馴染みのないものなので、従うのを手伝っていただけますか?ありがとう。

– ミス

2020 年 9 月 7 日 16:03



------------------------

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()

そう思います。間違いがあれば、あなたの意見を教えてください。

よろしく!



------------------------

私はスクレイピーについて何も知らないので、この解決策が機能しない場合は申し訳ありません。

しかし、単純なカウンターの場合は、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 を引いたものになります。アクションは何度もループし、カウンターから 1 が減ります。これはカウンタがゼロになるまで繰り返され、その時点で while ループが中断され、コードのループが停止します。

200 ステータスのチェックを追加するには、コードに if チェックを追加します。申し訳ありませんが、その方法がわかりません。それを x -= 1 の前に置きます。200 ステータスが True の場合は、次の値を追加します。 while ループを終了するための Break ステートメント。あるいは、コードの後半で x カウンターを使用したい場合 (たとえば、いくつかの異なる関数チェックを実行している場合)、break ステートメントの前に x = 0 を使用することもできます。

繰り返しになりますが、私はまだスクレイピーについて何も知りません。これは適切な解決策ではありません。

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。