瞎BB 验证码真的玩出花来了,爬某些网站的时候,得先通过验证码验证,才能访问网站页面,真是一波违停一波又起。所以开了一个新坑,浅浅的谈一谈验证码反爬这件小事情。哈哈哈其实说到验证码,我脑海里想到的是**“Are you a robot?”**
验证码形式
有基于文本、图像、音频形式的验证码
流行的验证方式
输入式、滑动式、宫格式、点击式图文验证
普通验证码 输入式验证码
这种也可以叫做图形验证码识别 ,主要是用户输入图片中的字母、数字、汉字等即可通过验证,这种算是最常见也是最简单的一种识别方式。
获取验证码
为了便于实验,我直接将验证码图片保存到本地,并且是以pic.jpg格式命名。
验证码
思路
只需要识别出图片中的内容,然后在验证框里面输入对应内容即可。这种针对文字的识别叫做OCR(Optical Character Recognition)。在python中可以用第三方库tesserocr,对于背景没有什么影响的验证码,可以直接用这个库。但是总是有不讲武德的验证码(背景嘈杂,花里胡哨的),直接识别的识别率会很低。针对这种问题,可以对图片进行灰度化、二值化处理,之后再识别。
动手写代码
1
2
3
4
5
6
7
8
import tesserocr
from PIL import Image
# 打开图片
image = Image. open('pic.jpg' )
# 传入image对象进行识别,并转化为文本
resultCode = tesserocr. image_to_text(image)
pring(resultCode)
#结果:75F4
上面一种方法,有时候识别结果与实际结果会存在偏差,因为这种验证码的背景存在线条干扰。
二值化
二值化就是将大于某个值的像素点都修改为255,小于该值的修改为0
0表示白色,1表示黑色。默认阈值是127
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import tesserocr
from PIL import Image
# 打开图片
image = Image. open('pic.jpg' )
image = image. convert('L' )
# 自定义灰度界限,大于这个值为黑色,小于这个值为白色
threshold = 127
table = []
for i in range (256 ):
if i < threshold:
table. append(0 )
else :
table. append(1 )
image = image. point(table, '1' )
resultCode = tesserocr. image_to_text(image)
pring(resultCode)
#结果:75F4
处理后的图:
复杂验证码 滑动式验证码相对于图形验证码来说,难度还真不小。
思路
用opencv分析缺块位置,然后用selenium模拟滑动行为,这里要注意滑动速度,匀速不行,因为人移动滑块是很难做到匀速的,很容易被机器识别出是程序操作。
分析缺块位置
还是为了测试方便,我先把缺块背景图下载到本地。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from matplotlib import pyplot as plt
import cv2
pic_path = './2.jpg'
# 读取图片
image = cv2. imread(pic_path)
# 颜色转换,将 BGR 转换为 RGB(原始图或许存在误差)
image = cv2. cvtColor(image, cv2. COLOR_BGR2RGB)
plt. imshow(image)
plt. show() #输出结果一
# 输出缺块边缘范围的影响,其余的灰度化。
canny = cv2. Canny(image, 300 , 300 )
plt. imshow(canny)
plt. show() #输出结果二
运行结果如下:
结果一
结果二
现在要做的就在找到缺块位置的坐标。Canny是边缘检测算法,能够检测到缺块的边缘。cv库中的canny将原图灰阶化之后输出边缘像。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#边缘化处理:
canny = cv2. Canny(image,600 ,600 ) #这里的阈值越高,输出的边缘越准确
plt. imshow(canny)
plt. show()
# 获取坐标:
# 找出图片中所有检测到的区块,并用线框标记下来
contours, hierarchy = cv2. findContours(canny, cv2. RETR_CCOMP, cv2. CHAIN_APPROX_SIMPLE)
# 坐标初值设置为0
dx, dy = 0 , 0
for i, contour in enumerate (contours):
# 从长度(w)和高度(h)判断那些区块是真正的区块
x, y, w, h = cv2. boundingRect(contour)
if (w > 50 ) and (h > 50 ):
dx = x
dy = y
cv2. rectangle(image, (x, y), (x + w, y + h), (0 , 0 , 255 ), 2 )
print (dx, dy) #输出结果:253,143
可以看到除了圈出来的区块,还有一些小杂点
selenium模拟操作
上面的分析只是一小部分,接下来就是将它串起来用
思路:
用selenium打开浏览器,并把图片下载下来 找到按钮(有些验证码是需要点击一下按钮,才会出现缺块) 分析缺块位置(上面的方法) 模拟鼠标拖动滑块行为 1 .打开浏览器,下载图片。一般情况是先获取图片连接,然后再用request+open进行下载。不过这里有个问题,如果遇到反爬,图片下载不了,那就挺麻烦的。我选择直接用selenium截图。
1
2
3
4
5
6
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver. Chrome('./chromedriver' )
driver. get("http://www.example.com" )
driver. find_element_by_xpath('//body/div[4]/img[1]' ). screenshot("1.jpg" )
2 . 图片下载好之后,就用opencv分析缺块位置
3 . 找出滑块按钮元素,然后根据偏量移动滑块
1
2
3
4
5
button = driver. find_element_by_class_name("secsdk-captcha-drag" )
drag = ActionChains(driver)
drag. click_and_hold(button)
drag. move_by_offset(dx, 0 )
drag. perform()
总结 这两种验证码只是冰山一角,还有宫格式,点击图文式验证码,学麻了。