Files
tvbox_collect/recha18/py/panyq.md
T
2026-07-11 04:45:19 +00:00

7.5 KiB

盘友圈 (panyq.com) 全流程搜索API技术文档

第一部分:核心机制 — 动态 Action ID

panyq.com 的后端API调用依赖于一种动态凭证,我们称之为 Action ID。这是一个40位的SHA-1哈希字符串,在API请求中作为 next-action HTTP头的值。

关键特性:

  1. 动态性: Action ID 不是固定不变的。网站前端更新后,旧的ID会失效,导致API请求失败。
  2. 多样性: 完整的搜索流程(从获取凭证到获取最终链接)需要使用 三个不同Action ID,每个ID对应流程中的一个特定步骤。
  3. 来源: 所有有效的 Action ID 都嵌入在网站加载的 .js 静态文件中。

因此,任何可靠的客户端都必须实现一套自动化的 Action ID 获取与验证机制,而不是硬编码这些值。


第二部分:Action ID 的自动获取与验证流程

此流程旨在从网站前端动态发现所有潜在的 Action ID,并通过模拟真实API调用来筛选出分别用于三个关键步骤的有效ID。

流程 1: 搜寻候选 Action ID
  • 目标: 从网站的JavaScript文件中收集所有可能的 Action ID
  • 步骤:
    1. 请求方法: GET
    2. 请求地址: https://panyq.com/ (网站主页)
    3. 解析: 从返回的HTML中,使用正则表达式 <script src="(/_next/static/[^"]+\.js)" 提取所有JS文件的相对路径。
    4. 遍历请求:
      • 对上一步找到的每个JS文件路径,拼接成完整URL (如 https://panyq.com/_next/static/chunks/xxxx.js) 并发起 GET 请求。
      • 从每个JS文件的内容中,使用正则表达式 ["']([a-f0-9]{40})["'] 提取所有40位的十六进制字符串。
    5. 输出: 一个包含所有潜在 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:\?...) 在整个响应文本中匹配链接。
    • 最终结果: 一个可用的下载链接字符串。