feat(client): add guarded product link capture

This commit is contained in:
QiuSW
2026-08-04 09:26:37 +08:00
parent e7a4be1b9b
commit 7040bb61d8
11 changed files with 808 additions and 13 deletions
+75
View File
@@ -0,0 +1,75 @@
"""打开已验证的拼多多商品直链并采集只读本地证据。"""
from __future__ import annotations
import argparse
from pathlib import Path
import sys
CLIENT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(CLIENT_ROOT / "src"))
from cmbuyer_client.device.adb import AdbClient, DeviceConnectionError, SubprocessAdbRunner
from cmbuyer_client.device.baseline import NoReconnectUiautomatorConnector
from cmbuyer_client.pdd.product_open import ProductOpenCapturer, ProductOpenError
from cmbuyer_client.pdd.product_url import ProductUrlError, parse_product_url
def parse_arguments(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="打开 canonical 拼多多商品链接并采集只读证据。")
parser.add_argument("--serial", required=True, help="ADB device serial;禁止自动选择。")
parser.add_argument("--url", required=True, help="唯一允许的 goods.html?goods_id= 直链。")
parser.add_argument("--output-dir", required=True, type=Path, help="新建的本地证据目录;不得覆盖已有目录。")
parser.add_argument("--timeout", type=float, default=10.0, help="ADB 和只读 RPC 超时(秒)。")
parser.add_argument("--adb", default="adb", help="adb 可执行文件路径。")
return parser.parse_args(argv)
def validate_arguments(arguments: argparse.Namespace) -> None:
if not arguments.serial.strip():
raise ValueError("必须显式提供非空 --serial。")
if arguments.timeout <= 0:
raise ValueError("--timeout 必须大于 0。")
parse_product_url(arguments.url)
def main(argv: list[str] | None = None) -> int:
arguments = parse_arguments(argv)
try:
validate_arguments(arguments)
link = parse_product_url(arguments.url)
except (ValueError, ProductUrlError) as error:
print(f"失败:{error}", file=sys.stderr)
return 2
try:
import adbutils
import uiautomator2 as u2
except ImportError:
print("失败:缺少 uiautomator2;请在采购工具虚拟环境中运行。", file=sys.stderr)
return 2
client = AdbClient(SubprocessAdbRunner(arguments.adb), timeout_seconds=arguments.timeout)
connector = NoReconnectUiautomatorConnector(
adbutils.AdbClient(socket_timeout=arguments.timeout).device_list,
u2.connect,
)
capturer = ProductOpenCapturer(client, connector, timeout_seconds=arguments.timeout)
try:
result = capturer.open_and_capture(arguments.serial, link.canonical_url, arguments.output_dir)
except (DeviceConnectionError, ProductOpenError) as error:
# 不打印 ADB 输出、serial、Activity、XML 或页面正文。
print(f"商品打开取证失败:{error}", file=sys.stderr)
return 1
except OSError:
print("商品打开取证失败:无法创建或发布本地证据目录。", file=sys.stderr)
return 1
print(f"商品打开取证完成:{result.output_directory}")
print(f"manifest:{result.manifest_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())