selenium + opencv模拟通过腾讯滑动验证码

opencv识别验证码图片缺口,selenium实现点击及拖拽滑动验证码,成功率95%以上

环境

操作系统 Windows 10
编译器 VScode
python 3.8.9

安装

使用 pipenv 安装第三方模块:

1
2
3
pipenv install requests
pipenv install selenium
pipenv install opencv-python

更多见 selenium的安装及配置

代码

代码来源

只需要修改 __init__()login() 方法

前几次滑动失败很正常,成功一次基本上就稳了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import time
import random
import requests

from cv2 import cv2 as cv
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


driver = Chrome()


class Tx():

def __init__(self):

# 网站登陆地址,这里是大乐斗网址(有时不会有验证码)
self.url = 'http://dld.qzapp.z.qq.com/qpet/cgi-bin/phonepk?zapp_uin=&sid=b+BqyBVjL/uOqlmjH2/Kp0/GqwABVg/196790a110201==&channel=0&g_ut=1&cmd=index'
# 账号
self.username = '123456345' # 这个账户不存在
# 密码
self.password = "abcdxdk" # 这个密码不存在

# 显性等待
self.wait = WebDriverWait(driver, 10)

def login(self):
'''
实现网站登陆
'''

# 登陆网址
driver.get(self.url)

# 输入账号
账号 = self.wait.until(EC.presence_of_element_located((By.ID, 'u')))
账号.send_keys(self.username)

# 输入密码
密码 = self.wait.until(EC.presence_of_element_located((By.ID, 'p')))
密码.send_keys(self.password)

# 登录
self.wait.until(EC.presence_of_element_located((By.ID, 'go'))).click()
time.sleep(5)

# 如果该iframe是可用的则返回True并且将给定的驱动切换到指定的iframe,否则引发异常
# 这样就可以判断有没有验证码
iframe = self.wait.until(
EC.frame_to_be_available_and_switch_to_it((By.ID, 'tcaptcha_iframe')))
if iframe:
self.tx_code()

def tx_code(self):
'''
滑动滑块
'''

# 获取验证码链接
bk_block = self.wait.until(EC.presence_of_element_located(
(By.ID, 'slideBg'))).get_attribute('src')

if self.save_img(bk_block):
dex = self.get_pos()
track_list = self.get_track(dex)
time.sleep(0.5)

# 滑块定位
slid_ing = self.wait.until(EC.presence_of_element_located(
(By.ID, 'tcaptcha_drag_thumb')))

# 鼠标按下
ActionChains(driver).click_and_hold(
on_element=slid_ing).perform()

time.sleep(0.2)
print('轨迹', track_list)

for track in track_list:
# 鼠标移动到距离当前位置(x,y)
ActionChains(driver).move_by_offset(
xoffset=track, yoffset=0).perform()
time.sleep(1)

# 第三步,释放鼠标
ActionChains(driver).release(
on_element=slid_ing).perform()
time.sleep(1)

# 识别图片
return True
else:
print('缺口图片捕获失败')
return False

@staticmethod
def save_img(bk_block):
'''
将验证码缺口图片保存到当前目录下
不要用中文路径, 会报错
'''

try:
# 发起请求且将响应结果赋值给img
img = requests.get(bk_block)
time.sleep(0.25)

# 写入图片
with open('./tx.jpeg', 'wb') as f:
f.write(img.content)

return True
except:
return False

@staticmethod
def get_pos():
'''
识别缺口
注意:网页上显示的图片为缩放图片, 缩放 50 % 所以识别坐标需要 0.5
contours 轮廓图像, 类型list
hierarchy 每条轮廓对应的属性, 属于 numpy.ndarray 类
cv.RETR_EXTERNAL 表示只检测外轮廓
cv.CHAIN_APPROX_SIMPLE 压缩水平、垂直、对角线方向的元素, 只保留该方向的终点坐标, 例如一个矩形轮廓只需4个点来保存轮廓信息
'''

# 读取并把图像赋值给img
image = cv.imread('./tx.jpeg')

# 高斯模糊,(5, 5)表示高斯矩阵的长与宽都是5,标准差取0
blurred = cv.GaussianBlur(image, (5, 5), 0)

# 边缘检测,200, 400表最小和最大阈值
canny = cv.Canny(blurred, 200, 400)

contours, _ = cv.findContours(
canny, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for _, contour in enumerate(contours):
# m 类型dict
m = cv.moments(contour)
if m['m00'] == 0:
cx = _ = 0
else:
cx, _ = m['m10'] / m['m00'], m['m01'] / m['m00']

# 判断轮廓面积和封闭轮廓的周长或曲线的长度
if 6000 < cv.contourArea(contour) < 8000 and 370 < cv.arcLength(contour, True) < 390:
if cx < 400:
continue

# 计算轮廓的垂直边界最小矩形 x,y是矩阵左上点的坐标 w,h是矩阵的宽和高
x, y, w, h = cv.boundingRect(contour) # 外接矩形

# 绘制一个矩形 (图像、矩形顶点、矩形对角线的另一个顶点、线条颜色、线条宽度)
cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
# cv.imshow('image', image) # 显示识别结果
print('【缺口识别】 {x}px'.format(x=x/2))
return x/2
return 0

@staticmethod
def get_track(distance):
'''
模拟轨迹
'''

# 初始位置
distance -= 26
# 初速度
v = 0
# 单位时间为0.2s来统计轨迹,轨迹即0.2内的位移
t = 0.2
# 位移/轨迹列表,列表内的一个元素代表0.2s的位移
tracks = []
# 当前的位移
current = 0
# 到达mid值开始减速
mid = distance * 7 / 8

# 先滑过一点,最后再反着滑动回来
distance += 10
# a = random.randint(1,3)
while current < distance:
if current < mid:
# 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细
a = random.randint(2, 4) # 加速运动
else:
a = -random.randint(3, 5) # 减速运动

# 初速度
v0 = v
# 0.2秒时间内的位移
s = v0 * t + 0.5 * a * (t ** 2)
# 当前的位置
current += s
# 添加到轨迹列表
tracks.append(round(s))

# 速度已经达到v,该速度作为下次的初速度
v = v0 + a * t

# 反着滑动到大概准确位置
for _ in range(4):
tracks.append(-random.randint(2, 3))
for _ in range(4):
tracks.append(-random.randint(1, 3))
return tracks


if __name__ == '__main__':

Tx = Tx()
Tx.login() # 登陆网站