验证码破解

1,630 阅读8分钟

验证码的作用

验证码的主要目的是防⽌恶意机器⼈或⾃动化程序对⽹站进⾏滥⽤、欺诈或恶意攻击。它通过要求用户识别和输⼊验证码来验证⽤⼾的“⼈类⾝份”,因为机器⽆法像⼈类⼀样正确地理解和回答验证码 中的内容。

常见的验证码

  1. 数字验证码:这类验证码是最为常⻅的,通常由4-6个数字组成,⽤⼾需要通过填写这些数字来完 成某些操作,如注册账⼾或登录。
  2. 字母验证码:字母验证码也较为常⻅,常出现在短信验证和Web⽹站的注册表单中。它的展现形式是在验证码输⼊框附近的图⽚中展⽰字⺟,⽤⼾需要识别出特定的字⺟组合来获得验证码。
  3. 图⽚验证码:图⽚验证码是⼀种较复杂且难以破解的验证码形式。它通常展示⼀组图片,用户需要根据系统的提示从多个选项中选择正确的图片。这种验证码增加了用户的认知负担,从而提高了安全性。
  4. ⽂字验证码:⽂字验证码同样常⻅于Web⽹站的应⽤,其表现形式是在验证码输⼊框附近展示⼀段⽂字,用户需要识别并输⼊其中的⽂字。
  5. 基于知识的验证码:这种验证码通常包含⼀些普通⺠众熟知的问题,如数学题或日期问题,以此来测试用户的知识和常识,⽽不是简单的图像识别能力。
  6. 动作验证码:动作验证码是通过让用户执⾏⼀定的动作来进行身份验证,这种⽅式的用户体验较好,因为它们不需要用户具备专门的知识。

验证码破解

通常情况下我们是不需要进⾏验证码破解的,因为可以直接登录进⼊网站,获取⽹站的cookie进⾏请求,可以模拟成已经登录的状态。

但是在后续可能需要重新登录认证,因为某些⽹站的设置⽤⼾信息是有期限的。我们需要重新进行手动复制,比较麻烦,这个时候我们就需要破解验证码,进行登录来获取新的登录cookie。

破解验证码我们通常使⽤⾃动化⼯具selenium以及验证码识别⼯具或者第三⽅识别平台(俗称打码平台)

模块准备

#-U 下载最新或者更新到最新版本
pip install -U selenium

# 图⽚⽂字识别提取模块
pip install -U ddddocr
# 使⽤ ddddocr 模块,可以将图⽚中的⽂字提取出来,常⻅的应⽤包括⾝份证识别、⻋牌识别、发票 识别等

如果想要快速的下载,在后面加上镜像源即可

验证selenium和chromedriver是否准备好

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

service= Service(executable_path='chromedriver.exe')
driver=webdriver.Chrome(service=service)

driver.set_window_size(1100,850)  #设置打开的窗口的大小
driver.get('https://www.baidu.com/?tn=88093251_87_hao_pg')  #选择自动化操作页面

input() #打开之后就不会关闭

字母数字验证码识别--使用模块

古诗词文网so.gushiwen.cn/user/login.…

#找到验证码
#保存验证码
#识别验证码
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from ddddocr import DdddOcr

driver=webdriver.Chrome(service=Service('../chromedriver.exe'))  #选择使用的浏览器以及驱动的位置
url='https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
driver.get(url)

# 输入账号、密码
driver.find_element(By.XPATH,'//input[@name="email"]').send_keys('xxxxxxxx')
driver.find_element(By.XPATH,'//input[@name="pwd"]').send_keys('xxxxxxxx')

# 获取、保存验证码
imgCode=driver.find_element(By.ID,'imgCode')  #找到验证码位置
imgCode.screenshot('img.png')    #保存验证码
# 对验证码进行识别
with open('img.png',mode='rb') as file:   #读取数据进行验证码的识别
    yzm=DdddOcr(show_ad=False).classification(file.read())  #show_ad=False:让DdddOcr中的自带输出内容关闭,classification验证码识别的内容进行识别
# 输入验证码
driver.find_element(By.XPATH,'//input[@id="code"]').send_keys(yzm)
# 点击登录
driver.find_element(By.XPATH,'//input[@id="denglu"]').click()

input()

使用ddddocr模块进行简单的识别还是比较可靠的,比如字母,数字

字母数字验证码识别--第三方打码平台

第三方打码平台

# 超级鹰 # 注册⼀个超级鹰账号 并且登录(http://www.chaojiying.com/api-14.html) 
# 充值 选择⾃定义⾦额 1:100 
# 在开放⽂档中下载python⽰例 
# 在⽤⼾中⼼的软件ID中创建⼀个软件ID
from user import username,password
chaojiying = Chaojiying_Client(username, password, '958015')   #用户中心>>软件ID 生成一个替换 96001
im = open('a.jpg', 'rb').read()                                #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 1004))                            #1902 验证码类型  官方网站>>价格体系 3.4+版print 后要加()
#print chaojiying.PostPic(base64_str, 1902)                    #此处为传入 base64代码

滑块验证码流程

目标网站:

网易易盾:dun.163.com/trial/sense

1.找到滑块验证码的位置

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait  #显示等待,需要的元素出来就继续执行下面的代码
from selenium.webdriver.support.expected_conditions import presence_of_element_located as pe  #presence_of_element_located用于判断指定元素是否存在于网页之中,需要传入一个元组

driver=webdriver.Chrome(service=Service('../chromedriver.exe'))
url='https://dun.163.com/trial/sense'  #网易易盾
driver.get(url)
driver.set_window_size(1100,800)

#找到点击的位置
wait=WebDriverWait(driver,20)  #设置等待
#在设置的等待时间内判断页面中是否有这个元素
# wait.until(pe((By.XPATH, '//li[@class="tcapt-tabs__tab sactive"]'))).click()
wait.until(pe((By.XPATH, '//li[@captcha-type="jigsaw"]'))).click()  #pe中需要传入一个元组

driver.execute_script(f'window.scrollTo(0,150)')   #向下滑动

2.通过点击获得验证码

wait.until(pe((By.XPATH, '//div[@class="yidun_intelli-icon"]'))).click()  #pe中需要传入一个元组
# 获得验证码并保存下来
wait.until(pe((By.XPATH, '//div[@class="yidun_bgimg"]'))).screenshot('yzm.png')  #保存验证码
input()

3.识别验证距离

通过第三方打码平台,如:


#识别验证距离(这里利用的图灵)
import base64
import json
import requests
from user import username,password
# 调用图灵平台接口
def b64_api(username, password, img_path, ID):
    with open(img_path, 'rb') as f:
        b64_data = base64.b64encode(f.read())
    b64 = b64_data.decode()
    data = {"username": username, "password": password, "ID": ID, "b64": b64, "version": "3.1.1"}
    data_json = json.dumps(data)
    result = json.loads(requests.post("http://www.fdyscloud.com.cn/tuling/predict", data=data_json).text)
    return result
#调用平台接口识别距离
result = b64_api(username=username, password=password, img_path='yzm.png', ID="78915616")
# { "code": 1, "message": "", "data": { "滑块": { "X坐标值": 28, "Y坐标值": 65 }, "缺口": { "X坐标值": 139, "Y坐标值": 66 } } }
ranges = result['data']['缺口']['X坐标值']-result['data']['滑块']['X坐标值']
print(ranges)

4.计算运动轨迹

# 运算规矩就是给传⼊⼀个距离,设置每次移动的步⻓,⽽不是直接移动到结果位置。
def get_move_track(gap):
    track = [] # 移动轨迹
    current = 0 # 当前位移
    # 减速阈值
    mid = gap * 4 / 5 # 前4/5段加速 后1/5段减速
    t = 0.2 # 计算间隔
    v = 0 # 初速度
    while current < gap:
        if current < mid:
            a = 5 # 加速度为+5
        else:
            a = -5 # 加速度为-5
        v0 = v # 初速度v0
        v = v0 + a * t # 当前速度
        move = v0 * t + 1 / 2 * a * t * t # 移动距离
        current += move # 当前位移
        track.append(round(move)) # 加⼊轨迹
    return track

range_list=get_move_track(ranges)

5.滑动验证码到指定位置

# 5.滑动验证码到指定位置
from selenium.webdriver.common.action_chains import ActionChains
# yzm_img=wait.until(pe((By.XPATH,'//div[@class="yidun_slider yidun_slider--hover"]')))
yzm_img=wait.until(pe((By.XPATH,'//img[@class="yidun_jigsaw"]')))

action = ActionChains(driver)  #创建一个行为链,只有行为链中才有这些操作
action.click_and_hold(yzm_img) #按住图片不放
for i in range_list:
    action.move_by_offset(i,0) #滑动的x轴距离和y轴距离
action.perform()  #执行按住和滑动
action.release().perform() #执行松开
print('执行完毕')

点触验证码-b站登录

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait  #显示等待,找到了就继续执行下面的代码
from selenium.webdriver.support.expected_conditions import presence_of_element_located as pe  #pe模块用于判断需找的元素是否存在,需要传入一个元组
from user import username,password
from time import sleep
from PIL import Image
from PIL.Image import open as pli_open,Resampling  #截图
import base64
import json
import requests
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep


driver=webdriver.Chrome(service=Service('../chromedriver.exe'))
driver.get('https://passport.bilibili.com/login')
driver.set_window_size(1100,800)  #设置窗口大小

# 1.显示出验证码
wait=WebDriverWait(driver,20)
wait.until(pe((By.XPATH,'//div[@class="tab__form"]/div[1]/input'))).send_keys(username)
wait.until(pe((By.XPATH,'//div[@class="tab__form"]/div[3]/input'))).send_keys(password)
# wait.until(pe((By.XPATH,'//div[@class="btn_primary disabled"]'))).click()
wait.until(pe((By.XPATH,'//div[@class="btn_primary "]'))).click()

# 2.获取验证码
wait.until(pe((By.XPATH,'//img[@class="geetest_item_img"]')))
sleep(2)
driver.save_screenshot('html.png')  #将验证码截图下来
# 截取验证码
image=Image.open('html.png')
image.crop((380,129,686,484)).save('yzm.png')  #通过左上角坐标(477,219)和右下角(860,656)的坐标进行截图
# 拉升验证码,让验证码⼤⼩和⽹⻚⼀样,获取的数据才是⼀样的不然分辨率不⼀样
a = pli_open('yzm.png')
a.resize((306,306+48),Resampling.LANCZOS).save('yzm1.png')
# { "code": 1, "message": "", "data": { "顺序1": { "X坐标值": 240, "Y坐标值": 281 }, "顺序2": { "X坐标值": 159, "Y坐标值": 195 }, "顺序3": { "X坐标值": 59, "Y坐标值": 187 }, "顺序4": { "X坐标值": 257, "Y坐标值": 145 } } }
def b64_api(username, password, img_path, ID):
    with open(img_path, 'rb') as f:
        b64_data = base64.b64encode(f.read())
    b64 = b64_data.decode()
    data = {"username": username, "password": password, "ID": ID, "b64": b64, "version": "3.1.1"}
    data_json = json.dumps(data)
    result = json.loads(requests.post("http://www.fdyscloud.com.cn/tuling/predict", data=data_json).text)
    return result

# 识别得到结构s
result = b64_api(username=username, password=password, img_path='yzm1.png', ID="08272733")
print(result)

# 执⾏点击操作
text = wait.until(pe((By.XPATH, '//img[@class="geetest_item_img"]')))

for k, v in result['data'].items():
    print(v['X坐标值'] - 150, v['Y坐标值'] - 150 - 48)
    (ActionChains(driver).move_to_element(text)
    # 图⽚⾼是305 text 默认是中⼼,我们需要回到圆点,需要对x,y都进⾏调整
    # -150是图⽚的⾼,-48 是图⽚上的哪⾏字的⾼度
    .move_by_offset(v['X坐标值'] - 150, v['Y坐标值'] - 150 - 48)
    .click().perform())
    sleep(1)
print('执行完毕')
input()