前言
本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
逆向地址
网址 :aHR0cHM6Ly9ndDQuZ2VldGVzdC5jb20v
接口:
captcha_id接口:https://gt4.geetest.com/assets/index.xxxxxxxx.js- 请求验证码接口(load接口):
https://gcaptcha4.geetest.com/load - 验证验证码接口(verify接口):
https://gcaptcha4.geetest.com/verify
逆向分析
1.三个接口
首先分析一下这三个接口,看看请求和响应的参数是否动态
captcha_id接口 :https://gt4.geetest.com/assets/index.604047e9.js
这个接口返回的是一个js文件,文件里有我们需要的caprcha_id,这个参数需要传递给下面两个接口(请求验证码接口和验证验证码接口)当作请求参数,如下图所示:

请求验证码接口(load接口) :https://gcaptcha4.geetest.com/load
请求参数
callback:geetest_13位的时间戳captcha_id:由captcha_id接口返回challenge:uuid格式的数据client_type:webrisk_type:match,消消乐验证码lang:zho
响应数据(JSONP格式),有以下几个参数需要注意:
ques:消消乐矩阵(3x3),每个数字代表一种图片,将三个相同数字换到同一列或同一行即可通过pow_detail:里面的4个参数均作为参数用于生成加密参数wversionbitshashfuncdatetime
lot_number:传递给下一个接口当请求参数,并且传参用于加密参数wpayload:传递给下一个接口当请求参数process_token:传递给下一个接口当请求参数
验证验证码接口(verify接口) :https://gcaptcha4.geetest.com/verify
请求参数
callback:geetest_13位的时间戳captcha_id:由captcha_id接口返回client_type:webrisk_type:match,消消乐验证码lot_number:由请求验证码接口返回payload:由请求验证码接口返回process_token:由请求验证码接口返回payload_protocol:1pt:1,作为参数用于生成加密参数ww:加密参数(1504位),接下去要分析的重点
2.加密参数w
加密参数w就不分析了,和上一篇文章的一样,只不过传入的参数从移动的距离setLeft变成了点击两块拼图的坐标userresponse。
点击传送到上一篇文章
3.加密参数userresponse

1.验证码规则
原始数据是一个3 行 3 列的矩阵,我们的目标:
找到唯一一对需要交换的元素,交换后让一行 / 一列全部相同。
2.核心规律
所有消消乐题目,都满足一个固定特征:
✅ 存在一行 / 一列:2 个数字相同,1 个数字不同
我们只需要找到这组数据,再判断:
-
不同数字的上方 / 下方(列场景)
-
不同数字的左侧 / 右侧(行场景)
是否是目标数字,即可确定交换坐标。
3.代码
python
# 处理消消乐验证码,返回拼图前后的数据
def solve_match_puzzle(ques):
# 1. 转置矩阵:处理【原始列】(极验核心逻辑)
grid = [list(item) for item in zip(*ques)]
# ========== 第一部分:遍历转置后的行 = 原始数据的列 ==========
for r, row in enumerate(grid):
# 过滤无重复的行
same_nums = [n for n in row if row.count(n) == 2]
if not same_nums:
continue
target = same_nums[0]
# 找到唯一不同元素的列索引
diff_nums = [n for n in row if row.count(n) == 1]
if not diff_nums:
continue
c = row.index(diff_nums[0])
coord1 = [r, c]
coord2 = None
# 判断上下交换
if r == 0 and grid[r + 1][c] == target:
coord2 = [r + 1, c]
elif r == 2 and grid[r - 1][c] == target:
coord2 = [r - 1, c]
elif r == 1:
if grid[r - 1][c] == target:
coord2 = [r - 1, c]
elif grid[r + 1][c] == target:
coord2 = [r + 1, c]
# 找到答案,转换为原始坐标并返回
if coord2:
original_coord1 = [coord1[1], coord1[0]]
original_coord2 = [coord2[1], coord2[0]]
return [original_coord1, original_coord2]
# ========== 第二部分:遍历原始数据的行(完整补充) ==========
for r, row in enumerate(ques):
same_nums = [n for n in row if row.count(n) == 2]
if not same_nums:
continue
target = same_nums[0]
diff_nums = [n for n in row if row.count(n) == 1]
if not diff_nums:
continue
c = row.index(diff_nums[0])
coord1 = [r, c]
coord2 = None
if r == 0 and ques[r + 1][c] == target:
coord2 = [r + 1, c]
elif r == 2 and ques[r - 1][c] == target:
coord2 = [r - 1, c]
elif r == 1:
if ques[r - 1][c] == target:
coord2 = [r - 1, c]
elif ques[r + 1][c] == target:
coord2 = [r + 1, c]
if coord2:
return [coord1, coord2]
# 兜底
return None
结果演示

最终代码
JS代码
javascript
const CryptoJs = require("crypto-js");
// ========================= 工具函数 =========================
dtavm = {};
dtavm.log = console.log;
function proxy(obj, objname, type) {
function getMethodHandler(WatchName, target_obj) {
let methodhandler = {
apply(target, thisArg, argArray) {
if (this.target_obj) {
thisArg = this.target_obj;
}
let result = Reflect.apply(target, thisArg, argArray);
if (target.name !== "toString") {
if (target.name === "addEventListener") {
dtavm.log(
`调用者 => [${WatchName}] 函数名 => [${target.name}], 传参 => [${argArray[0]}], 结果 => [${result}].`,
);
} else if (WatchName === "window.console") {
} else {
dtavm.log(
`调用者 => [${WatchName}] 函数名 => [${target.name}], 传参 => [${argArray}], 结果 => [${result}].`,
);
}
} else {
dtavm.log(
`调用者 => [${WatchName}] 函数名 => [${target.name}], 传参 => [${argArray}], 结果 => [${result}].`,
);
}
return result;
},
construct(target, argArray, newTarget) {
var result = Reflect.construct(target, argArray, newTarget);
dtavm.log(
`调用者 => [${WatchName}] 构造函数名 => [${target.name}], 传参 => [${argArray}], 结果 => [${result}].`,
);
return result;
},
};
methodhandler.target_obj = target_obj;
return methodhandler;
}
function getObjhandler(WatchName) {
let handler = {
get(target, propKey, receiver) {
let result = target[propKey];
if (result instanceof Object) {
if (typeof result === "function") {
dtavm.log(
`调用者 => [${WatchName}] 获取属性名 => [${propKey}] , 是个函数`,
);
return new Proxy(result, getMethodHandler(WatchName, target));
} else {
dtavm.log(
`调用者 => [${WatchName}] 获取属性名 => [${propKey}], 结果 => [${result}]`,
);
}
return new Proxy(result, getObjhandler(`${WatchName}.${propKey}`));
}
if (typeof propKey !== "symbol") {
dtavm.log(
`调用者 => [${WatchName}] 获取属性名 => [${propKey?.description ?? propKey}], 结果 => [${result}]`,
);
}
return result;
},
set(target, propKey, value, receiver) {
if (value instanceof Object) {
dtavm.log(
`调用者 => [${WatchName}] 设置属性名 => [${propKey}], 值为 => [${value}]`,
);
} else {
dtavm.log(
`调用者 => [${WatchName}] 设置属性名 => [${propKey}], 值为 => [${value}]`,
);
}
return Reflect.set(target, propKey, value, receiver);
},
has(target, propKey) {
var result = Reflect.has(target, propKey);
dtavm.log(
`针对in操作符的代理has=> [${WatchName}] 有无属性名 => [${propKey}], 结果 => [${result}]`,
);
return result;
},
deleteProperty(target, propKey) {
var result = Reflect.deleteProperty(target, propKey);
dtavm.log(
`拦截属性delete => [${WatchName}] 删除属性名 => [${propKey}], 结果 => [${result}]`,
);
return result;
},
defineProperty(target, propKey, attributes) {
var result = Reflect.defineProperty(target, propKey, attributes);
dtavm.log(
`拦截对象define操作 => [${WatchName}] 待检索属性名 => [${propKey.toString()}] 属性描述 => [${attributes}], 结果 => [${result}]`,
);
// debugger
return result;
},
getPrototypeOf(target) {
var result = Reflect.getPrototypeOf(target);
dtavm.log(`被代理的目标对象 => [${WatchName}] 代理结果 => [${result}]`);
return result;
},
setPrototypeOf(target, proto) {
dtavm.log(
`被拦截的目标对象 => [${WatchName}] 对象新原型==> [${proto}]`,
);
return Reflect.setPrototypeOf(target, proto);
},
preventExtensions(target) {
dtavm.log(`方法用于设置preventExtensions => [${WatchName}] 防止扩展`);
return Reflect.preventExtensions(target);
},
isExtensible(target) {
var result = Reflect.isExtensible(target);
dtavm.log(
`拦截对对象的isExtensible() => [${WatchName}] isExtensible, 返回值==> [${result}]`,
);
return result;
},
};
return handler;
}
if (type === "method") {
return new Proxy(obj, getMethodHandler(objname, obj));
}
return new Proxy(obj, getObjhandler(objname));
}
// 【全局防 toString 检测 · 完整版】
(() => {
"use strict";
// 1. 保存原生的 Function.toString 方法
const $toString = Function.toString;
// 2. 创建唯一隐形标记(Symbol)
const myFunction_toString_symbol = Symbol(
"_" + Math.random().toString(36).slice(2),
);
// 3. 自定义 toString 逻辑(检票员)
const myToString = function () {
return (
(typeof this === "function" && this[myFunction_toString_symbol]) ||
$toString.call(this)
);
};
// 4. 隐形贴标工具(给函数挂隐藏属性)
function set_native(func, key, value) {
Object.defineProperty(func, key, {
enumerable: false, // 设置为【不可枚举】= 隐形
configurable: true,
writable: true,
value: value, // 要伪装的内容:原生函数字符串{[native code]}
});
}
// 5. 替换全局的 toString(接管所有函数)
delete Function.prototype.toString; // 删除系统原生的toString
set_native(Function.prototype, "toString", myToString);
// Function.prototype:安装目标 → 函数总原型
// 'toString':安装的属性名 → 覆盖 toString 方法
// myToString:我们自己写的自定义 toString 逻辑(有标记就伪装原生,没标记就正常输出)
// 6. 给全局挂载一键伪装工具
globalThis.safefunction = (func) => {
set_native(
func, // 要伪装的函数
myFunction_toString_symbol, // 要伪装的属性名
`function ${func.name || ""}() { [native code] }`, // 要伪装的内容
);
};
// 7. 自我保护:让我们的伪装工具也看起来像原生
set_native(
Function.prototype.toString,
myFunction_toString_symbol,
"function toString() { [native code] }",
);
})();
function obj_toString(obj, tag) {
// 标准原生属性配置(完全模拟浏览器原生行为,防检测)
Object.defineProperty(obj, Symbol.toStringTag, {
value: tag,
enumerable: false, // 不可枚举(原生对象默认行为)
configurable: true, // 可重新配置(防止重复定义报错)
writable: false, // 不可修改(更贴近原生,防二次检测)
});
}
// ========================= 补环境代码 =========================
// EventTarget
EventTarget = function EventTarget() {
};
safefunction(EventTarget);
EventTarget.prototype.addEventListener = function () {
};
safefunction(EventTarget.prototype.addEventListener);
// Node
Node = function Node() {
};
safefunction(Node);
Object.setPrototypeOf(Node.prototype, EventTarget.prototype);
// window
Window = function Window() {
};
safefunction(Window);
Object.setPrototypeOf(Window.prototype, EventTarget.prototype);
window = global;
self = top = globalThis = parent = window;
Object.setPrototypeOf(window, Window.prototype);
obj_toString(window, "Window");
// window = proxy(window, "window");
// HTMLHtmlElement
HTMLHtmlElement = function HTMLHtmlElement() {
};
safefunction(HTMLHtmlElement);
// Object.setPrototypeOf(HTMLHtmlElement.prototype, HTMLElement.prototype);
html = new HTMLHtmlElement();
obj_toString(html, "HTMLHtmlElement");
// html = proxy(html, "html");
// HTMLHeadElement
HTMLHeadElement = function HTMLHeadElement() {
};
safefunction(HTMLHeadElement);
// Object.setPrototypeOf(HTMLHeadElement.prototype, HTMLElement.prototype);
head = new HTMLHeadElement();
obj_toString(head, "HTMLHeadElement");
// head = proxy(head, "head");
// HTMLBodyElement
HTMLBodyElement = function HTMLBodyElement() {
};
safefunction(HTMLBodyElement);
// Object.setPrototypeOf(HTMLBodyElement.prototype, HTMLElement.prototype);
HTMLBodyElement.prototype.removeChild = function (args) {
console.log("对象:HTMLHtmlElement.removeChild", args);
};
body = new HTMLBodyElement();
obj_toString(body, "HTMLBodyElement");
// body = proxy(body, "body");
// document
Document = function Document() {
};
safefunction(Document);
Object.setPrototypeOf(Document.prototype, Node.prototype);
Document.prototype.head = head;
Document.prototype.body = body;
Document.prototype.documentElement = html;
Document.prototype.createElement = function createElement(tag) {
console.log(`特殊检测: createElement 被调用 (tag: ${tag})`);
};
safefunction(Document.prototype.createElement);
HTMLDocument = function HTMLDocument() {
};
safefunction(HTMLDocument);
Object.setPrototypeOf(HTMLDocument.prototype, Document.prototype);
document = new HTMLDocument();
obj_toString(document, "HTMLDocument");
// document = proxy(document, "document");
// navigator
Navigator = function Navigator() {
};
safefunction(Navigator);
Navigator.prototype = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36'
,
platform: "Win32",
hardwareConcurrency: 16,
webdriver: false,
languages: ["zh-CN", "zh"],
appName: "Netscape",
appVersion: '5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
plugins: {
0: {
0: {},
1: {},
},
1: {
0: {},
1: {},
},
2: {
0: {},
1: {},
},
3: {
0: {},
1: {},
},
4: {
0: {},
1: {},
},
length: 5,
},
};
navigator = new Navigator();
obj_toString(navigator, "Navigator");
// navigator = proxy(navigator, "navigator");
// location
Location = function Location() {
};
safefunction(Location);
location = new Location();
location = {
ancestorOrigins: {},
href: "https://gt4.geetest.com/",
origin: "https://gt4.geetest.com",
protocol: "https:",
host: "gt4.geetest.com",
hostname: "gt4.geetest.com",
port: "",
pathname: "/",
search: "",
hash: "",
};
obj_toString(location, "Location");
// location = proxy(location, "location");
// Screen
Screen = function Screen() {
};
safefunction(Screen);
screen = new Screen();
obj_toString(screen, "Screen");
// screen = proxy(screen, "screen");
// localStorage
Storage = function Storage() {
};
safefunction(Storage);
localStorage = new Storage();
obj_toString(localStorage, "Storage");
// localStorage = proxy(localStorage, "localStorage");
// crypto
Crypto = function Crypto() {
};
safefunction(Crypto);
Crypto.prototype.getRandomValues = function getRandomValues(typedArray) {
for (let i = 0; i < typedArray.length; i++) {
// 生成对应类型的随机数
if (typedArray instanceof Uint8Array) {
typedArray[i] = Math.floor(Math.random() * 256); // 0-255
} else if (typedArray instanceof Uint16Array) {
typedArray[i] = Math.floor(Math.random() * 65536); // 0-65535
} else if (typedArray instanceof Uint32Array) {
typedArray[i] = Math.floor(Math.random() * 4294967296); // 0-4294967295
}
}
// ✅ 关键:必须返回传入的数组(原生规范)
return typedArray;
};
safefunction(Crypto.prototype.getRandomValues);
crypto = new Crypto();
obj_toString(crypto, "Crypto");
// crypto = proxy(crypto, "crypto");
// ========================= 源码 =========================
require("./source.js");
// ========================= 测试 =========================
function get_w(userresponse,hashfunc, lot_number, datetime, captcha_id) {
let key;
let pow_msg;
let pow_sign;
// 🔥 核心:循环生成 key,直到 pow_sign 以 00 开头
do {
// 每次循环重新生成 key
key = window.key();
// 拼接 pow_msg
pow_msg = `1|8|${hashfunc}|${datetime}|${captcha_id}|${lot_number}||${key}`;
// 计算 sha256
if (hashfunc === 'md5') {
pow_sign = CryptoJs.MD5(pow_msg).toString();
} else if (hashfunc === 'sha1') {
pow_sign = CryptoJs.SHA1(pow_msg).toString();
} else if (hashfunc === 'sha256') {
pow_sign = CryptoJs.SHA256(pow_msg).toString();
}
// 调试:打印看看是否满足条件
// console.log("重试中...pow_sign:", pow_sign);
} while (pow_sign.indexOf('00') !== 0); // 不满足就一直循环
console.log("✅ 找到符合条件的pow_sign:", pow_sign);
console.log("✅ 对应key:", key);
str1 = lot_number.substr(19, 6) // 索引19开始截取6位
str2 = lot_number.substr(23, 8) // 索引23开始截取8位
str3 = lot_number.substr(5, 8) // 索引5开始截取8位
str4 = lot_number.substr(14, 6) // 索引14开始截取6位
const passtime = Math.floor(Math.random() * 300) + 500; // 300-800ms 真人耗时
aaa = {
"passtime": passtime, // 滑动耗时
"userresponse": userresponse, // [[第1个点击的坐标],[第2个点击的坐标]]
"device_id": "",
"lot_number": lot_number, // load接口返回
// hashfunc、datetime 由load接口返回, captch_id 由captcha_id接口返回
"pow_msg": pow_msg,
// 由pow_msg 加密得来
"pow_sign": pow_sign,
"geetest": "captcha", // 固定的
"lang": "zh", // 固定的
"ep": "123", // 固定的
"biht": "1426265548", // 固定的
"gee_guard": {
"roe": {
"aup": "3",
"sep": "3",
"egp": "3",
"auh": "3",
"rew": "3",
"snh": "3",
"res": "3",
"cdc": "3"
}
},
"4MTT": "0Qh0", // 动态的
// 3个值都来自load接口,截取lot_number中的部分值
[str1]: {
[str2]: {
[str3]: str4
}
},
"em": { "ph": 0, "cp": 0, "ek": "11", "wd": 1, "nt": 0, "si": 0, "sc": 0 } // 固定的
};
console.log(aaa)
arr = {
options: { 'pt': '1' }
}
result = window.iii(JSON.stringify(aaa), arr);
return result;
}
console.log(get_w([[2,2],[1,2]], 'sha256', "dbbcf007750e48bea942e4bb7d350bc9", '2026-04-03T13:29:07.115836+08:00', '54088bb07d2df3c46b79f80300b0abbe'));
Python代码
python
import requests
import execjs
import time
import re
import random
import json
jsocde = open('消消乐验证码.js','r',encoding='utf-8').read()
jscompile = execjs.compile(jsocde)
session = requests.Session()
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Referer": "https://gt4.geetest.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
"sec-ch-ua": "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\""
}
cookies = {
"captcha_v4_user": "",
"Hm_lvt_25b04a5e7a64668b9b88e2711fb5f0c4": "",
"sensorsdata2015jssdkcross": ""
}
session.headers.clear()
session.headers.update(headers)
session.cookies.update(cookies)
# 动态生成challenge,uuid格式
def generate_challenge():
def replace(c):
r = random.randint(0, 15)
v = r if c == 'x' else (r & 0x3 | 0x8)
return hex(v)[2:]
uuid_str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
result = ''
for char in uuid_str:
result += replace(char) if char in ('x','y') else char
return result
# 解析JSONP响应
def parse_jsonp(response_text):
"""把 geetest_xxx({...}) 转为 纯JSON"""
left = response_text.find('(') + 1
right = response_text.rfind(')')
json_str = response_text[left:right]
return json.loads(json_str)
# 处理消消乐验证码,返回拼图前后的数据
def solve_match_puzzle(ques):
# 1. 转置矩阵:处理【原始列】(极验核心逻辑)
grid = [list(item) for item in zip(*ques)]
# ========== 第一部分:遍历转置后的行 = 原始数据的列 ==========
for r, row in enumerate(grid):
# 过滤无重复的行
same_nums = [n for n in row if row.count(n) == 2]
if not same_nums:
continue
target = same_nums[0]
# 找到唯一不同元素的列索引
diff_nums = [n for n in row if row.count(n) == 1]
if not diff_nums:
continue
c = row.index(diff_nums[0])
coord1 = [r, c]
coord2 = None
# 判断上下交换
if r == 0 and grid[r + 1][c] == target:
coord2 = [r + 1, c]
elif r == 2 and grid[r - 1][c] == target:
coord2 = [r - 1, c]
elif r == 1:
if grid[r - 1][c] == target:
coord2 = [r - 1, c]
elif grid[r + 1][c] == target:
coord2 = [r + 1, c]
# 找到答案,转换为原始坐标并返回
if coord2:
original_coord1 = [coord1[1], coord1[0]]
original_coord2 = [coord2[1], coord2[0]]
return [original_coord1, original_coord2]
# ========== 第二部分:遍历原始数据的行(完整补充) ==========
for r, row in enumerate(ques):
same_nums = [n for n in row if row.count(n) == 2]
if not same_nums:
continue
target = same_nums[0]
diff_nums = [n for n in row if row.count(n) == 1]
if not diff_nums:
continue
c = row.index(diff_nums[0])
coord1 = [r, c]
coord2 = None
if r == 0 and ques[r + 1][c] == target:
coord2 = [r + 1, c]
elif r == 2 and ques[r - 1][c] == target:
coord2 = [r - 1, c]
elif r == 1:
if ques[r - 1][c] == target:
coord2 = [r - 1, c]
elif ques[r + 1][c] == target:
coord2 = [r + 1, c]
if coord2:
return [coord1, coord2]
# 兜底
return None
# 请求 captcha_id 接口
def get_captcha_id():
# 请求的js文件名是会变化的,有效时间大概6-8个小时
url = "https://gt4.geetest.com/assets/index.1395bf87.js"
response = session.get(url)
pattern = r'captchaId:"([a-f0-9]{32})"'
match = re.search(pattern, response.text)
captcha_id = match.group(1)
return captcha_id
# 请求验证码接口(load接口)
def get_captcha_load(captcha_id):
# 动态生成callback(时间戳,极验要求)
callback = f"geetest_{int(time.time() * 1000)}"
# 动态生成 challenge
challenge = generate_challenge()
params = {
"callback": callback,
"captcha_id": captcha_id,
"challenge": challenge,
"client_type": "web",
"risk_type": "match",
"lang": "zho"
}
url = "https://gcaptcha4.geetest.com/load"
response = session.get(url, params=params)
result = parse_jsonp(response.text)
return result
# 请求验证接口(verify接口)
def get_captcha_verify(captcha_id, load_response, w):
# 动态生成callback(时间戳,极验要求)
callback = f"geetest_{int(time.time() * 1000)}"
# 从load结果中拿【最新、未过期】的参数
lot_number = load_response["data"]["lot_number"]
payload = load_response["data"]["payload"]
process_token = load_response["data"]["process_token"]
params = {
# 时间戳,动态
"callback": callback,
"captcha_id": captcha_id, # 验证码id
"client_type": "web",
# 动态,由load接口返回
"lot_number": lot_number,
"risk_type": "slide",
# 动态,由load接口返回
"payload": payload,
# 动态,由load接口返回
"process_token": process_token,
"payload_protocol": "1",
"pt": "1",
# 加密参数,1504位
"w": w,
}
# print("verify接口请求参数>>>>",params)
url = "https://gcaptcha4.geetest.com/verify"
response = session.get(url, params=params)
result = parse_jsonp(response.text)
return result
if __name__ == '__main__':
captcha_id = get_captcha_id()
# print("captcha_id>>>>", captcha_id)
# 首次获取Load数据
load_response = get_captcha_load(captcha_id)
print("load接口响应数据>>>>", load_response)
lot_number = load_response["data"]["lot_number"]
hashfunc = load_response["data"]["pow_detail"]["hashfunc"]
datetime = load_response["data"]["pow_detail"]["datetime"]
# 处理消消乐验证码,返回拼图前后的数据
ques = load_response["data"]["ques"]
userresponse = solve_match_puzzle(ques)
# userresponse = input("请输入拼图前/后数据:")
print("拼图前/后数据>>>>", userresponse)
w = jscompile.call("get_w",userresponse, hashfunc, lot_number, datetime, captcha_id)
print("加密参数w>>>>", w)
# 执行验证流程
verify_response = get_captcha_verify(captcha_id, load_response,w)
print("验证结果>>>>", verify_response)