AutoRpa/rpa_selection/http_util.py

90 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 商城搜索的 HTTP:解 cookie、认 JSON、失败按次数重试。
from __future__ import annotations
import json
import time
from typing import Any
from urllib.parse import unquote
import requests
import urllib3
from mall_config import API_MAX_RETRIES, API_REQUEST_TIMEOUT_SECONDS, API_RETRY_BASE_SECONDS
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 去掉 cookie 值两端引号并做 URL 解码。空值当空串。
def parse_cookie_value(raw_value: str | None) -> str:
if not raw_value:
return ""
return unquote(raw_value.strip().strip('"'))
# 把商城响应收成 JSON 对象。剥掉 BOM 和防劫持前缀。不是对象就报平台和查询键。
def parse_response_json_or_raise(
response: requests.Response,
platform_name: str,
query_key: str,
) -> dict[str, Any]:
try:
payload_json = response.json()
except ValueError as exc:
raw_text = response.text or ""
cleaned_text = raw_text.lstrip("\ufeff").strip()
for prefix in (")]}',", "while(1);"):
if cleaned_text.startswith(prefix):
cleaned_text = cleaned_text[len(prefix) :].strip()
try:
payload_json = json.loads(cleaned_text)
except ValueError:
snippet = cleaned_text.replace("\r", " ").replace("\n", " ")[:200]
raise RuntimeError(
f"{platform_name} 接口返回非 JSON,查询键 {query_key},"
f"HTTP {response.status_code},响应片段:{snippet}"
) from exc
if not isinstance(payload_json, dict):
raise RuntimeError(
f"{platform_name} 接口 JSON 根结构异常,查询键 {query_key}:{type(payload_json)}"
)
return payload_json
# POST 商城搜索。HTTP 或 JSON 失败时按次数重试,仍失败就报平台和查询键。
def post_json_with_retry(
session: requests.Session,
url: str,
*,
platform_name: str,
query_key: str,
**request_kwargs: Any,
) -> dict[str, Any]:
last_error: Exception | None = None
for attempt in range(1, API_MAX_RETRIES + 1):
try:
response = session.post(
url,
timeout=API_REQUEST_TIMEOUT_SECONDS,
verify=False,
**request_kwargs,
)
response.raise_for_status()
return parse_response_json_or_raise(response, platform_name, query_key)
except (requests.RequestException, RuntimeError) as exc:
last_error = exc
if attempt >= API_MAX_RETRIES:
break
sleep_seconds = API_RETRY_BASE_SECONDS * attempt
print(
f"[重试] {platform_name} 查询键 {query_key} 第 {attempt}/{API_MAX_RETRIES} 次失败:"
f"{exc},{sleep_seconds:.1f} 秒后重试。"
)
time.sleep(sleep_seconds)
raise RuntimeError(
f"{platform_name} 接口请求失败,查询键 {query_key},"
f"重试 {API_MAX_RETRIES} 次后仍未成功:{last_error}"
) from last_error