登录同花顺,发现它的滑动验证码比豆瓣简单不少,就拿之前豆瓣那套过滑动验证码的python代码改了一下,前后花了半个多小时就搞定了。

改动的地方
1. 图片获取
某瓣的图片 URL 藏在 style 属性里,要用正则去抠。同某顺直接给 src,省事多了。
# 同某顺:直接取src
bg_url = driver.find_element(By.ID, 'slicaptcha-img').get_attribute("src")
slider_url = driver.find_element(By.ID, 'slicaptcha-block').get_attribute("src")
2. 滑块定位
某瓣用 XPath 一层层找,同某顺直接给 ID,省心。
block = driver.find_element(By.ID, 'slider')
3. 尺寸常量
同某顺背景图 340×195,显示尺寸 309×177,把 OpenCV 计算里的常量替换掉就行,算法不用动。

完整代码
完整代码贴在下面,核心逻辑全部复用,改动不超过 30 行。
import re
import requests
import cv2
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.chrome.options import Options
def download_image(url, path):
headers = {"User-Agent": "Mozilla/5.0", "Referer": "https://www.douban.com/"}
resp = requests.get(url, headers=headers)
with open(path, "wb") as f:
f.write(resp.content)
print(f"已下载: {path}")
def get_slide_distance(bg_path, slider_path, offset=0):
"""
计算滑块滑动距离
bg_path: 背景图路径 (340x195)
slider_path: 滑块图路径 (55x55)
offset: 偏移调整值
"""
# 尺寸常量
bg_width = 340
bg_height = 195
slider_size = 55
# 显示尺寸比例 (CSS显示尺寸 / 实际尺寸)
display_bg_width = 309
display_bg_height = 177.22
display_slider_size = 49.9853
# 缩放比例
scale = display_bg_width / bg_width # 309/340 ≈ 0.9088
# 读取图片
bg = cv2.imread(bg_path)
slider = cv2.imread(slider_path, cv2.IMREAD_UNCHANGED)
# 处理滑块 - 取alpha通道作为灰度图
if slider.shape[2] == 4:
slider_gray = slider[:, :, 3] # alpha通道
else:
slider_gray = cv2.cvtColor(slider, cv2.COLOR_BGR2GRAY)
# 背景转灰度
bg_gray = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
# 边缘检测
bg_edges = cv2.Canny(bg_gray, 100, 200)
slider_edges = cv2.Canny(slider_gray, 100, 200)
# 模板匹配
result = cv2.matchTemplate(bg_edges, slider_edges, cv2.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(result)
# 缺口在原图上的X坐标 (缺口左上角 + 滑块宽度/2 = 缺口中心)
center_x_original = max_loc[0] + slider_size // 2 + offset
# 换算到显示坐标的left值
left_display = center_x_original * scale - display_slider_size / 2
return left_display
def extract_url_from_style(style):
match = re.search(r'url("([^"]+)")', style)
return match.group(1) if match else None
def slide_simple(distance):
"""直接滑到底"""
return [distance]
def get_tracks(distance):
"""
生成仿人滑动的轨迹数组
原理:模拟人的拖动习惯 —— 先加速后减速
:param distance: 需要滑动的总距离(像素)
:return: 轨迹数组,每个元素是每次移动的偏移量(像素)
"""
tracks = [] # 存储每一步移动的距离
current = 0 # 当前已经移动的总距离
mid = distance * 0.6 # 加速阶段的终点(前60%加速,后40%减速)
t = 0.05 # 时间间隔(秒),越小轨迹越平滑
v = 0 # 初始速度(像素/秒)
# 循环生成轨迹,直到走完总距离
while current < distance:
# 根据当前位置决定加速度
# 前60%:加速(a=20),模拟人启动时加快速度
# 后40%:减速(a=-3),模拟人快到目标时减速
a = 20 if current < mid else -3
# 匀变速运动位移公式:s = v0 * t + 0.5 * a * t^2
s = v * t + 0.5 * a * t * t
# 累加位移
current += s
# 将本次移动距离取整后加入轨迹(像素必须是整数)
tracks.append(round(s))
# 更新速度:v = v0 + a * t
v += a * t
# 修正误差:因为取整可能导致总距离不等于目标距离
total = sum(tracks)
if total > distance:
# 如果超了,从最后一步减去多余的部分
tracks[-1] -= total - distance
elif total < distance:
# 如果不够,补一步剩余距离
tracks.append(distance - total)
return tracks
def slide(driver):
# 切换iframe
time.sleep(1)
n = 1
while n <= 5:
try:
# 获取背景图URL
bg_element = driver.find_element(By.ID, 'slicaptcha-img')
bg_url = bg_element.get_attribute("src")
# 获取滑块大图元素(小方块那个)
elem = driver.find_element(By.ID, 'slicaptcha-block')
big_url = elem.get_attribute("src")
print(f"背景图URL: {bg_url[:80]}...")
print(f"滑块图URL: {big_url[:80]}...")
# 下载图片
download_image(bg_url, "bg.png")
download_image(big_url, "slider_big.png")
# 计算距离
distance = get_slide_distance("bg.png", "slider_big.png")
print(f"需要滑动: {distance:.2f}px")
tracks = slide_simple(distance)
# 找滑块元素
block = driver.find_element(By.ID, 'slider')
# 1. 按下滑块
ActionChains(driver).click_and_hold(block).perform()
time.sleep(0.1)
# 2. 按轨迹滑动
for track in tracks:
ActionChains(driver).move_by_offset(xoffset=track, yoffset=random.uniform(-1, 1)).perform()
time.sleep(random.uniform(0.01, 0.02))
# 3. 松开滑块
ActionChains(driver).release().perform()
time.sleep(2)
# 4. 检查验证是否成功
# 如果验证成功,instructionText 会消失或变成其他状态
print(f"第{n}次验证成功")
break
n += 1
print(f"第{n - 1}次验证失败,重试...")
print(f"本次将会滑动:{distance}")
except Exception as e:
print(e)
print(f"第{n}次验证成功")
break
def main():
url = "https://upass.10jqka.com.cn/login"
options = Options()
options.add_argument('--disable-blink-features=AutomationControlled')
driver = webdriver.Chrome(options=options)
driver.get(url)
time.sleep(2)
# 点击密码登录
driver.find_element(By.ID, "to_account_login").click()
time.sleep(2)
# 输入账号密码
driver.find_element(By.ID, "uname").send_keys("color")
driver.find_element(By.ID, "passwd").send_keys("wewewrqr")
time.sleep(1)
# 点击登录按钮
driver.find_element(By.CLASS_NAME, "submit_btn").click()
time.sleep(3)
# # 滑动验证码(内部会自动下载图片、计算距离、滑动)
slide(driver) # distance参数会被覆盖,随便传个0
time.sleep(3)
print("完成")
driver.quit()
if __name__ == '__main__':
main()
总结
核心改动就三处:图片获取方式、滑块定位、尺寸常量。其他代码全部复用,验证成功率 90% 以上。如果你之前跑过某瓣那套滑动验证码得到代码,这个十分钟内就能跑起来。
本文仅用于技术学习,请勿用于非法用途。