Как настроить Scrapy для обработки капчей

Я пытаюсь очистить сайт, который требует, чтобы пользователь вводил значение поиска и капчу. У меня есть программа оптического распознавания символов (OCR) для капчей, которая достигает примерно 33% времени. Поскольку captchas всегда алфавитный текст, я хочу перезагрузить captcha, если функция OCR возвращает неалфавитные символы. Когда у меня есть текстовое "слово", я хочу отправить форму поиска.

Результаты возвращаются на той же странице, с формой, готовой для нового поиска и новой капчей. Поэтому мне нужно промыть и повторить, пока я не исчерпал свои поисковые запросы.

Здесь алгоритм верхнего уровня:

  • Сначала загрузите страницу.
  • Загрузите изображение captcha, запустите его через OCR
  • Если OCR не возвращается с текстовым результатом, обновите капчу и повторите этот шаг.
  • Отправить форму запроса на странице с поисковым запросом и captcha
  • Проверьте ответ, чтобы увидеть, правильно ли был конвертирован.
  • Если это было правильно, очистите данные
  • Перейдите к 2

Я попытался использовать конвейер для получения captcha, но тогда у меня нет значения для отправки формы. Если я просто извлечу изображение, не пройдя через фреймворк, используя urllib или что-то еще, cookie с сеансом не будет отправлен, поэтому проверка на качалке на сервере не удастся.

Какой идеальный способ лечения?

Ответ 1

Это действительно глубокая тема с кучей решений. Но если вы хотите применить логику, которую вы определили в своем посте, вы можете использовать scrapy 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

Этот пример будет перехватывать каждый ответ и попытаться решить капчу. Если это не удастся, он будет повторять страницу для новой captcha, в случае успеха она добавит некоторые мета-ключи для ответа с решенными значениями captcha.
В вашем пауке вы будете использовать его следующим образом:

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