验证码识别,这是一个爬虫开发人员绕不开的痛

Posted 码农小师妹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了验证码识别,这是一个爬虫开发人员绕不开的痛相关的知识,希望对你有一定的参考价值。

点击上方 码农小师妹 ”, 选择“ 设为星标
最新分享,第一时间送达!



Tango,目前就职在一家对日开发的的IT服务公司。 不是科班出身的我,出于对编程的热爱,自学了软件开发。 从此深深陷入在代码的世界而无法自拔。
编辑:王老湿

今天来来分享下关于爬虫开发中验证码识别的问题。先来说下验证码的事情, 验证码(CAPTCHA )的全称为 全自动区分计算机和人类的公开图灵测试 从其定义可以看出,验证码用于测试用户是否为真实人类。 很多网站在登录或者注册时都会出现一个被扭曲的图片,上面写着一些文本。这些其实就是一种验证码。可以看到,验证码的设计初衷是为了区分出人和机器,防止一些机器/爬虫浪费大量的服务器资源,进而导致正常用户访问受到影响。

但是对于我们做爬虫开发来说,验证码就是拦路虎了,而且现在验证码也在不断设计技术中,这对我们爬虫开发人员就是一个绕不开的痛了。所以这周我们将带着大家通过Python程序来自动识别这些验证码。不过 要做好心里准备,程序并不能百分之百的准确识别出来。

情景演示

当我们访问 http://example.python-scraping.com/user/register 这个网站时,你会看到如下的画面:

红框中的内容就是验证码了。我们通过爬虫获取一下这个页面的内容

import requests
from lxml.html import fromstring

def parse_form(html):
    tree = fromstring(html)
    data = {}
    for e in tree.cssselect('form input'):
        if e.get('name'):
            data[e.get('name')] = e.get('value')
    return data

url = "http://example.python-scraping.com/user/register"
session = requests.session()
html = session.get(url)
form = parse_form(html.content) # 获取页面的表单
print(form)
验证码识别,这是一个爬虫开发人员绕不开的痛

需要安装一个cssselect库。pip install cssselect

这些表单内容我们都很好处理,如果要处理验证码,我们首先需要将它加载出来。

在Python中处理图像我们通常使用Pillow包,安装方法:pip install Pillow

我们通过如下代码来返回Image对象。

from io import BytesIO
from PIL import Image
import base64

def get_img(html):
    tree = fromstring(html)
    img_data = tree.cssselect('div#recaptcha img')[0].get('src')
    img_data = img_data.partition(',')[-1]
    binary_img_data = base64.b64decode(img_data)
    img = Image.open(BytesIO(binary_img_data))
    return img

完整代码如下:

import requests
from lxml.html import fromstring

from io import BytesIO
from PIL import Image
import base64

def parse_form(html):
    tree = fromstring(html)
    data = {}
    for e in tree.cssselect('form input'):
        if e.get('name'):
            data[e.get('name')] = e.get('value')
    return data
def get_img(html):
    tree = fromstring(html)
    img_data = tree.cssselect('div#recaptcha img')[0].get('src')
    img_data = img_data.partition(',')[-1]
    binary_img_data = base64.b64decode(img_data)
    img = Image.open(BytesIO(binary_img_data))
    return img

url = "http://example.python-scraping.com/user/register"
session = requests.session()
html = session.get(url)
form = parse_form(html.content) # 获取页面的表单
img = get_img(html.content) # 获取图片信息
print(img)
验证码识别,这是一个爬虫开发人员绕不开的痛

我们为了能从图片中识别出字符,接下来需要用到光学字符识别(OCR)技术。

Tesseract的使用

安装方法:pip install pytesseract

根据自己的系统下载对应的版本,我这边以Windows系统为例。

这时运行基本会报错:

验证码识别,这是一个爬虫开发人员绕不开的痛

我们还需要修改一下源码,就是红框中的py文件。

验证码识别,这是一个爬虫开发人员绕不开的痛

我们把红框中的内容换成我们软件的安装路经,以下是我的安装路径:

验证码识别,这是一个爬虫开发人员绕不开的痛
import pytesseract
str_info = pytesseract.image_to_string(img)
print(str_info)

通常我们如果直接去识别这个图片,很大可能会返回空字符串。这是因为这里面有背景噪音也就是干扰元素。

我们观察我们的验证码的文本颜色为黑色的,所以我们可以将图片中黑色部分分离出来,这个过程通常称为阈值化。

img.save('temp.png?raw=true')
new_img = img.convert('L'# 转化灰度图
new_img.save('temp2.png?raw=true')
bw = new_img.point(lambda x: 0 if x < 1 else 255,'1')
bw.save('temp3.png?raw=true')

运行后我们可以看到如下的图片,在我们脚本的同级目录下:

验证码识别,这是一个爬虫开发人员绕不开的痛

这样我们处理后的图片噪音就少了很多,再通过我们的pytesseract库来进行识别,这样的成功率会比较高。

完整代码:

import requests
from lxml.html import fromstring

from io import BytesIO
from PIL import Image
import base64

import pytesseract

def parse_form(html):
    tree = fromstring(html)
    data = {}
    for e in tree.cssselect('form input'):
        if e.get('name'):
            data[e.get('name')] = e.get('value')
    return data
def get_img(html):
    tree = fromstring(html)
    img_data = tree.cssselect('div#recaptcha img')[0].get('src')
    img_data = img_data.partition(',')[-1]
    binary_img_data = base64.b64decode(img_data)
    img = Image.open(BytesIO(binary_img_data))
    return img

url = "http://example.python-scraping.com/user/register"
session = requests.session()
html = session.get(url)
form = parse_form(html.content) # 获取页面的表单
img = get_img(html.content)
print(img)
img.save('temp.png?raw=true')
new_img = img.convert('L'# 转化灰度图
new_img.save('temp2.png?raw=true')
bw = new_img.point(lambda x: 0 if x < 1 else 255,'1')
bw.save('temp3.png?raw=true')
str_info = pytesseract.image_to_string(bw)
print(str_info)

运行效果:

我们可以看到图片上的内容被识别出来了。

问题

对于这样简单的验证码,即使成功率达不到100%也没关系,只要多请求几次还是可以成功的,但是要注意频率,要不然很容易被封IP。

除了这种简单的验证码之外,还有很多复杂的验证码,这时候我不建议大家花费太多精力在破解验证码上面。我们可以借助第三方工具或者打码平台甚至人工干预来绕过验证码。

好了,这期的内容就是这些,欢迎大家在留言区讨论。

点下「在看」,给文章盖个戳吧!

以上是关于验证码识别,这是一个爬虫开发人员绕不开的痛的主要内容,如果未能解决你的问题,请参考以下文章

使用SCF快速部署验证码识别接口

使用云函数 SCF 快速部署验证码识别接口

了解SSL证书从HTTPS开始 开发者绕不开的“劫”

你绕不开的组件—锁,4个方面手撕锁的多种实现

c++后端绕不开的7个开源项目,每一个源码值得深入研究

做机器人开发,你一定绕不开的模块!