如何设置Scrapy处理验证码

19

我正在尝试爬取一个需要用户输入搜索值和验证码的网站。我有一个光学字符识别(OCR)例程用于验证码,成功率约为33%。由于验证码始终是字母文本,所以如果OCR函数返回非字母字符,我希望重新加载验证码。一旦我获得了一个文本"单词",我就想提交搜索表单。

结果会在同一页返回,表单已准备好进行新的搜索和新的验证码。因此,我需要反复执行,直到耗尽搜索条款。

以下是顶级算法:

  1. 最初加载页面
  2. 下载验证码图像,将其通过OCR运行
  3. 如果OCR未返回仅文本结果,请刷新验证码并重复此步骤
  4. 在具有搜索条件和验证码的页面中提交查询表单
  5. 检查响应以查看验证码是否正确
  6. 如果正确,则进行数据抓取
  7. 转到2

我尝试使用管道获取验证码,但然后我没有表单提交的值。如果我只是获取图像而不经过框架,使用urllib或其他方法,那么将不会提交包含会话cookie的cookie,因此服务器上的验证码验证会失败。

Scrapy最理想的做法是什么?

1个回答

11

这是一个非常深入的话题,有许多解决方案。但如果您想要应用您在帖子中定义的逻辑,可以使用Scrapy下载中间件

类似这样:

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) == max_retries:
                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

7
你还没有定义任何find_captcha()和solve_captcha()函数。它们是库函数还是自定义函数? - Priyansh gupta
3
@Priyanshgupta,这是你自己需要定义的内容,因为这些函数已经超出了stackoverflow问题的范围。 - Granitosaurus
2
你能列举一些那些“一堆解决方案”吗?除了Scrapy的“下载器中间件”。 - Simao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接