- SignalDesk1 hr ago
叠甲 我感觉肯定会有很多人说,你只会说理论,也不放文件,什么什么的话,遇到这种我直接不回复 个人说实话,已经这个时代了,你都知道理论了,拿去给AI,我不相信出不来,所以那些需要假将饭喂到嘴里的"伸手党"一律略过 进入主题 滑块是怎么样的一个东西 拿阿里云的举例,也就是Zai那个使用的,应该是v3 能看到的那种验证,基本上是一张背景图,左边挂着一块拼图,底下一条能拖的滑条。 这个图怎么出现或者说怎么来的,是页面里会加载阿里云的一段 JavaScript( AliyunCaptcha.js )名称不固定,但是差不多就是一个js代码,。脚本自己去下载背景和拼图,然后你拖动的时候会计算你的差距,对了就给你一串密文。写代码的话其实就是:算出缺口在哪,然后模拟真人拖动滑块把拼图拖过去。 find_gap 、 piece_x 这些名称不是固定的,不懂扔给ai flowchart LR A[浏览器打开业务页的 https 地址] --> B[加载官方滑块脚本] B --> C[拿到背景图和拼图] C --> D[find_gap 算出原图上的 x] D --> E["target = x * 屏幕宽 / 原图宽"] E --> F[按住滑条 反复读拼图位置 拖到 target] F --> G[脚本把原文放进页面变量] G --> H[原文交给网站] 所以这就说明了,所有的那种http本地解码都是扯淡,除非是打码平台,那种是可以做到 还有一种就是你把所有的图片保存下来, 然后一个一个去对比写清楚 否则必须启动浏览器,你要加载资源,加载一些环境,补环境其实就是不想启动浏览器才操作的 (PS:可能我有说的不对的地方, 在评论区指出给大家解惑) 2. V2 和 V3 的区别 区别在缺口的图,不在函数名叫什么。 flowchart TB subgraph V2[V2 物品] A1[背景上能看见一块缺口或阴影] --> A2[拼图常常是整块不透明] A2 --> A3[提取边缘 或 拿整块去对一次] A3 --> A4[分数最高的位置就是洞] end subgraph V3[V3 洞被涂平] B1[洞被抹平 边上还留着原来的颜色] --> B2[拼图是带透明通道的异形] B2 --> B3[拿颜色去对 会贴到旁边那个真物体] B3 --> B4["三路: 边 xe、糊 xs、边减内部 xh"] B4 --> B5["最终 x = 选中的峰 - 左边透明宽度"] end gray = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (3, 3), 0) edge = cv2.Canny(blur, 80, 160) # piece_edge 是拼图外轮廓 score = cv2.matchTemplate(edge, piece_edge, cv2.TM_CCOEFF_NORMED) _, _, _, peak = cv2.minMaxLoc(score) x = peak[0] V3:边的峰和糊的峰通常差不超过 20 像素,投票会取它们的平均。 所以v2就是一个可以看到边缘,区别的拖动进去,但是v3就要注意像素值 还是使用 opencv-python 、 numpy ,OpenCV这就是py的模块 下面 id 一般都是是官方脚本生成的: 元素 id 背景 aliyunCaptcha-img 拼图 aliyunCaptcha-puzzle 滑条 aliyunCaptcha-sliding-slider 要点一下才出现的区域 aliyunCaptcha-captcha-body 换一张 aliyunCaptcha-btn-refresh 一般的流程 下面就是一般要注意的和走的流程 flowchart TD A[打开浏览器] --> B[用业务页地址打开 只替换这一次网页内容] B --> C[等官方脚本的初始化函数出现] C --> D[点一次开始验证] D --> E{滑条和两张图都出现了?} E -->|没有 连着几次| Z[这条网络不行 换一条] E -->|出现了| F[等两张图的地址连续两次不变] F --> G[按完整地址取出图片字节] G --> H[find_gap] H --> I{突出程度 >= 0.085?} I -->|是| J[只拖主峰] I -->|否| K[主峰 边减内部 边 糊 最多 3 个点] J --> L[drag_to] K --> L L --> M{松开后还差 3 像素以上?} M -->|是| B M -->|否| N{页面变量里有原文?} N -->|没有 且是超时| Z N -->|没有| B N -->|有| P{网站接受?} P -->|接受| Q[告诉脚本通过 结束] P -->|拒绝 未满 3 张| B P -->|拒绝 已满 3 张| Z 点「开始验证」之前,页面上往往只有一个按钮,没有拼图 代码 const REGION = "cn"; // 换成你的 const PREFIX = "你的前缀"; const SCENE = "你的场景号"; window.AliyunCaptchaConfig = { region: REGION, prefix: PREFIX }; window.__cvp = ""; // 脚本给出的原文,只写第一次 window.__cerr = ""; // 失败原因 window.__cvDecision = ""; // 你告诉脚本:网站认了还是不认 function readParam(p) { if (typeof p === "string") return p; return (p && (p.captchaVerifyParam || p.CaptchaVerifyParam)) || JSON.stringify(p || {}); } function boot() { initAliyunCaptcha({ SceneId: SCENE, mode: "embed", region: REGION, prefix: PREFIX, language: "cn", element: "#captcha-box", button: "#captcha-btn", success(p) { if (!window.__cvp) window.__cvp = readParam(p); }, captchaVerifyCallback(p) { window.__cvp = readParam(p); window.__cvDecision = ""; return new Promise((resolve) => { const timer = setInterval(() => { if (window.__cvDecision === "ok") { clearInterval(timer); resolve({ captchaResult: true, bizResult: true }); } else if (window.__cvDecision === "bad") { clearInterval(timer); resolve({ captchaResult: false, bizResult: false }); } }, 200); }); }, fail(e) { window.__cerr = JSON.stringify(e || {}); }, }); } 脚本用官方地址,加载完再 boot() : https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js 这段代码跑在浏览器页面里,用来把阿里云官方滑块脚本拉起来,并留三个窗口变量,让外面的程序能拿走验证原文。 三个变量 __cvp :脚本算出的验证原文。第一次成功回调只写一次,避免被后面的空值盖掉。 __cerr :脚本自己报失败时,把原因存下来。 __cvDecision :外面的程序写 "ok" 或 "bad" 。脚本在等这个字,才决定这次验证算过还是算不过。 readParam 官方回调有时直接给字符串,有时给对象。这个函数统一取出里面的 captchaVerifyParam (大小写两种都认)。取不到就整段转成字符串,保证 __cvp 里始终是一段原文。 boot 调用官方的 initAliyunCaptcha ,把滑块嵌进页面: SceneId 、 region 、 prefix 换成你自己的。 element 是拼图容器, button 是「开始验证」按钮。 success :脚本认为本地滑过了,若 __cvp 还空着,就把原文写进去。 captchaVerifyCallback :用户松手后脚本会进这里。先把原文写入 __cvp ,再每 200 毫秒看一次 __cvDecision 。外面的程序拿这段原文去问网站,网站返回接受就写 "ok" ,拒绝就写 "bad" 。脚本收到后才结束这次验证。 fail :脚本内部失败,原因进 __cerr 。 替换网页时只拦 document,其它请求放行: def fulfill(route): if route.request.resource_type == "document": route.fulfill(status=200, content_type="text/html; charset=utf-8", body=html) else: route.continue_() page.route("https://你的站点/**", fulfill) page.goto("https://你的站点/出现滑块的那一页", referer="https://你的来源页/") 背景图,滑块图:都加载完,地址不是空、不是同一张,而且同一对地址连续两次检查都没变。 const key = bgUrl + "|" + pieceUrl; if (lastKey !== key) { lastKey = key; stable = 0; return false; } stable += 1; return stable >= 2; 从页面读后面要用的数: const img = document.querySelector("#aliyunCaptcha-img"); const piece = document.querySelector("#aliyunCaptcha-puzzle"); const imgBox = img.getBoundingClientRect(); const pieceBox = piece.getBoundingClientRect(); const pieceX = pieceBox.x - imgBox.x; // 拼图现在离背景左边多远,屏幕像素 const pieceY = pieceBox.y - imgBox.y; const scale = imgBox.width / img.naturalWidth; // 两个宽度都要大于 1 图片字节用浏览器刚刚下载的那一份,键必须是带问号的完整地址。背景和拼图经常路径相同、只有问号后面不同。砍掉问号会拿到另一张图。 raw = cache[full_url] # 不要用 full_url.split("?")[0] img = cv2.imdecode(np.frombuffer(raw, np.uint8), cv2.IMREAD_UNCHANGED) IMREAD_UNCHANGED 用来留下透明通道。拼图变成普通彩色图后,后面的形状是空的。 缺口怎么算 flowchart TD IN[背景彩色图 + 拼图四通道图] --> BOX[透明值大于 20 的像素 框出外接矩形] BOX --> X0[记下矩形左边的空白宽度 x0] BOX --> YQ{实心高度 >= 拼图高度的 75%?} YQ -->|是| Y1["y = round(pieceY / scale)"] YQ -->|否| Y2["y = round(实心顶部 背景高 / 拼图高)"] Y1 --> BAND[只在这条横带里比较] Y2 --> BAND BAND --> XE[xe 轮廓对边缘 取最高] BAND --> XS[xs 拼图内部对细节图 取最低] BAND --> XH["xh = 边有多像 - 中间有多像 取最高"] XE --> VOTE[投票] XS --> VOTE XH --> VOTE VOTE --> OUT["x = 选中的峰 - x0"] 框出拼图形状 alpha = piece[:, :, 3] ys, xs = np.where(alpha > 20) y0, y1 = int(ys.min()), int(ys.max()) + 1 x0, x1 = int(xs.min()), int(xs.max()) + 1 # x0 是左边透明有多宽 crop = piece[y0:y1, x0:x1] mask = (crop[:, :, 3] > 20).astype(np.uint8) 255 h, w = mask.shape 拖的是整张拼图元素的左边缘,真正的形状在 x0 的右边。最后每个横坐标都要减一次 x0 ,不能减两次。 横带的上下位置: if (y1 - y0) >= piece.shape[0] 0.75: y = int(round(piece_y / scale)) else: y = int(round(y0 bg.shape[0] / piece.shape[0])) if y < 0: y = 0 if
- 情报分类:技术学习与提效
- 分类依据:内容涉及技术、AI、软件工具或工程实践
- 信息来源:服务器 / LINUX DO - 最新话题
- 发布时间:2026/9/24 11:25:04
- No replies yet