爬虫 - 无缺口滑块验证码

前言:

  • 在我们使用爬虫登录账号中常常会遇到各种验证码,如:图片验证码、滑块验证……
  • 其中滑块验证码又分有缺口滑块验证码和无缺口滑块验证码,无缺口滑块验证码只需要用户使用鼠标将滑块从左侧拖动到右侧即可。程序通过记录用户拖动滑块的轨迹,这一串的轨迹数据采用模式识别的手段就可以判断出这是否是真人在操作。
  • 滑块验证通常需要使滑块按照正常的加速度进行拖动,停靠在一个合适的位置,在使用 Selenium 时通常需要设置一个合适的滑动加速度来使自己伪装的更像人类用户而不是计算机。
  • 那么如何使用 Selenium 模拟这一过程呢?

无缺口滑块验证码

下面通过几个例子,来展示用 Selenium 解决这个问题的方法

示例 1:携程网注册

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
import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options

# 创建一个参数对象
options = Options()
# 设置界面最大化
options.add_argument('--start-maximized')


def main():
# 模拟创建一个浏览器对象,然后通过对象去操作浏览器
browser = webdriver.Chrome(ptions=options)
# 携程网注册
url = 'https://passport.ctrip.com/user/reg/home'
browser.get(url)
time.sleep(2)
# 点击”同意并继续“
browser.find_elements_by_class_name('reg_agree')[0].click()
time.sleep(2)

# 获取滑块位置
slider = browser.find_elements_by_class_name('cpt-drop-btn')[0]
# 获取滑动轨迹的宽度元素
ele = browser.find_elements_by_class_name('cpt-bg-bar')[0]
time.sleep(1)

# 移动滑块
ActionChains(browser).drag_and_drop_by_offset(slider, ele.size['width'], -slider.size['height']).perform()
time.sleep(2)

# 关闭浏览器(退出浏览器)
browser.quit()


if __name__ == '__main__':
main()

无需设置合适的滑动加速度

示例 2:企查查登录

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
import random
from time import sleep

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options

# 创建一个参数对象
options = Options()
# 设置界面最大化
options.add_argument('--start-maximized')
# 设置为开发者模式,防止被各大网站识别出来使用了Selenium
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 隐藏window.navigator.webdriver
options.add_argument('--disable-blink-features=AutomationControlled')


def get_move_track(distance):
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * (4 / 5) # 前4/5段加速 后1/5段减速
# 计算间隔
t = random.randint(2, 3) / 10
# 初速度
v = 0
while current < distance:
if current < mid:
# 加速度为+10
a = 10
else:
# 加速度为-3
a = -3
# 初速度v0
v0 = v
# 当前速度
v = v0 + a * t
# 移动距离
move = v0 * t + 1 / 2 * a * t * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
return track


def main():
# 模拟创建一个浏览器对象,然后通过对象去操作浏览器
browser = webdriver.Chrome(options=options)
# 企查查登录
url = 'https://www.qcc.com/user_login'
browser.get(url)
sleep(2)
# 点击密码登录
browser.find_element_by_id('normalLogin').click()
# 获取输入手机号框
phone = browser.find_element_by_id('nameNormal')
# 输入手机号
phone.send_keys('xxx')
sleep(1)
# 获取输入密码框
pwd = browser.find_element_by_id('pwdNormal')
# 输入密码
pwd.send_keys('xxx')
sleep(1)

# 获取滑块位置
slider = browser.find_element_by_id('nc_1_n1z')
# 获取滑块宽度元素
ele = browser.find_elements_by_class_name('nc-lang-cnt')[0]

# 设置滑块加速度,得到滑块运动轨迹
tracks = get_move_track(ele.size['width'])
# 按住滑块
ActionChains(browser).click_and_hold(slider).perform()
for x in tracks:
# 移动滑块,只有水平方向有运动,按轨迹移动
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
sleep(2)

# 点击立即登录
browser.find_elements_by_class_name('login-btn')[0].click()
sleep(2)

# 关闭浏览器(退出浏览器)
browser.quit()


if __name__ == '__main__':
main()

⚠️注意事项:需要设置合适的滑动加速度;如果发现移动滑块动作不流畅,解决方案

示例 3:蓝奏云登录

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
import random
from time import sleep

from selenium import webdriver
from selenium.webdriver import ActionChains


def get_move_track(distance):
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * (4 / 5) # 前4/5段加速 后1/5段减速
# 计算间隔
t = random.randint(2, 3) / 10
# 初速度
v = 0
while current < distance:
if current < mid:
# 加速度为+10
a = 10
else:
# 加速度为-3
a = -3
# 初速度v0
v0 = v
# 当前速度
v = v0 + a * t
# 移动距离
move = v0 * t + 1 / 2 * a * t * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
return track


def main():
# 模拟创建一个浏览器对象,然后通过对象去操作浏览器
browser = webdriver.Firefox()
# 蓝奏云
url = 'https://www.lanzou.com/u'
browser.get(url)
# 设置页面最大化
browser.maximize_window()
sleep(2)
# 执行js代码,修改selenium标识,绕过网站对selenium的检测
browser.execute_script('Object.defineProperties(navigator,{webdriver:{get:()=>false}})')
# 查找用户名输入框
username = browser.find_element_by_id('username')
# 输入用户名
username.send_keys('xxx')
sleep(1)
# 查找密码输入框
pwd = browser.find_elements_by_class_name('pwd')[0]
# 输入密码
pwd.send_keys('xxx')
sleep(1)

# 获取滑块位置
slider = browser.find_element_by_id('nc_1_n1z')
# 获取滑块框位置
ele = browser.find_elements_by_class_name('nc-lang-cnt')[0]

# 设置滑块加速度,得到滑块运动轨迹
tracks = get_move_track(ele.size['width'])
# 按住滑块
ActionChains(browser).click_and_hold(slider).perform()
for x in tracks:
# 移动滑块,只有水平方向有运动,按轨迹移动
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
sleep(2)

# 点击登录
browser.find_element_by_id('s3').click()
sleep(3)

# 关闭浏览器(退出浏览器)
browser.quit()


if __name__ == '__main__':
main()

需要设置合适的滑动加速度

示例 4:淘宝注册

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
import time
import random

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options

# 创建一个参数对象,用来控制chrome以无界面模式打开
options = Options()
# 设置界面最大化
options.add_argument('--start-maximized')
# 设置为开发者模式,防止被各大网站识别出来使用了Selenium
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 隐藏window.navigator.webdriver
options.add_argument('--disable-blink-features=AutomationControlled')


def move_slider(browser):
# 找到滑块
slider = browser.find_element_by_id('nc_1_n1z')
# 找到滑块轨迹框
ele = browser.find_elements_by_class_name('nc-lang-cnt')[0]
time.sleep(1)
ActionChains(browser).drag_and_drop_by_offset(slider, ele.size['width'], -slider.size['height']).perform()
time.sleep(1)


def main():
# 模拟创建一个浏览器对象,然后通过对象去操作浏览器
browser = webdriver.Chrome(options=options)
url = 'https://reg.taobao.com/member/reg/fill_mobile.htm'
browser.get(url)
# time.sleep(2)
time.sleep(2)

# 点击“同意协议”按钮
browser.find_element_by_id('J_AgreementBtn').click()
# 获取手机号输入框
phone = browser.find_element_by_id('J_Mobile')
# 输入手机号
phone.send_keys('xxxx')

# 移动滑块
move_slider(browser)

# 点击下一步
browser.find_element_by_id('J_BtnMobileForm').click()
time.sleep(3)

# 关闭浏览器(退出浏览器)
browser.quit()


if __name__ == '__main__':
main()

注意:不要设置合适的滑动加速度,否则会失败!

示例 5:淘宝登录

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
# 参考链接:https://blog.csdn.net/xlw_like/article/details/107939072
import random
from time import sleep

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options


option = Options()
option.add_argument('--start-maximized')
# Chrome79版本以上需要加上该句
option.add_argument('--disable-blink-features=AutomationControlled')
option.add_experimental_option(
'excludeSwitches', ['enable-automation', 'enable-logging'])


def get_move_track(distance):
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * (3 / 5) # 前4/5段加速 后1/5段减速
# 计算间隔
t = random.randint(2, 3) / 10
# 初速度
v = 0
while current < distance:
if current < mid:
# 加速度为+10
a = 10
else:
# 加速度为-3
a = -3
# 初速度v0
v0 = v
# 当前速度
v = v0 + a * t
# 移动距离
move = v0 * t + 1 / 2 * a * t * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move))
return track


def move_slider(browser):
# 切换iframe
browser.switch_to.frame(browser.find_element_by_id('baxia-dialog-content'))

slider = browser.find_element_by_id('nc_1_n1z')
ele = browser.find_elements_by_class_name('nc-lang-cnt')[0]
# 设置滑块加速度,得到滑块运动轨迹
tracks = get_move_track(ele.size['width'])
sleep(1)
print('*' * 60)
print(slider.size['width'])
print(slider.size['height'])
print('*' * 60)

# 移动滑块
# ActionChains(browser).drag_and_drop_by_offset(
# slider, ele.size['width']-slider.size['width'], -slider.size['height']).perform()

# 按住滑块
ActionChains(browser).click_and_hold(slider).perform()
for x in tracks:
# 移动滑块,只有水平方向有运动,按轨迹移动
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()


def main():
browser = webdriver.Chrome(options=option)
url = 'https://www.taobao.com/'
browser.get(url)
sleep(1)
browser.find_elements_by_class_name('h')[0].click()
sleep(1)
browser.find_element_by_id('fm-login-id').send_keys('username')
sleep(0.5)
browser.find_element_by_id(
'fm-login-password').send_keys('password')
sleep(0.5)
browser.find_elements_by_class_name(
'fm-submit')[0].click()
sleep(0.5)

move_slider(browser)
sleep(5)

browser.quit()


if __name__ == '__main__':
main()

  1. 需要修改 chromedriver.exe
  2. Chrome79 版本以需要添加 selenium 参数
    1
    options.add_argument('--disable-blink-features=AutomationControlled')
  3. 滑块加速度设置与否都可以

注意事项

  1. 如果发现移动滑块动作不流畅,可以修改 Selenium 源码,路径如下:

    1
    C:\Programs\Python\Python36\Lib\site-packages\selenium\webdriver\common\actions\pointer_input.py

    将其中的 DEFAULT_MOVE_DURATION = 250 的值修改到 50 左右,参考链接,如图:

  2. 开启 Chorme 开发者模式,关闭自动测试状态

    1
    options.add_experimental_option('excludeSwitches', ['enable-automation'])

    加上这个代码会关闭 “Chrome 正受到自动测试软件的控制” 的显示

  3. 使用 Selenium 时会被部分网站监测到,这时你在 Selenium 打开的浏览器下的按 F12 进入检查,然后在 Console 下输入 window.navigator.webdriver 会发现结果为 true,如图:

    但是当你人为的打开浏览器时,同样的输入 window.navigator.webdriver 这时会发现结果为 undefined 或者 false,如图:

    这时就需要增加一个配置参数,修改 Selenium 标识,绕过网站对 Selenium 的检测

    1
    options.add_argument('--disable-blink-features=AutomationControlled')
  4. 如果以上 3 点还不行的话,可以尝试修改 chromedriver 中的变量,注意修改后的字符串长度不能变

参考链接

  1. 淘宝爬虫之自动登录
  2. 淘宝爬虫之登陆验证(二)
  3. 使用 Python Selenium 实现拖拽动作时动作不流畅
  4. Chome 88 如何正确隐藏 webdriver?
  5. 修改 chromedriver 中的变量 $cdc_...,注意修改后的字符串长度不能变