爬虫模拟登录破解无原图滑动验证码: https://www.cnblogs.com/98WDJ/p/11050559.html

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

1、截取带缺口的图片;

2、寻找原图,并截图;

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

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

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

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

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时,显示的为原图。

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

# 获取第一个图
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一定的缺值进行判断缺口。

# 如何能找到滑块的位置
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秒以后释放。

# 将整个过程分为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 < d3:
        t = np.random.randint(low = 1, high=3)
        if s + t >= d3:
            track3.append(d3 - s)
            break
        else:
            s = s + t
            track3.append(t)    
    return {"track1": track1, 'track2': track2, 'track3': track3}
%%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']

最后,遍历执行动作链。

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