python爬虫-->验证码处理

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

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

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

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

import urllib
import urllib2
import cookielib
from io import BytesIO
import lxml.html
from PIL import Image


REGISTER_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 data

def 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 success

if __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-8

import sys
import re
import urllib2
import urllib
import time
from io import BytesIO
from PIL import Image
from form import register


def main(api_key):
    captcha = CaptchaAPI(api_key)
    print register('Test Account', 'Test Account', 'example125@webscraping.com', 'example', captcha.solve)


class CaptchaError(Exception):
    pass


class 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,并持续请求,直到验证码图像处理完成或者达到超时时间。

这里写图片描述

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值