验证码反爬

瞎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()

总结

这两种验证码只是冰山一角,还有宫格式,点击图文式验证码,学麻了。