You've already forked pruchase_jhw
249 lines
12 KiB
Markdown
249 lines
12 KiB
Markdown
# 日本跨境电商采购自动化下单工具 (首期:骏河屋)
|
|
|
|
本项目是一个为日本电商网站(首期重点支持:**骏河屋 Surugaya**)设计的生产级、高稳定性、长期可持续运行的浏览器自动化采购下单系统。
|
|
|
|
## 🪐 技术架构与设计亮点
|
|
|
|
1. **核心驱动**:基于 **Python 3.12+ (Asyncio) + Playwright (Async API)**。全部使用异步协同技术,资源消耗低、性能极高。
|
|
2. **多账号串行锁隔离**:
|
|
- 内存中维护针对 `account_id` 维度的 `asyncio.Lock` 协程锁。
|
|
- 实现**同一个账号串行执行(规避多开浏览器登录冲突/风控),不同账号并发运行**。
|
|
3. **隔离数据环境 (Session 隔离)**:
|
|
- 每个账号在配置中独占一个 `user_data_dir` 浏览器环境。
|
|
- 自动持久化 Cookie、购物车和登录状态,无须每次做短信验证码登录。
|
|
4. **强特征隐藏防爬**:
|
|
- 抹除网页端的 `navigator.webdriver` 痕迹。
|
|
- 注入防指纹脚本(WebGL供应商混淆、多语言偏好 `ja-JP`、标准 Chrome 插件列表伪装)。
|
|
- 配合首选日语区域、东京时区,深度规避 Cloudflare, Akamai 等反爬检测。
|
|
5. **结构化非阻塞日志 (Loguru)**:
|
|
- 支持设置 `DEBUG`、`INFO`、`ERROR` 日志级别。
|
|
- 日志在独立的日志缓冲队列中异步写入,绝不拖慢自动化线程。
|
|
- 每天 `00:00` 自动切割日志文件,默认最多保留 14 天并压缩打包成 zip。
|
|
6. **故障截图与自动归档**:
|
|
- 在采购关键节点(如购买前确认)及发生**任何执行崩溃时**,系统自动捕获紧急截图。
|
|
- 自动按照日期、Topic 智能规划保存目录:
|
|
`archives/YYYYMMDD/{topic}/{scene}_{timestamp}.png`
|
|
7. **任务级桌面录屏 (FFmpeg,可选)**:
|
|
- 录屏开关通过采购消息中的 `record_video` 字段控制,默认关闭。
|
|
- 开启后使用 FFmpeg 录制整个桌面,可覆盖浏览器窗口、系统弹层和鼠标操作过程。
|
|
- 任务结束后自动归档视频到:
|
|
`archives/YYYYMMDD/{topic}/video_{task_id}_{timestamp}.mp4`
|
|
|
|
---
|
|
|
|
## 📂 项目目录结构
|
|
|
|
```text
|
|
jp_purchase_zdh_5/
|
|
├── .env.dev # 本地开发环境环境变量
|
|
├── .env.prod # 生产部署环境环境变量
|
|
├── pyproject.toml # 现代依赖管理配置文件 (PEP 621)
|
|
├── accounts.yaml # 站点多账号隔离配置文件
|
|
├── README.md # 项目运行配置手册
|
|
├── logs/ # 自动生成的日志切片目录 (Git已忽略)
|
|
├── archives/ # 自动截图及归档素材目录 (Git已忽略)
|
|
└── src/ # 核心源代码
|
|
├── __init__.py
|
|
├── main.py # 进程启动入口 (主信号拦截、服务初始化)
|
|
├── config/
|
|
│ ├── __init__.py
|
|
│ ├── settings.py # Pydantic Settings 全局配置管理
|
|
│ └── accounts.py # Pydantic 账号解析器 (YAML 结构化解析)
|
|
├── queue/
|
|
│ ├── __init__.py
|
|
│ ├── listener.py # Redis Stream 持续监听器 (PEL 奔溃恢复、优雅停机)
|
|
│ └── dispatcher.py # 采购任务分发器 (基于账号协程锁并发串行控制)
|
|
├── browser/
|
|
│ ├── __init__.py
|
|
│ └── manager.py # Playwright 浏览器实例管理 (数据隔离、隐藏测试特征)
|
|
├── tasks/
|
|
│ ├── notifications/
|
|
│ │ ├── __init__.py
|
|
│ │ ├── service.py # 通知服务入口 (事件路由、配置校验、渠道分发)
|
|
│ │ └── channels/
|
|
│ │ └── email.py # SMTP 邮件通知渠道
|
|
│ ├── __init__.py
|
|
│ ├── base.py # 任务基类 (Playwright 生命周期、通用异常捕获、截图归档)
|
|
│ └── surugaya.py # 骏河屋 (Surugaya) 采购业务骨架实现
|
|
└── utils/
|
|
└── utils/
|
|
├── __init__.py
|
|
└── logger.py # Loguru 结构化分割日志配置
|
|
```
|
|
|
|
---
|
|
|
|
## 🛠 快速上手指南
|
|
|
|
### 1. 安装项目依赖
|
|
|
|
本项目推荐使用 [uv](https://github.com/astral-sh/uv) 进行极速的依赖和环境管理。确保您的操作系统上已安装 `uv`(且 Python 版本 >= 3.12):
|
|
|
|
```bash
|
|
# 使用 uv 同步并安装所有依赖(自动创建并管理 .venv 虚拟环境)
|
|
uv sync
|
|
|
|
# 安装 Playwright 核心驱动浏览器
|
|
uv run playwright install chromium
|
|
```
|
|
|
|
> **传统模式(不使用 uv):**
|
|
> 如果您没有安装 `uv`,也可以使用传统的 `pip` 方式进行安装:
|
|
> ```bash
|
|
> # 1. 激活您的虚拟环境 (venv)
|
|
> python -m venv .venv
|
|
> source .venv/bin/activate # Windows 下使用: .venv\Scripts\activate
|
|
>
|
|
> # 2. 安装项目核心库 (建议使用 pip)
|
|
> pip install -e .
|
|
>
|
|
> # 3. 安装 Playwright 核心驱动浏览器
|
|
> playwright install chromium
|
|
> ```
|
|
|
|
### 2. 账号的配置、新增与调整
|
|
|
|
系统所有的可用账号均在根目录的 `accounts.yaml` 中定义:
|
|
|
|
- **新增账号**:在 `surugaya` 节点下增加 `- account_id: "surugaya_acc_0X"`,并给出一个独立的 `user_data_dir` 缓存路径(如 `./user_data/surugaya/acc_0X`)。重启程序即可被正常识别。
|
|
- **调整密码/用户名**:直接编辑对应的参数。
|
|
- **重置登录状态 (清除 Cookie 重新登录)**:无需更改任何代码,直接在物理硬盘上**删除该账号对应的缓存文件夹**(例如 `./user_data/surugaya/acc_01`),再次运行任务时 Playwright 会为您初始化全新的纯净浏览器环境。
|
|
|
|
### 3. 通知模块配置
|
|
|
|
通知模块与采购业务解耦,作为独立基础设施存在。第一期仅支持邮件通知,并且默认只在采购任务失败时触发。
|
|
|
|
在 `.env.dev` 或 `.env.prod` 中新增以下配置:
|
|
|
|
```text
|
|
NOTIFY_ENABLED=true
|
|
NOTIFY_CHANNELS=email
|
|
NOTIFY_ON_TASK_FAILED=true
|
|
|
|
MAIL_ENABLED=true
|
|
MAIL_SMTP_HOST=smtp.example.com
|
|
MAIL_SMTP_PORT=465
|
|
MAIL_SMTP_USE_SSL=true
|
|
MAIL_SMTP_STARTTLS=false
|
|
MAIL_USERNAME=bot@example.com
|
|
MAIL_PASSWORD=your-password
|
|
MAIL_FROM_ADDRESS=bot@example.com
|
|
MAIL_FROM_NAME=JP Purchase Bot
|
|
MAIL_TO_ADDRESSES=ops@example.com,owner@example.com
|
|
MAIL_SUBJECT_PREFIX=[JP-Purchase]
|
|
MAIL_TIMEOUT_SECONDS=10
|
|
```
|
|
|
|
说明:
|
|
|
|
- `NOTIFY_ENABLED` 是通知总开关。
|
|
- `NOTIFY_CHANNELS` 预留多渠道扩展能力,第一期请填写 `email`。
|
|
- `MAIL_TO_ADDRESSES` 支持多个收件人,使用英文逗号分隔。
|
|
- 若邮件配置不完整,程序会打印告警日志,但不会阻止主进程启动,也不会影响任务 ACK。
|
|
|
|
---
|
|
|
|
## ⚡ 运行采购守护进程
|
|
|
|
通过 `APP_ENV` 环境变量区分本地开发与生产配置。
|
|
|
|
### 开发环境下启动 (加载 `.env.dev`):
|
|
```bash
|
|
# Windows Power Shell (使用 uv 推荐)
|
|
$env:APP_ENV="dev"; uv run python -m src.main
|
|
|
|
# Linux / macOS CMD (使用 uv 推荐)
|
|
APP_ENV=dev uv run python -m src.main
|
|
|
|
# --- 传统模式 (不使用 uv,需确保已激活虚拟环境) ---
|
|
# Windows Power Shell
|
|
# $env:APP_ENV="dev"
|
|
# python -m src.main
|
|
#
|
|
# Linux / macOS CMD
|
|
# APP_ENV=dev python -m src.main
|
|
```
|
|
|
|
### 生产环境下启动 (加载 `.env.prod`):
|
|
```bash
|
|
# Windows Power Shell (使用 uv 推荐)
|
|
$env:APP_ENV="prod"; uv run python -m src.main
|
|
|
|
# Linux / macOS CMD (使用 uv 推荐)
|
|
APP_ENV=prod uv run python -m src.main
|
|
|
|
# --- 传统模式 (不使用 uv,需确保已激活虚拟环境) ---
|
|
# Windows Power Shell
|
|
# $env:APP_ENV="prod"
|
|
# python -m src.main
|
|
#
|
|
# Linux / macOS CMD
|
|
# APP_ENV=prod python -m src.main
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 模拟测试指南
|
|
|
|
Redis Stream 采购任务的消息结构体设计如下,你可以通过 Redis CLI 向 Stream 发送任务消息:
|
|
|
|
- 推荐将 `record_video` 放在消息顶层;系统也兼容 `payload.record_video`。
|
|
- 开启录屏前请确保运行环境已安装并可直接调用 `ffmpeg`。
|
|
|
|
```bash
|
|
# 向 Stream (purchase:tasks) 中投递一个骏河屋采购任务,并开启整桌面录屏
|
|
XADD purchase:tasks * task_id "task_test_1001" topic "surugaya_tasks" purchase_account "surugaya_acc_01" site_id "surugaya" record_video "1" payload "{\"item_list\":[{\"id\":\"m72275988725\",\"price_limit\":6500,\"number\":1}]}"
|
|
```
|
|
|
|
服务启动后,系统会:
|
|
1. 监听到上述消息并将其解析。
|
|
2. 调度 `surugaya_tasks` 的任务处理器 `SurugayaPurchaseTask`。
|
|
3. 获取该账号 `surugaya_acc_01` 的协程串行锁,隔离并发启动 Playwright 缓存环境。
|
|
4. 打开目标商品,到达确认环节截图并按目录保存:`archives/YYYYMMDD/surugaya_tasks/购买前确认_{timestamp}.png`。
|
|
5. 若 `record_video="1"`,任务结束后会输出视频文件名与绝对路径,并归档到:`archives/YYYYMMDD/surugaya_tasks/video_task_test_1001_{timestamp}.mp4`。
|
|
6. 任务结束后自动进行 `XACK` 确认并安全关舱。
|
|
7. 若任务执行失败且通知模块已启用,系统会向 `MAIL_TO_ADDRESSES` 中配置的收件人发送失败邮件,邮件正文包含任务 ID、账号、错误原因、截图路径和录屏路径。
|
|
|
|
---
|
|
|
|
## 🐳 Docker 部署(Linux 服务器 + noVNC 远程桌面)
|
|
|
|
容器内通过 supervisord 编排 5 个进程:`Xvfb`(虚拟显示器 :99)→ `fluxbox`(窗口管理器)→ `x11vnc`(VNC server)→ `noVNC/websockify`(浏览器远程桌面)→ 业务进程。浏览器以有头模式跑在虚拟桌面上,Google Pay 模板匹配、pyautogui 鼠标操作与 FFmpeg 录屏(Linux 下自动切换为 `x11grab`)均在该桌面执行。
|
|
|
|
### 构建与启动
|
|
|
|
```bash
|
|
# 构建(构建机需能访问私有 PyPI 索引 git.jerryyan.net 拉取 surugaya-common;
|
|
# 若索引需认证,追加 --build-arg UV_INDEX_JERRYYAN_USERNAME=xxx --build-arg UV_INDEX_JERRYYAN_PASSWORD=xxx)
|
|
docker compose build
|
|
|
|
# 启动(VNC_PASSWORD 必填,是 noVNC/VNC 的访问密码)
|
|
export VNC_PASSWORD=your_password
|
|
docker compose up -d
|
|
```
|
|
|
|
### 访问入口
|
|
|
|
| 入口 | 地址 | 用途 |
|
|
| ---- | ---- | ---- |
|
|
| noVNC 远程桌面 | `http://<host>:6080/vnc.html` | 查看/操作容器内浏览器(鼠标键盘全可用,Google Pay 最终确认等人工环节在此操作) |
|
|
| 截图查看器 | `http://<host>:8080/` | 浏览归档截图 |
|
|
| 账号配置 | `http://<host>:8080/accounts` | 在线编辑 accounts.yaml,支持"保存并重启"(优雅停机后由 supervisord 自动拉起,按新配置重新预热浏览器) |
|
|
|
|
> ⚠️ 8080 与 6080 端口均无应用层鉴权(accounts.yaml 含账号密码),切勿直接暴露公网,应置于内网或经反向代理加鉴权后访问。
|
|
|
|
### 持久化目录(compose 已挂载)
|
|
|
|
- `deploy/chrome-profile/` → `/app/chrome-profile`:浏览器登录态,**必须持久化**;
|
|
- `deploy/config/accounts.yaml` → `/app/config/accounts.yaml`:账号配置(容器内 `ACCOUNTS_CONFIG_PATH` 已指向该路径),首次启动时若不存在会用镜像内模板初始化;
|
|
- `deploy/archives/` → `/archives`、`deploy/logs/` → `/logs`:截图/录屏归档与日志。
|
|
|
|
> 说明:`/app/chrome-profile` 与 `/app/config` 已在 Dockerfile 中声明 `VOLUME`——即使绕过 compose 直接 `docker run` 且忘记挂载,登录态与账号配置也会落入匿名卷而不会随容器删除丢失。新增账号无需改挂载:在 accounts.yaml 中把 `user_data_dir` 指向 `/app/chrome-profile/` 下新的子目录即可。
|
|
|
|
### 部署注意事项
|
|
|
|
1. **代理地址**:容器内 `127.0.0.1` 指向容器自身。代理跑在宿主机时,accounts.yaml 中应填 `http://host.docker.internal:<port>`(compose 已配置 `host-gateway`)。
|
|
2. **登录态迁移**:可将 Windows 的 `chrome-profile/<账号>` 目录拷入 `deploy/chrome-profile/`,但建议首次部署后通过 noVNC 人工核验登录态,失效则在远程桌面内重新登录一次。
|
|
3. **模板图重采集(重要)**:`resources/images/` 下的模板图截自 Windows 屏幕,Linux 下字体/渲染差异可能导致 OpenCV 匹配(阈值 0.85)不达标。识别失败时系统会把整屏截图存到 `archives/<日期>/google_pay_debug/`(可经 8080 截图查看器浏览),从中裁出新模板替换后重新构建镜像即可。
|
|
4. **分辨率**:虚拟桌面默认 `1920x1080x24`,可通过环境变量 `SCREEN_RES` 调整;调整后模板图需按新分辨率重新采集。
|