Compare commits
5 Commits
f8ff1ec0cc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21483e5056 | ||
|
|
12e95e28ae | ||
|
|
9c829356c3 | ||
|
|
ad36215bcb | ||
|
|
e5fd184e00 |
@@ -22,6 +22,7 @@ allowed-tools: "Read,Grep,Glob"
|
||||
|------|------|------|
|
||||
| 02-xx | 缓存查询 | cache/search |
|
||||
| 03-01~03-05 | 报价相关 | search、select、baggage、seat、async-query |
|
||||
| 03-06 | 解锁器 | Unlocker API(Agent/Request 模式、异步轮询、mipu_requests SDK) |
|
||||
| 04-01~04-05 | 订单管理 | hold、payment、query、cancel、retry |
|
||||
| 07-01~07-04 | 请求实体 | FlightSegmentRequest、Passenger、ContactInfo、Ancillary |
|
||||
| 08-01~08-06 | 响应实体 | Itinerary、FlightFare、SegmentElement、FlightPolicy、AncillaryProduct、feeItems |
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
> 验价:可满足8秒内响应,>95%成功率。
|
||||
> 进一步获取免费行李/退改规则信息(对于无法再shopping/search获取的航司场景)
|
||||
|
||||
### 重要说明
|
||||
|
||||
> **验价业务不会有缓存**,每次请求都是完全重新搜索。
|
||||
> **sessionId 不会被复用**,即使输入了 sessionId 也会被忽略,系统会创建全新的会话。
|
||||
> 如需复用 session 加速后续操作(如获取行李、选座、下单),请使用 `shopping/search` 返回的 sessionId。
|
||||
|
||||
## 错误场景(不应该使用本接口的场景)
|
||||
|
||||
> 如航司在点选行程后占位,本接口需谨慎使用,防止航司侧异常。
|
||||
|
||||
@@ -92,8 +92,7 @@
|
||||
| msg | String | null | 系统消息:成功时为 null,失败时返回具体系统提示信息 |
|
||||
| sessionId | String | 66d807b0-abbc-4b57-aeff-bbf4fc29fb75 | UUID: 与航司通信的session值,可以用于加速后续动作,如继续获取包裹,选座,下单。 |
|
||||
| itineraries | Array\<Itinerary\> | - | 航线组合列表。由于在本接口已确定路线和FareFamily,数组长度 ≤ 1 |
|
||||
| ancillaries | Array\<[AncillaryGroup](#ancillarygroup)\> | {json实体} | 辅营产品分组列表(新结构,三级嵌套) |
|
||||
| ancillaryList | Array | {json实体} | *(兼容旧结构)* 当 `ancillaries` 为空时可能存在,建议优先使用 `ancillaries` |
|
||||
| ancillaries | Array\<[AncillaryGroup](#ancillarygroup)\> | {json实体} | 辅营产品分组列表(三级嵌套) |
|
||||
|
||||
### 异步模式响应(async=true)
|
||||
|
||||
@@ -259,6 +258,5 @@ ancillaries (Array)
|
||||
|
||||
> **提示**:
|
||||
> - `ancillaries` 数组按 `journeyDirection`(outbound/inbound)和 `fareFamilyType` 分组
|
||||
> - 优先使用 `ancillaries`(新三级结构),`ancillaryList` 为兼容旧结构
|
||||
> - 同一 `pricingMode` 内,`categoryDetail` 按 `pieceNo` 升序排列
|
||||
> - STEP 模式下,相同 `pieceNo` 可能有多个重量/价格选项(如第 1 件可选 10kg 或 23kg)
|
||||
|
||||
275
plugins/mipu-api/skills/mipu-api/docs/03-06_unlocker.md
Normal file
275
plugins/mipu-api/skills/mipu-api/docs/03-06_unlocker.md
Normal file
@@ -0,0 +1,275 @@
|
||||
<!-- mipuyun-api-doc: unlocker -->
|
||||
# 解锁器 (Unlocker)
|
||||
|
||||
解锁器是米普云的核心代理服务,通过米普 API 代理发送 HTTP 请求到目标网站。目标网站看到的是米普的 IP 和会话,而非你的真实信息。
|
||||
|
||||
## 安装 SDK
|
||||
|
||||
```bash
|
||||
pip install mipu-requests
|
||||
```
|
||||
|
||||
升级到最新版本:
|
||||
|
||||
```bash
|
||||
pip install --upgrade mipu-requests
|
||||
```
|
||||
|
||||
查看当前版本:
|
||||
|
||||
```bash
|
||||
pip show mipu-requests
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```python
|
||||
import mipu_requests
|
||||
|
||||
s = mipu_requests.session(
|
||||
base_url="https://api-eu.mipuyun.com",
|
||||
client_key="<YOUR_CLIENT_KEY>",
|
||||
client_secret="<YOUR_CLIENT_SECRET>",
|
||||
agent="JQ",
|
||||
)
|
||||
s.proxies = "res_mipu_xxx"
|
||||
|
||||
# 发送请求(SDK 自动处理异步轮询、会话管理)
|
||||
resp = s.get("https://www.example.com/page",
|
||||
headers={"user-agent": "Mozilla/5.0"})
|
||||
resp.raise_for_status()
|
||||
print(resp.text)
|
||||
```
|
||||
|
||||
## 从 requests 迁移
|
||||
|
||||
只需两步:
|
||||
|
||||
1. `import requests` → `import mipu_requests`
|
||||
2. 构造时传入凭据,`proxies` 改为字符串
|
||||
|
||||
```python
|
||||
# ── 迁移前 ──
|
||||
import requests
|
||||
s = requests.Session()
|
||||
s.proxies = {"https": "http://proxy:8080"}
|
||||
resp = s.get("https://example.com/api")
|
||||
|
||||
# ── 迁移后 ──
|
||||
import mipu_requests
|
||||
s = mipu_requests.session(
|
||||
base_url="https://api-eu.mipuyun.com",
|
||||
client_key="<YOUR_CLIENT_KEY>",
|
||||
client_secret="<YOUR_CLIENT_SECRET>",
|
||||
agent="JQ",
|
||||
)
|
||||
s.proxies = "res_mipu_xxx"
|
||||
resp = s.get("https://example.com/api")
|
||||
```
|
||||
|
||||
`.get()`、`.post()`、`.json()`、`.raise_for_status()` 等方法完全兼容,其余代码无需改动。
|
||||
|
||||
## 请求方法
|
||||
|
||||
```python
|
||||
resp = s.get(url, params={"key": "value"}, headers={...})
|
||||
resp = s.post(url, data={"key": "value"}, headers={...})
|
||||
resp = s.post(url, json={"key": "value"}, headers={...})
|
||||
resp = s.put(url, data=body, headers={...})
|
||||
resp = s.delete(url, headers={...})
|
||||
```
|
||||
|
||||
## Agent 模式 vs Request 模式
|
||||
|
||||
```python
|
||||
# Agent 模式 — 通过 Agent 代理,维持登录态和会话(默认)
|
||||
s = mipu_requests.session(..., agent="JQ")
|
||||
|
||||
# Request 模式 — 直接代理,无需 Agent
|
||||
s = mipu_requests.session(..., mode="request")
|
||||
```
|
||||
|
||||
## 异步模式
|
||||
|
||||
SDK 默认启用异步模式,自动处理提交和轮询:
|
||||
|
||||
```python
|
||||
# 默认异步(推荐)
|
||||
s = mipu_requests.session(..., async_mode=True)
|
||||
|
||||
# 关闭异步
|
||||
s = mipu_requests.session(..., async_mode=False)
|
||||
|
||||
# 自定义轮询参数
|
||||
s = mipu_requests.session(
|
||||
...,
|
||||
poll_interval=3, # 轮询间隔(秒)
|
||||
poll_max=20, # 最大轮询次数
|
||||
submit_timeout=60, # 提交超时(秒)
|
||||
poll_timeout=(5, 30), # 轮询超时 (connect, read)
|
||||
)
|
||||
```
|
||||
|
||||
## 会话管理
|
||||
|
||||
SDK 自动管理 sessionId,无需手动传递:
|
||||
|
||||
```python
|
||||
s = mipu_requests.session(...)
|
||||
|
||||
# 第 1 次请求 — SDK 自动获取 sessionId
|
||||
resp1 = s.get("https://example.com/login")
|
||||
|
||||
# 第 2 次请求 — SDK 自动传入 sessionId,保持会话
|
||||
resp2 = s.get("https://example.com/dashboard")
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
```python
|
||||
import mipu_requests
|
||||
|
||||
try:
|
||||
resp = s.get("https://example.com/api")
|
||||
resp.raise_for_status()
|
||||
except mipu_requests.HTTPError as e:
|
||||
print(f"目标网站错误: {e.response.status_code}")
|
||||
except mipu_requests.APIError as e:
|
||||
# 米普 API 返回非零 code,如反爬失败、鉴权失败等
|
||||
print(f"API 错误: {e}")
|
||||
except mipu_requests.PollTimeoutError as e:
|
||||
print(f"异步轮询超时: {e}")
|
||||
except mipu_requests.RequestException as e:
|
||||
print(f"请求异常: {e}")
|
||||
```
|
||||
|
||||
## Response 对象
|
||||
|
||||
与 `requests.Response` 完全兼容:
|
||||
|
||||
```python
|
||||
resp.status_code # int 目标网站 HTTP 状态码
|
||||
resp.text # str 响应体文本
|
||||
resp.content # bytes 响应体字节
|
||||
resp.headers # dict 响应头
|
||||
resp.ok # bool status_code 在 200-399
|
||||
resp.url # str 请求 URL
|
||||
resp.json() # dict 解析 JSON
|
||||
resp.raise_for_status() # >=400 抛异常
|
||||
resp.session_id # str 当前会话 ID
|
||||
```
|
||||
|
||||
## 完整流程示例
|
||||
|
||||
```python
|
||||
import mipu_requests
|
||||
|
||||
with mipu_requests.session(
|
||||
base_url="https://api-eu.mipuyun.com",
|
||||
client_key="<YOUR_CLIENT_KEY>",
|
||||
client_secret="<YOUR_CLIENT_SECRET>",
|
||||
agent="JQ",
|
||||
) as s:
|
||||
s.proxies = "res_mipu_xxx"
|
||||
|
||||
# 访问首页
|
||||
resp = s.get("https://www.example.com/home")
|
||||
resp.raise_for_status()
|
||||
|
||||
# 提交表单(sessionId 自动传递)
|
||||
resp = s.post("https://www.example.com/submit",
|
||||
data={"field": "value"},
|
||||
headers={"content-type": "application/x-www-form-urlencoded"})
|
||||
resp.raise_for_status()
|
||||
print(resp.json())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 原始 API 参考
|
||||
|
||||
以下为直接调用解锁器 API 的参考,推荐优先使用上方 SDK。
|
||||
|
||||
### 认证
|
||||
|
||||
所有请求必须包含以下 Header:
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| Content-Type | application/json | 固定值 |
|
||||
| client-key | (由米普云提供) | 客户端 key |
|
||||
| client-secret | (由米普云提供) | 客户端 secret |
|
||||
|
||||
### Agent 模式
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| URL | `POST https://{endpoint}/unlocker/agent` |
|
||||
|
||||
**请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://www.example.com/page",
|
||||
"method": "GET",
|
||||
"agent": "JQ",
|
||||
"proxy": "res_mipu_xxx",
|
||||
"async": true,
|
||||
"headers": {"user-agent": "Mozilla/5.0"},
|
||||
"sessionId": ""
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| url | string | 是 | 目标 URL |
|
||||
| method | string | 是 | HTTP 方法 |
|
||||
| agent | string | 是 | Agent 编码 |
|
||||
| proxy | string | 是 | 代理资源标识 |
|
||||
| async | boolean | 否 | 异步模式,默认 true |
|
||||
| headers | object | 否 | 传递给目标网站的请求头 |
|
||||
| body | string | 否 | 请求体 |
|
||||
| contentType | string | 否 | Content-Type 覆盖 |
|
||||
| sessionId | string | 否 | 会话 ID |
|
||||
|
||||
### Request 模式
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| URL | `POST https://{endpoint}/unlocker/request` |
|
||||
|
||||
请求体与 Agent 模式相同,但不需要 `agent` 字段。
|
||||
|
||||
### 同步响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"sessionId": "abc123",
|
||||
"statusCode": 200,
|
||||
"responseBody": "<html>...</html>",
|
||||
"responseHeaders": {"content-type": "text/html"}
|
||||
}
|
||||
```
|
||||
|
||||
### 异步响应
|
||||
|
||||
首次提交返回 `{"code": 202, "agentRequestId": "req_xxx"}`,通过轮询接口获取结果:
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| URL | `GET https://{endpoint}/async/query/{agentRequestId}` |
|
||||
|
||||
### 错误码
|
||||
|
||||
| 码 | 含义 | 处理建议 |
|
||||
|----|------|----------|
|
||||
| 0 | 成功 | — |
|
||||
| 100 | 鉴权失败 | 检查 client-key 和 client-secret |
|
||||
| 102 | 超过限流 | 降低请求频率,稍后重试 |
|
||||
| 103 | Proxy 错误 | 检查 proxy 标识是否正确 |
|
||||
| 104 | Agent 不存在 | 检查 agent 编码,新创建的 Agent 需等待 5 分钟生效 |
|
||||
| 202 | 异步处理中 | 继续轮询 |
|
||||
| 403 | 反爬拦截 | 目标网站触发了反爬机制 |
|
||||
|
||||
更多错误码请查阅 `10_error-codes.md`。
|
||||
@@ -25,19 +25,20 @@
|
||||
| categoryCode | String | "StandardCheckedBaggage" | 辅营类型代码:`StandardCheckedBaggage` = 托运行李,`CabinBaggageUnderSeat` = 客舱行李(座椅下),`CabinBaggageOverheadLocker` = 客舱行李(行李架) |
|
||||
| segmentIndex | Integer | 1 | 航段序号,从 1 开始 |
|
||||
| paxType | String | "ADT" | 乘客类型:`ADT` = 成人,`CHD` = 儿童,`INF` = 婴儿 |
|
||||
| piece | Integer | 1 | 行李件数(PC) |
|
||||
| weight | Integer | 20 | 行李重量(KG) |
|
||||
| size | String | | 行李尺寸 |
|
||||
| maxPiece | Integer | 1 | 最多行李件数,表示最多允许 N 件行李 |
|
||||
| totalWeight | Integer | 20 | 最多行李总重量(KG),表示最多允许 xxKG |
|
||||
|
||||
**示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"freeAncillaryList": [
|
||||
{ "categoryCode": "CabinBaggageOverheadLocker", "segmentIndex": 1, "paxType": "ADT", "piece": 1, "weight": 7 },
|
||||
{ "categoryCode": "StandardCheckedBaggage", "segmentIndex": 1, "paxType": "ADT", "piece": 1, "weight": 20 },
|
||||
{ "categoryCode": "CabinBaggageOverheadLocker", "segmentIndex": 2, "paxType": "ADT", "piece": 1, "weight": 7 },
|
||||
{ "categoryCode": "StandardCheckedBaggage", "segmentIndex": 2, "paxType": "ADT", "piece": 1, "weight": 20 }
|
||||
{ "categoryCode": "CabinBaggageOverheadLocker", "segmentIndex": 1, "paxType": "ADT", "maxPiece": 1, "totalWeight": 7 },
|
||||
{ "categoryCode": "StandardCheckedBaggage", "segmentIndex": 1, "paxType": "ADT", "maxPiece": 1, "totalWeight": 20 },
|
||||
{ "categoryCode": "CabinBaggageOverheadLocker", "segmentIndex": 2, "paxType": "ADT", "maxPiece": 1, "totalWeight": 7 },
|
||||
{ "categoryCode": "StandardCheckedBaggage", "segmentIndex": 2, "paxType": "ADT", "maxPiece": 1, "totalWeight": 20 }
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
> **解读**:以上示例表示去程(segmentIndex=1)和回程(segmentIndex=2)每位成人乘客享有:客舱行李最多 1 件、最多 7KG,托运行李最多 1 件、最多 20KG。
|
||||
Reference in New Issue
Block a user