web-dev-qa-db-fra.com

Comment configurer Scrapy pour gérer un captcha

J'essaie de gratter un site qui nécessite que l'utilisateur saisisse la valeur de recherche et un captcha. J'ai une routine de reconnaissance optique de caractères (OCR) pour le captcha qui réussit environ 33% du temps. Étant donné que les captchas sont toujours du texte alphabétique, je souhaite recharger le captcha si la fonction OCR renvoie des caractères non alphabétiques. Une fois que j'ai un texte "Word", je veux soumettre le formulaire de recherche.

Les résultats reviennent sur la même page, avec le formulaire prêt pour une nouvelle recherche et un nouveau captcha. Je dois donc rincer et répéter jusqu'à ce que j'aie épuisé mes termes de recherche.

Voici l'algorithme de niveau supérieur:

  1. Charger la page initialement
  2. Téléchargez l'image captcha, exécutez-la via l'OCR
  3. Si l'OCR ne revient pas avec un résultat texte uniquement, actualisez le captcha et répétez cette étape
  4. Soumettez le formulaire de requête dans la page avec le terme de recherche et le captcha
  5. Vérifiez la réponse pour voir si le captcha était correct
  6. Si c'était correct, grattez les données
  7. Allez à 2

J'ai essayé d'utiliser un pipeline pour obtenir le captcha, mais je n'ai pas la valeur pour la soumission du formulaire. Si je récupère simplement l'image sans passer par le cadre, en utilisant urllib ou quelque chose, alors le cookie avec la session n'est pas soumis, donc la validation captcha sur le serveur échoue.

Quelle est la façon idéale Scrapy de faire cela?

16
Sushil

C'est un sujet vraiment profond avec un tas de solutions. Mais si vous voulez appliquer la logique que vous avez définie dans votre article, vous pouvez utiliser des astuces Downloader Middlewares .

Quelque chose comme:

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

Cet exemple va intercepter chaque réponse et essayer de résoudre le captcha. En cas d'échec, il réessayera la page pour le nouveau captcha, s'il réussit, il ajoutera des méta-clés à la réponse avec des valeurs captcha résolues.
Dans votre araignée, vous l'utiliseriez comme ceci:

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