7.5 KiB
盘友圈 (panyq.com) 全流程搜索API技术文档
第一部分:核心机制 — 动态 Action ID
panyq.com 的后端API调用依赖于一种动态凭证,我们称之为 Action ID。这是一个40位的SHA-1哈希字符串,在API请求中作为 next-action HTTP头的值。
关键特性:
- 动态性:
Action ID不是固定不变的。网站前端更新后,旧的ID会失效,导致API请求失败。 - 多样性: 完整的搜索流程(从获取凭证到获取最终链接)需要使用 三个不同 的
Action ID,每个ID对应流程中的一个特定步骤。 - 来源: 所有有效的
Action ID都嵌入在网站加载的.js静态文件中。
因此,任何可靠的客户端都必须实现一套自动化的 Action ID 获取与验证机制,而不是硬编码这些值。
第二部分:Action ID 的自动获取与验证流程
此流程旨在从网站前端动态发现所有潜在的 Action ID,并通过模拟真实API调用来筛选出分别用于三个关键步骤的有效ID。
流程 1: 搜寻候选 Action ID
- 目标: 从网站的JavaScript文件中收集所有可能的
Action ID。 - 步骤:
- 请求方法:
GET - 请求地址:
https://panyq.com/(网站主页) - 解析: 从返回的HTML中,使用正则表达式
<script src="(/_next/static/[^"]+\.js)"提取所有JS文件的相对路径。 - 遍历请求:
- 对上一步找到的每个JS文件路径,拼接成完整URL (如
https://panyq.com/_next/static/chunks/xxxx.js) 并发起GET请求。 - 从每个JS文件的内容中,使用正则表达式
["']([a-f0-9]{40})["']提取所有40位的十六进制字符串。
- 对上一步找到的每个JS文件路径,拼接成完整URL (如
- 输出: 一个包含所有潜在
Action ID的集合(候选ID池)。
- 请求方法:
流程 2: 顺序验证并分配 Action ID
此流程按顺序验证候选ID池中的ID,并将它们分配给三个特定角色。验证一个ID后,应将其从候选池中移除,以避免重复分配。
2.1 验证 credential_action_id (用于获取搜索凭证)
- 目标: 找到能成功执行 第三部分 - 步骤1 的ID。
- 验证方法:
- 遍历候选ID池。
- 将当前候选ID作为
<action_id_1>,使用一个测试关键词(如 "test")调用 步骤1: 获取搜索凭证 API。 - 成功标准: API调用成功 (HTTP状态码200) 并且响应体中能够成功解析出
sign,sha,hash三个值。 - 结果: 第一个满足条件的ID即为有效的
credential_action_id。
2.2 验证 intermediate_action_id (用于执行中间步骤)
- 前提: 已获得
credential_action_id以及通过它获取的测试凭证 (hash,sha)。 - 目标: 找到能成功执行 第三部分 - 步骤3 的ID。
- 验证方法:
- 遍历剩余的候选ID池。
- 将当前候选ID作为
<action_id_2>,使用测试凭证和一个伪造的eid(如 "fake_eid")调用 步骤3: 执行中间状态确认 API。 - 成功标准: API调用成功 (HTTP状态码200),且响应体为非空字符串。
- 结果: 第一个满足条件的ID即为有效的
intermediate_action_id。
2.3 验证 final_link_action_id (用于获取最终链接)
- 前提: 已获得前两个ID及所有测试数据,包括通过 步骤2 获取的真实测试
eid。 - 目标: 找到能成功执行 第三部分 - 步骤4 的ID。
- 验证方法:
- 遍历剩余的候选ID池。
- 将当前候选ID作为
<action_id_3>,使用测试eid调用 步骤4: 获取最终链接 API。 - 成功标准: API调用成功 (HTTP状态码200),且响应体内容包含 "http", "magnet", "aliyundrive", 或
"url"等关键字。 - 结果: 第一个满足条件的ID即为有效的
final_link_action_id。
第三部分:搜索API调用四步流程
在成功获取并分配了三个 Action ID 后,即可执行标准的搜索流程。
步骤 1: 获取搜索凭证 (Credentials)
-
API功能: 根据搜索关键词获取后续API调用所需的
sign,sha, 和hash三个核心凭证。 -
请求方法:
POST -
请求地址:
https://panyq.com/ -
请求头 (Headers):
{ "Content-Type": "text/plain;charset=UTF-8", "next-action": "<credential_action_id>", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..." } -
请求体 (Payload):
[{"cat": "all", "query": "<搜索关键词>", "pageNum": 1}](JSON字符串) -
关键输出 (从响应体解析):
sign(字符串): 用于步骤2。sha(64位哈希字符串): 用于步骤3。hash(字符串): 用于步骤3的URL路径和请求头。
步骤 2: 获取搜索结果列表
-
API功能: 使用
sign凭证,拉取包含具体资源条目的JSON列表。 -
请求方法:
GET -
请求地址:
https://panyq.com/api/search?sign=<sign> -
请求头 (Headers): 仅需标准
User-Agent。 -
请求体 (Payload): 无
-
返回值 (Response Body): 包含
data.hits数组的JSON对象。- 关键输出:
data.hits数组。每个对象最重要的字段是:eid(字符串): 资源的唯一标识符,用于步骤3和步骤4。desc(字符串),size_str(字符串) 等。
- 关键输出:
步骤 3: 执行中间状态确认
-
API功能: 针对每一个要获取链接的
eid执行的“解锁”或“状态确认”步骤。 -
请求方法:
POST -
请求地址:
https://panyq.com/search/<hash> -
请求头 (Headers):
{ "Content-Type": "text/plain;charset=UTF-8", "next-action": "<intermediate_action_id>", "Referer": "https://panyq.com/search/<hash>", "next-router-state-tree": "<URL编码后的JSON>", "User-Agent": "..." }next-router-state-tree的原始JSON:["",{"children":["search",{"children":[["hash","<hash>","d"],{"children":["__PAGE__",{},"/search/<hash>","refresh"]}]}]},null,null,true]
-
请求体 (Payload):
[{"eid": "<eid>", "sha": "<sha>", "page_num": "1"}](JSON字符串) -
返回值: 无需解析,但请求必须成功。
步骤 4: 获取最终链接
-
API功能: 获取可下载链接的最后一步。
-
请求方法:
POST -
请求地址:
https://panyq.com/go/<eid> -
请求头 (Headers):
{ "Content-Type": "text/plain;charset=UTF-8", "next-action": "<final_link_action_id>", "Referer": "https://panyq.com/go/<eid>", "next-router-state-tree": "<URL编码后的JSON>", "User-Agent": "..." }next-router-state-tree的原始JSON:["",{"children":["go",{"children":[["eid","<eid>","d"],{"children":["__PAGE__",{},"/go/<eid>","refresh"]}]}]},null,null,true]
-
请求体 (Payload):
[{"eid": "<eid>"}](JSON字符串) -
关键输出 (从响应体解析):
- JSON方式: 查找响应最后一行类似
2:[1,{"url":"..."}]的结构,提取url字段。 - 正则方式: 若JSON解析失败,使用正则表达式
(https?://...|magnet:\?...)在整个响应文本中匹配链接。 - 最终结果: 一个可用的下载链接字符串。
- JSON方式: 查找响应最后一行类似