web-dev-qa-db-ja.com

Scrapyを設定してキャプチャに対応する方法

ユーザーが検索値とキャプチャを入力する必要があるサイトをスクレイピングしようとしています。キャプチャ用の光学式文字認識(OCR)ルーチンがあり、これは約33%の時間成功します。キャプチャは常にアルファベットのテキストなので、OCR関数がアルファベット以外の文字を返した場合は、キャプチャをリロードしたいと思います。 「Word」というテキストを入手したら、検索フォームを送信します。

結果は同じページに戻り、フォームは新しい検索と新しいキャプチャの準備ができています。そのため、検索語句がなくなるまですすぎ、繰り返す必要があります。

最上位のアルゴリズムは次のとおりです。

  1. 最初にページを読み込む
  2. キャプチャ画像をダウンロードし、OCRで実行します
  3. OCRがテキストのみの結果を返さない場合は、キャプチャを更新してこの手順を繰り返します。
  4. 検索語句とキャプチャを含むクエリフォームをページに送信します
  5. 応答をチェックして、キャプチャが正しいかどうかを確認します
  6. 正しければ、データを削り取ります
  7. 2へ

CAPTCHAを取得するためにパイプラインを使用しようとしましたが、フォーム送信の値がありません。フレームワークを経由せずに画像をフェッチするだけの場合、urllibなどを使用すると、セッションのCookieが送信されないため、サーバーでのキャプチャ検証が失敗します。

これを行うための理想的なScrapyの方法は何ですか?

16
Sushil

それはたくさんの解決策がある本当に深いトピックです。ただし、投稿で定義したロジックを適用する場合は、スクレイピー Downloader Middlewares を使用できます。

何かのようなもの:

class CaptchaMiddleware(object):
    max_retries = 5
    def process_response(request, response, spider):
        if not request.meta.get('solve_captcha', False):
            return response  # only solve requests that are marked with meta key
        catpcha = find_catpcha(response)
        if not captcha:  # it might not have captcha at all!
            return response
        solved = solve_captcha(captcha)
        if solved:
            response.meta['catpcha'] = captcha
            response.meta['solved_catpcha'] = solved
            return response
        else:
            # retry page for new captcha
            # prevent endless loop
            if request.meta.get('catpcha_retries', 0) == 5:
                logging.warning('max retries for captcha reached for {}'.format(request.url))
                raise IgnoreRequest 
            request.meta['dont_filter'] = True
            request.meta['captcha_retries'] = request.meta.get('captcha_retries', 0) + 1
            return request

この例では、すべての応答をインターセプトし、キャプチャを解決しようとします。失敗した場合は、新しいキャプチャのページを再試行し、成功した場合は、解決されたキャプチャ値を使用して応答にメタキーを追加します。
クモでは、次のように使用します。

class MySpider(scrapy.Spider):
    def parse(self, response):
        url = ''# url that requires captcha
        yield Request(url, callback=self.parse_captchad, meta={'solve_captcha': True},
                      errback=self.parse_fail)

    def parse_captchad(self, response):
        solved = response['solved']
        # do stuff

    def parse_fail(self, response):
        # failed to retrieve captcha in 5 tries :(
        # do stuff
11
Granitosaurus