7.2 光学字符识别
光学字符识别 (Optical Character Recognition ,OCR )用于从图像中抽取文本。本节中,我们将使用开源的Tesseract OCR引擎。该引擎最初由惠普公司开发,目前由Google主导。Tesseract的安装说明可以从https://code. google.com/p/tesseract-ocr/wiki/ReadMe 获取。然后,可以使用pip 安装其Python封装版本pytesseract 。
pip install pytesseract
如果直接把验证码原始图像传给pytesseract ,解析结果一般都会很糟糕。
>>> import pytesseract >>> img = get_captcha(html) >>> pytesseract.image_to_string(img) ''
上面的代码在执行后,会返回一个空字符串 [1] ,也就是说Tesseract在抽取输入图像中的字符时失败了。这是因为Tesseract的设计初衷是抽取更加典型的文本,比如背景统一的书页。如果我们想要更加有效地使用Tesseract,需要先修改验证码图像,去除其中的背景噪音,只保留文本部分。为了更好地理解我们将要处理的验证码系统,图7.3中又给出了几个示例验证码。
图7.3
从图7.3中的例子可以看出,验证码文本一般都是黑色的,背景则会更加明亮,所以我们可以通过检查像素是否为黑色将文本分离出来,该处理过程又被称为阈值化 。通过Pillow 可以很容易地实现该处理过程。
>>> img.save('captcha_original.png') >>> gray = img.convert('L') >>> gray.save('captcha_gray.png') >>> bw = gray.point(lambda x: 0 if x < 1 else 255, '1') >>> bw.save('captcha_thresholded.png')
此时,只有阈值小于1的像素才会保留,也就是说,只有全黑的像素才会保留下来。这段代码片段保存了3张图像,分别是原始验证码图像、转换后的灰度图以及阈值化处理后的图像。图7.4所示为每个阶段保存的图像。
图7.4
最终,经过阈值化处理的图像中,文本更加清晰,此时我们就可以将其传给Tesseract进行处理了。
>>> pytesseract.image_to_string(bw) 'strange'
成功了!验证码中的文本已经被成功抽取出来了。在我测试的100张图片中,该方法正确解析了其中的84个验证码图像。由于示例文本总是小写的ASCII字符,因此我们可以将结果限定在这些字符中,从而进一步提高性能。
>>> import string >>> word = pytesseract.image_to_string(bw) >>> ascii_word = ''.join(c for c in word if c in string.letters).lower()
在对相同的100张图片的测试中,其识别率提高到了88%。下面是目前注册脚本的完整代码。
import cookielib import urllib import urllib2 import string import pytesseract REGISTER_URL = 'http://example.webscraping.com/user/register' def register(first_name, last_name, email, password): 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) 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() return success def ocr(img): gray = img.convert('L') bw = gray.point(lambda x: 0 if x < 1 else 255, '1') word = pytesseract.image_to_string(bw) ascii_word = ''.join(c for c in word if c in string.letters).lower() return ascii_word
register() 函数下载注册页面,抓取其中的表单,并在表单中设置新账号的名称、邮箱地址和密码。然后抽取验证码图像,传给OCR函数,并将OCR函数产生的结果添加到表单中。接下来提交表单数据,检查响应URL,确认注册是否成功。如果注册失败,响应URL仍然会是注册页,这既可能是因为验证码图像解析不正确,也可能是注册账号的邮箱地址已经存在。现在,只需要使用新账号信息调用register() 函数,就可以注册账号了。
>>> register(first_name, last_name, email, password) True
7.2.1 进一步改善
要想进一步改善验证码OCR的性能,下面还有一些可能会使用到的方法:
实验不同阈值;
腐蚀阈值文本,突出字符形状;
调整图像大小(有时增大尺寸会起到作用);
根据验证码字体训练OCR工具;
限制结果为字典单词。
如果你对改善性能的实验感兴趣,可以使用该链接中的示例数据:https://bitbucket.org/wswp/code/src/tip/chapter07/samples/ 。不过,对于我们注册账号这一目的,目前88%的准确率已经足够了,这是因为即使是真实用户也会在输入验证码文本时出现错误。实际上,即使1%的准确率也是足够的,因为脚本可以运行多次直至成功,不过这样做对服务器不够友好,甚至可能会导致IP被封禁。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论