Files
datie-bom/browser_login/README.md

236 lines
6.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# ERP 自动化数据采集 — 技术说明
> 目标系统:[腾一工业互联网平台](https://yunmes.tftykj.cn/#)
> 框架DrissionPagePython
> 更新2026-04-16
---
## 一、ERP 接口如何鉴权
### 1.1 基础机制
该 ERP 基于 **ASP.NET Boilerplate (ABP) 框架**,接口鉴权通过以下两种凭据组合完成:
| 凭据 | 类型 | 说明 |
|---|---|---|
| `auth_token` | Cookie / JWT | 登录后由服务端颁发携带用户ID、角色、租户ID等声明 |
| `ASP.NET_SessionId` | Cookie | 服务端会话标识 |
| `Abp.TenantId` / `Abp.TenantCode` | Cookie | 多租户标识 |
| `x-sig` | 请求Header | 客户端动态生成的请求签名,每次请求不同 |
### 1.2 `auth_token` 解析JWT
登录成功后的 JWT 结构Base64 解码 Payload
```json
{
"nameid": "103",
"unique_name": ["DTCS", "DTCS"],
"role": "0099",
"http://www.aspnetboilerplate.com/identity/claims/tenantId": "18",
"iss": "gdtykj",
"aud": "TfTechApi",
"exp": 1776330877,
"nbf": 1776323677
}
```
该 Token 有效期约 **2 小时**`exp - nbf = 7200s`)。
### 1.3 `x-sig` 签名
每次 POST 请求都携带一个动态值,例:
```
x-sig: QpE4yJ/TNqPJvIkYjww5QM.w/cn1wtipR2zQZSWlrF9L8
```
该签名由**浏览器端 JavaScript 在运行时生成**,算法未公开,无法离线复现。
⚠️ 这意味着**无法绕过浏览器直接用 `requests` 调用 API**。
### 1.4 典型 API 请求示例BOM 查询)
```
POST https://yunmes.tftykj.cn/api/services/TfTechApi/Material/MaterialBom_SearchList_Proxy
Content-Type: application/x-www-form-urlencoded
x-sig: <动态签名>
Cookie: auth_token=<JWT>; Abp.TenantId=18; ...
page=1&rows=50&MaterialCode=&MaterialName=&...
```
---
## 二、我们的程序如何绕过 `x-sig` 获取数据
### 核心思路:让真实浏览器替我们发请求
由于 `x-sig` 无法离线生成,我们**不直接调用 API**,而是:
```
Python 控制真实 Chrome 浏览器
→ 浏览器完成登录cookies 自动注入)
→ 浏览器跳转到目标页面(页面自动发起 API 请求)
→ Python 拦截浏览器网络层的原始响应
→ 提取响应 JSON保存到本地
```
```
┌──────────────────────────────────────────┐
│ Python 程序 │
│ │
│ DrissionPage.listen.start() ←──────┐ │
│ ↓ │ │
│ page.refresh() → Chrome 发出请求 │ │
│ ↓ │ │
│ 服务器响应(含数据)│ │
│ ↓ │ │
│ packet = listen.wait() ────────────┘ │
│ ↓ │
│ packet.response.body → JSON 数据 │
└──────────────────────────────────────────┘
```
### Vue 表单填写的特殊处理
该 ERP 前端使用 **Vue.js 双向绑定**。普通的 `clear() + input()` 会留下残值导致登录失败。
我们使用 JS 原生 setter 强制清空后再模拟键盘输入:
```python
# 用 JS 原生 setter 清空(触发 Vue 响应式)
page.run_js("""
var el = arguments[0];
Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
).set.call(el, '');
el.dispatchEvent(new Event('input', {bubbles: true}));
""", ele)
time.sleep(0.05) # 等 Vue 处理 clear 事件
ele.input(value) # 模拟键盘输入,触发 keydown/change
```
---
## 三、使用方式
### 3.1 环境准备
```bash
# 安装依赖
pip install DrissionPage python-dotenv
# 配置账号(自动模式使用,手动模式不需要)
cp .env.example .env # 或直接编辑 .env
```
`.env` 文件内容:
```env
ERP_URL=https://yunmes.tftykj.cn/#
ERP_TENANT=gddtsk
ERP_USERNAME=DTCS
ERP_PASSWORD=123456
```
> ⚠️ `.env` 已加入 `.gitignore`,不会被提交到代码仓库。
---
### 3.2 自动模式(适合生产部署)
`.env` 读取账号自动完成登录,无需人工干预。
```bash
python bom_query.py
```
**执行流程:**
1. 打开 Chrome访问 ERP 登录页
2. 自动填写租户代码 / 账号 / 密码
3. 点击登录按钮,等待跳转(约 2s
4. 导航到 BOM 表查询页,触发数据加载
5. 拦截 API 响应,保存 JSON 到 `output/` 目录
**典型日志:**
```
[15:58:21] BOM 查询启动 [自动登录]
[15:58:23] 填写登录信息...
[15:58:25] ✅ 登录成功2s
[15:58:43] ✅ 拦截成功 → HTTP 200
[15:58:43] 本页记录: 50 条 | 总计: 1494 条
[15:58:43] ✅ 已保存: output/bom_page1_20260416_155843.json
```
---
### 3.3 手动模式(适合涉密账号,不留存密码)
程序只负责打开浏览器,由**人工在浏览器中完成登录**,程序检测到登录成功后自动继续执行数据采集。
```bash
python bom_query.py --manual
```
**执行流程:**
1. 打开 Chrome访问 ERP 登录页(字段为空,不填写任何内容)
2. 终端提示:`请在浏览器中手动输入账号并登录`
3. 用户在浏览器中自行输入账号、密码并点击登录
4. 程序检测到登录成功后,自动导航到 BOM 页面并采集数据
5. 采集完成,保存 JSON 到 `output/` 目录
**特点:**
- 密码不出现在任何配置文件或代码中
- 程序等待最长 **120 秒**供人工操作
- 每 10 秒打印一次进度提示
---
### 3.4 输出文件
数据保存在 `output/` 目录,文件名格式:
```
output/bom_page1_YYYYMMDD_HHMMSS.json
```
JSON 结构ABP 标准响应):
```json
{
"result": {
"totalCount": 1494,
"items": [
{
"materialCode": "1ADAA00001",
"materialName": "DA400大锥度中走丝",
...
}
]
},
"success": true
}
```
---
## 四、项目文件结构
```
browser_login/
├── login.py # 核心登录模块(自动 / 手动 / 共用等待逻辑)
├── bom_query.py # BOM 数据采集(支持 --manual 参数)
├── .env # 账号配置(自动模式,不提交 git
├── .gitignore # 忽略 .env 等敏感文件
├── output/ # 采集结果 JSON 文件
└── README.md # 本文档
```
---
## 五、已知限制
| 限制 | 说明 |
|---|---|
| 必须使用真实浏览器 | `x-sig` 由 JS 动态生成,无法离线复现 |
| 每次只获取 50 条 | 当前固定取第 1 页,翻页逻辑待开发 |
| JWT 有效期 ~2h | 长时间运行需重新登录 |
| 表格等待逻辑待优化 | 当前用刷新触发请求,偶发 WARN 提示 |