python爬虫-->验证码处理

来源:互联网 发布:java可以做网站吗 编辑:程序博客网 时间:2024/05/19 20:18

在上一篇博文中,介绍了如何自动化的进行表单交互,但是我们是用手动进行网址注册账号的,然后用这个账号密码进行自动化交互。那么在上一篇博文中为何不去自动化的注册账号呢?因为通常在网站注册账号,需要输入图片中的验证码。本篇博文中,将详细介绍如何自动化的对验证码进行处理。

本篇博文将从两个方面来对验证码进行处理

  • 利用OCR自动化处理验证码
  • 在线方式处理复杂验证码

OCR自动化处理验证码
打开注册网页http://example.webscraping.com/places/default/user/register,以下代码是用来获取表单的验证码图像。

import urllibimport urllib2import cookielibfrom io import BytesIOimport lxml.htmlfrom PIL import ImageREGISTER_URL = 'http://example.webscraping.com/places/default/user/register'def extract_image(html):    ## 利用lxml获取表单中图像数据。图像数据的前缀定义了数据类型。    tree = lxml.html.fromstring(html)    img_data = tree.cssselect('div#recaptcha img')[0].get('src')    ##利用逗号分割,将其分为两部分,移除该前缀。这是一张进行了base64编码的图像    img_data = img_data.partition(',')[-1]    #open('test_.png', 'wb').write(data.decode('base64'))    ##进行base64解码,回到最初的二进制    binary_img_data = img_data.decode('base64')    ##要想加载该图片,PIL需要对一个类似文件的接口,在传给Image类,我们又使用ByteIO对这个二进制进行封装    file_like = BytesIO(binary_img_data)    img = Image.open(file_like)    return img

光学字符识别(OCR)用于从图像中抽取文本。通过以下命令安装:

    sudo pip install pytesseract    安装后运行如果出现错误:OSError: [Errno 2] No such file or directory    sudo apt-get install tesseract-ocr

如果我们想更好的使用Tesseract,需要先修改验证码图像,去除其中背景噪音,只保留其中文本部分。可以通过阈值化对图像背景和文本部分进行分离。以下为实现代码:

def ocr(img):    # threshold the image to ignore background and keep text    #img.save('capcha_originl.png')    gray = img.convert('L')    #gray.save('captcha_greyscale.png')    bw = gray.point(lambda x: 0 if x < 1 else 255, '1')##只保留阈值小于1的像素也就是全黑的像素才保留。    #bw.save('captcha_threshold.png')    #print bw    word = pytesseract.image_to_string(bw)    ascii_word = ''.join(c for c in word if c in string.letters).lower() ##将识别的每个字母连接起来组成验证码    print ascii_word    return ascii_word

说了这么多,我们来尝试下自动化处理验证码和自动化注册,以下实现代码:

def parse_form(html):    """extract all input properties from the form    提取表单内所有的输入属性值对    """    tree = lxml.html.fromstring(html)    data = {}    for e in tree.cssselect('form input'):        if e.get('name'):            data[e.get('name')] = e.get('value')    return datadef register(first_name, last_name, email, password, captcha_fn):    ##自动化处理验证码,进而自动化的注册网站账号密码    cj = cookielib.CookieJar()    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))    html = opener.open(REGISTER_URL).read()    form = parse_form(html)    form['first_name'] = first_name    form['last_name'] = last_name    form['email'] = email    form['password'] = form['password_two'] = password    img = extract_image(html)##获取图像数据    captcha = ocr(img)##利用OCR进行识别图像内文本    form['recaptcha_response_field'] = captcha    encoded_data = urllib.urlencode(form)    request = urllib2.Request(REGISTER_URL, encoded_data)    response = opener.open(request)    success = '/user/register' not in response.geturl()##如果注册成功,则会发生跳转,'/user/register'不在跳转以后的网址内    return successif __name__ == '__main__':    print register('Test Account', 'Test Account', '1234567@webscraping.com', 'example', ocr)

在线方式处理复杂验证码
这里写图片描述
这里写图片描述
像类似以上的一些验证码图像,OCR很难很好的识别,有些验证码人眼都不容易识别。此时我们需要使用一些在线人工服务来解决。

这里我们使用9kw,打开网页9kw官网进行注册https://www.9kw.eu/register.html,注册完成后将被定位到https://www.9kw.eu/usercaptcha.html,在本页中,需要处理其他用户发来的验证码。在处理了几个验证码以后,我们将被定位到https://www.9kw.eu/userapi_203959.html,从而获取自己的API Key

这里写图片描述

下面是发送验证码到该API的初始实现代码

    def send(self, img_data):        """Send CAPTCHA for solving        发送验证码到该API        """        print 'Submitting CAPTCHA'        data = {            'action': 'usercaptchaupload',            'apikey': self.api_key,            'file-upload-01': img_data.encode('base64'),            'base64': '1',            '''            'selfsolve': '1':表示如果我们正在使用9kw的Web界面处理验证码,那么验证码图像会传给我们自己处理             如果我们没有处于登录,那么会将验证码图像传给其他用户处理            '''            'selfsolve': '1',            'maxtimeout': str(self.timeout)        }        encoded_data = urllib.urlencode(data)        request = urllib2.Request(self.url, encoded_data)        response = urllib2.urlopen(request)        result = response.read()        self.check(result)        return result

下面是获取验证码图像处理结果的代码

    def get(self, captcha_id):        """Get result of solved CAPTCHA        获取验证码图像处理结果        """        data = {            'action': 'usercaptchacorrectdata',            'id': captcha_id,            'apikey': self.api_key,            'info': '1'        }        encoded_data = urllib.urlencode(data)        response = urllib2.urlopen(self.url + '?' + encoded_data)        result = response.read()        self.check(result)        return result

9kw的API有两个缺点:
①其响应是普通字符串,而不是json结构化格式。例如:如果此时没有用户处理验证码图像,将会返回“ERROR NO USER”。幸好一般的验证码里面文本不会是“ERROR NO USER”。
②只有在其他用户有时间人工处理验证码图像时,上面的get函数才能返回信息。通常在30s以后。

为了实现友好,我们将会增加一个封装函数,用于提交验证码图像以及等待结果返回。下面的扩展版本把这些功能封装到一个可复用类中,另外增加了检查错误功能。

#coding:utf-8import sysimport reimport urllib2import urllibimport timefrom io import BytesIOfrom PIL import Imagefrom form import registerdef main(api_key):    captcha = CaptchaAPI(api_key)    print register('Test Account', 'Test Account', 'example125@webscraping.com', 'example', captcha.solve)class CaptchaError(Exception):    passclass CaptchaAPI:    def __init__(self, api_key, timeout=60):        self.api_key = api_key        self.timeout = timeout        self.url = 'https://www.9kw.eu/index.cgi'    def solve(self, img):        """Submit CAPTCHA and return result when ready        """        img_buffer = BytesIO()        img.save(img_buffer, format="PNG")        img_data = img_buffer.getvalue()        captcha_id = self.send(img_data)##发送验证码到该API        start_time = time.time()        while time.time() < start_time + self.timeout:            try:                text = self.get(captcha_id)##获取验证码图像处理结果            except CaptchaError:                pass # CAPTCHA still not ready            else:                if text != 'NO DATA':                    if text == 'ERROR NO USER':                        raise CaptchaError('Error: no user available to solve CAPTCHA')                    else:                        print 'CAPTCHA solved!'                        print "验证码为 :",text                        return text            print 'Waiting for CAPTCHA ...'        raise CaptchaError('Error: API timeout')    def send(self, img_data):        """Send CAPTCHA for solving        发送验证码到该API        """        print 'Submitting CAPTCHA'        data = {            'action': 'usercaptchaupload',            'apikey': self.api_key,            'file-upload-01': img_data.encode('base64'),            'base64': '1',            '''            'selfsolve': '1':表示如果我们正在使用9kw的Web界面处理验证码,那么验证码图像会传给我们自己处理             如果我们没有处于登录,那么会将验证码图像传给其他用户处理            '''            'selfsolve': '1',            'maxtimeout': str(self.timeout)        }        encoded_data = urllib.urlencode(data)        request = urllib2.Request(self.url, encoded_data)        response = urllib2.urlopen(request)        result = response.read()        self.check(result)        return result    def get(self, captcha_id):        """Get result of solved CAPTCHA        获取验证码图像处理结果        """        data = {            'action': 'usercaptchacorrectdata',            'id': captcha_id,            'apikey': self.api_key,            'info': '1'        }        encoded_data = urllib.urlencode(data)        response = urllib2.urlopen(self.url + '?' + encoded_data)        result = response.read()        self.check(result)        return result    def check(self, result):        """Check result of API and raise error if error code detected        该方法只检查初始字符,确认其是否遵循错误信息前包含4位数字错误码的格式。        """        if re.match('00\d\d \w+', result):            raise CaptchaError('API error: ' + result)if __name__ == '__main__':        api_key = '1FLYJ8B8035NQM9K12'        main(api_key)

上面的类使用你的APIkey以及超时时间进行实例化,其中超时时间为60s,然后solve()方法把验证码图像提交给API,并持续请求,直到验证码图像处理完成或者达到超时时间。

这里写图片描述

原创粉丝点击