python + selenium 爬虫模拟登录破解无原图滑动验证码

爬虫模拟登录破解无原图滑动验证码: https://cloud.tencent.com/developer/article/1711623

需求:部分网站在频繁的使用之后,会弹出滑块验证码(极验)。有别于过去,现在的原图并不会出现,因此较过去的思路转变为以下:

1、截取带缺口的图片;

2、寻找原图,并截图;

3、比较两张图片,寻找到缺口位置距离;

4、计算运动过程,并驱动浏览器移动滑块。

参考链接基本提供了1-3步的实现,第4步存在被识别为机器操作,需要进行更新(修正以后,目前成功率应该有50%以上),记录如下。

一、启动浏览器,配置option,以防被识别为自动化。

代码语言:javascript
复制
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver import ActionChains
from PIL import Image
import numpy as np
import pandas as pd
import time

配置浏览器

options = webdriver.ChromeOptions()

此步骤很重要,设置为开发者模式,防止被各大网站识别出来使用了Selenium

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

二、获取两张图片进行比较,通过网页分析发现。该标签,当display=block,opacity=1时,显示的为原图。

代码语言:javascript
复制
# 获取第一个图
img_name1 = r'../data/png/captcha2.png'
img_name2 = r'../data/png/captcha2_src.png'
img = wait.until(EC.presence_of_element_located((By.XPATH,'//canvas[@class="geetest_canvas_slice geetest_absolute"]')))
img.screenshot(img_name1)

获取原图

browser.execute_script("var x=document.getElementsByClassName('geetest_canvas_fullbg geetest_fade geetest_absolute')[0];"
"x.style.display='block';"
"x.style.opacity=1"
)
img = wait.until(EC.presence_of_element_located((By.XPATH,'//canvas[@class="geetest_canvas_slice geetest_absolute"]')))
img.screenshot(img_name2)

变回来

browser.execute_script("var x=document.getElementsByClassName('geetest_canvas_fullbg geetest_fade geetest_absolute')[0];"
"x.style.display='none';"
"x.style.opacity=0"
)

三、计算两张图片差异,通过rgb一定的缺值进行判断缺口。

代码语言:javascript
复制
# 如何能找到滑块的位置
def get_distance(img1,img2):
start_x=60#初始X
threhold=60#阈值
for x in range(start_x,img1.size[0]):
for y in range(img1.size[1]):
rgb1=img1.load()[x,y]
rgb2=img2.load()[x,y]
res1=abs(rgb1[0]-rgb2[0])
res2=abs(rgb1[1]-rgb2[1])
res3=abs(rgb1[2]-rgb2[2])
if not (res1<threhold and res2<threhold and res3<threhold):
return x-7#测试后-7可以提高成功率

四、计算模拟移动,根据自身的挪动特点,我一般的移动操作分为三个阶段(可根据自身特点进行设计)。

1:点击滑块以后,缓慢移动一下。约移动3次。

2:快速移动到缺口附件。大概0.3s。

3:到了缺口附近以后,缓慢靠近,然后在缺口处停留大概0.5秒以后释放。

代码语言:javascript
复制
# 将整个过程分为3段,总时长大概是1.2-1.6秒

第一段是启动阶段,第一次点击的时候,总会比较拘谨,慢速启动,大概消费t2(0.3)秒,s2为分段数

第二段则是很快到达缺口附近,大概剩余d3(5)距离处

第三段是缓慢对接,最后停在上面0.5秒

def get_tracks(distance,s1 = 2,t2 = 0.3,s2 = 3,d3 = 5):
# 计算d1移动过程
dtemp = 0
track1 = []
for i in range(s1):
t = np.random.randint(low = 1, high=3)
track1.append(t)
dtemp = dtemp + t

# 计算d2距离
distance =distance - dtemp - d3

track2 = []
a = 2 * distance /(t2 ** 2)
dtemp = 0
for i in range(s2):
    # 计算每段行走
    ttemp1 = t2/s2 * (i + 1)
    ttemp2 = t2/s2 * i
    t = int(0.5 * a * (ttemp1 ** 2) - 0.5 * a * (ttemp2 ** 2) )
    track2.append(t)
    dtemp = dtemp + t
    
#由于取整了,可以存在一点误差,调整d3
d3 = distance - dtemp + d3
print(d3)
track3 = []
s = 0
while s &lt; d3:
    t = np.random.randint(low = 1, high=3)
    if s + t &gt;= d3:
        track3.append(d3 - s)
        break
    else:
        s = s + t
        track3.append(t)    
return {&#34;track1&#34;: track1, &#39;track2&#39;: track2, &#39;track3&#39;: track3}</code></pre></div></div><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">%%time

计算路径,不行,这个会被识别为计算机

#获得滑块元素
geetest_slider_button=browser.find_element_by_class_name('geetest_slider_button')
#获得距离
img1 = Image.open(img_name1)
img2 = Image.open(img_name2)
distance=get_distance(img1,img2)
#获得步数
tracks_dic=get_tracks(distance,s1 = 2,t2 = 0.2,s2 = 3,d3 = 5)
#点击并按住 ActionChains(driver).click_and_hold(geetest_slider_button).perform()
track1=tracks_dic['track1']
track2=tracks_dic['track2']
track3=tracks_dic['track3']

最后,遍历执行动作链。

代码语言:javascript
复制
%%time

执行

ActionChains(browser).click_and_hold(geetest_slider_button).perform()

执行第一步

for t in track1:
ActionChains(browser).move_by_offset(xoffset=t,yoffset=0).perform()
#停顿一会,更像人
time.sleep(0.2)
for t in track2:
ActionChains(browser).move_by_offset(xoffset=t,yoffset=0).perform()
for t in track3:
ActionChains(browser).move_by_offset(xoffset=t,yoffset=0).perform()
time.sleep(0.5)

松开

ActionChains(browser).release(geetest_slider_button).perform()