# 亚马逊详情页标签批量查询.xlsm 逻辑拆解

源文件：`/Users/kk/Downloads/亚马逊详情页标签批量查询.xlsm`

## 1. 文件结构

- 文件类型：Excel 2007+ 宏工作簿 `.xlsm`
- 未加密
- 有 VBA 宏
- 无 XLM 宏
- 无外部远程关系
- 工作表：
  - `查询`：主操作页
  - `说明`：工具说明和两张标签截图
  - `退货与保留标签记录`：历史日志

## 2. 主表结构

`查询` 表：

| 区域 | 含义 |
|---|---|
| A1:B1 | 作者信息 |
| C1:D1 | 资源网址 |
| A2:C2 | 运行状态、进度、耗时 |
| D2 | 点击触发查询的单元格 |
| A3 | SKU / 产品名称 |
| B3 | ASIN码 / 链接 |
| C3 | 标签状态 |
| D3 | 检测时间 |
| B4:B最后一行 | 用户输入的 ASIN 或 Amazon 链接 |
| C4:C最后一行 | 宏写入检测结果 |
| D4:D最后一行 | 宏写入检测时间 |

`退货与保留标签记录` 表：

| 列 | 含义 |
|---|---|
| A | 检测时间 |
| B | ASIN / 链接 |
| C | 标签状态 |

## 3. 启动方式

不是按钮控件触发，而是 `Worksheet_SelectionChange` 事件触发：

1. 用户点击 `查询!D2`
2. 宏检查 B 列最后一行
3. 如果 `lastRow < 4`，提示没有 ASIN 数据
4. 计算待查数量：`totalCount = lastRow - 3`
5. 按每个项目约 5 秒估算耗时：`Int((totalCount * 5) / 60) + 1`
6. 弹窗确认是否开始
7. 用户点“是”后调用 `批量查询亚马逊标签`
8. 自动把光标移到 `E2`，避免重复触发

## 4. 单个 ASIN 检测逻辑

核心函数：`CheckAmazonBadge(AsinOrUrl As String) As String`

### URL 拼接

如果输入不包含 `http`：

```text
https://www.amazon.com/dp/ + Trim(输入)
```

如果输入包含 `http`：

```text
直接使用输入值
```

### 请求方式

使用同步 COM 请求：

```vb
Set xmlHttp = CreateObject("MSXML2.XMLHTTP")
xmlHttp.Open "GET", url, False
```

请求头：

```text
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Upgrade-Insecure-Requests: 1
```

### 页面解析

1. 如果 HTTP 状态码是 `200`
2. 创建 `htmlfile`
3. 把 `xmlHttp.responseText` 写入 `htmlDoc.body.innerHTML`
4. 读取 `htmlDoc.body.innerText`
5. 只对纯文本做关键词包含判断

## 5. 标签判断规则

按顺序判断，命中后立即返回：

| 判断顺序 | 关键词 | 输出 |
|---|---|---|
| 1 | `Frequently returned` | `Frequently returned item` |
| 2 | `usually keep this item` 或 `Highly retained` | `Customers usually keep` |
| 3 | `triggered an automated robot` 或 `api-services-support@amazon.com` | `被验证码拦截` |
| 4 | 无以上关键词 | `正常 (无标签)` |

如果 HTTP 状态码不是 200：

```text
网页请求失败 (状态码: xxx)
```

如果 VBA 抛异常：

```text
发生错误: Err.Description
```

## 6. 批量查询流程

核心过程：`批量查询亚马逊标签`

1. 设置 `ws = ActiveSheet`
2. 关闭事件：`Application.EnableEvents = False`
3. 修复第 1 行作者和网址样式
4. 确保存在 `退货与保留标签记录` 表
5. 计算 `lastRow = B列最后一行`
6. 从第 4 行循环到最后一行
7. 每行读取 `B列` 输入
8. 更新 A2/B2 进度状态
9. 调用 `CheckAmazonBadge`
10. 写入：
    - C列：标签状态
    - D列：检测时间，格式 `yyyy-mm-dd hh:mm:ss`
11. 根据结果设置 C列样式
12. 必要时写入日志表
13. 行与行之间随机等待 3 到 6 秒
14. 查询结束后更新 A2/B2
15. 打开事件：`Application.EnableEvents = True`

## 7. 结果样式

| 结果类型 | C列样式 |
|---|---|
| `Frequently returned item` | 浅红底、深红字、加粗 |
| `Customers usually keep` | 绿色底、白字、加粗 |
| `正常 (无标签)` | 默认样式、取消加粗 |
| 其他错误或请求失败 | 浅黄底、棕色字、加粗 |

## 8. 日志记录规则

### 命中标签

如果结果包含：

- `Frequently returned`
- `Customers usually keep`

则追加到 `退货与保留标签记录`：

| A | B | C |
|---|---|---|
| 当前检测时间 | 输入 ASIN 或链接 | 检测结果 |

### 标签消失

如果本次结果为 `正常`，且日志表 B 列曾经记录过同一个输入，则追加：

```text
标签消失 (恢复普通)
```

并给这一行设置灰色底。

## 9. 反爬和熔断逻辑

### 普通等待

每次请求后随机等待：

```vb
delaySec = Int((6 - 3 + 1) * Rnd + 3)
```

即 3 到 6 秒。

### 验证码等待

如果结果包含 `拦截`：

1. `blockedCount + 1`
2. 状态栏变红
3. 暂停 30 秒
4. 如果连续 3 次被拦截，弹窗提示并停止

### 手动中断

依赖 Windows API：

```vb
GetAsyncKeyState(27)
```

用户按 ESC 时中断。

## 10. 防删改逻辑

`查询` 表有 `Worksheet_Change` 保护逻辑：

1. 打开工作簿或切换工作表时，记录当前 A 列最后行：`g_OldRowCount`
2. 如果 A1 作者信息被改，且检测到行数减少，则执行 `Application.Undo`
3. 如果用户修改第 1 行且 A1 不等于指定作者文本，也执行撤销
4. 弹窗：`禁止删除或修改！操作已撤销。`

注意：宏里常量是：

```text
作者：Ginv | 公众号：Adobe of Amazon
```

但工作表实际写的是：

```text
作者: Ginv | 公众号：Adobe of Amazon
```

一个是中文冒号，一个是英文冒号。这可能导致保护判断存在误触发或逻辑不一致。

## 11. 兼容性限制

- 只支持美国站 `amazon.com`
- 依赖 Windows COM：`MSXML2.XMLHTTP`、`htmlfile`
- 依赖 Windows API：`user32.GetAsyncKeyState`
- 不适合 WPS
- 不适合 Mac 版 Excel
- 没有 Cookie、代理、重试、超时配置
- 没有真实 DOM 定位，只是全页面文本关键词匹配
- Amazon 返回机器人验证页时，只能识别部分关键词

## 12. 新版建议

### 必须复刻的功能

- 输入 ASIN 或链接
- 自动补全 `https://www.amazon.com/dp/{ASIN}`
- 批量请求
- 检测三类结果：
  - 高退货：`Frequently returned item`
  - 高保留：`Customers usually keep this item`
  - 无标签：`正常`
- 记录检测时间
- 标签命中时写入历史日志
- 曾命中过标签但后来恢复正常时写入“标签消失”
- 支持暂停、停止、进度、耗时统计

### 建议改进

- 用独立脚本或本地网页工具替代 VBA
- 使用 `fetch`/Node/Python 请求，并显式配置超时、重试、代理、并发
- 输出字段增加：
  - 标准化 ASIN
  - 请求 URL
  - HTTP 状态码
  - 是否验证码
  - 命中的关键词
  - 原始错误信息
  - 检测批次 ID
- 标签判断不要只扫全页文本，优先定位相关 badge 区域
- 支持从 Excel/CSV 导入和导出
- 加入限速策略：
  - 默认串行
  - 3 到 8 秒随机间隔
  - 连续验证码自动停止
  - 可配置代理和 Cookie
- 加入复查模式：
  - 只复查历史命中过标签的 ASIN
  - 生成“新增标签 / 标签仍存在 / 标签消失”状态

## 13. 可复用伪代码

```text
for each row from input:
    raw = row.asin_or_url.trim()
    if raw is empty:
        continue

    if raw contains "http":
        url = raw
    else:
        asin = normalize_asin(raw)
        url = "https://www.amazon.com/dp/" + asin

    html, status, error = request(url)

    if error:
        result = "请求错误"
    else if status != 200:
        result = "网页请求失败"
    else if html contains "Frequently returned":
        result = "Frequently returned item"
    else if html contains "usually keep this item" or "Highly retained":
        result = "Customers usually keep"
    else if html contains "triggered an automated robot" or "api-services-support@amazon.com":
        result = "被验证码拦截"
    else:
        result = "正常 (无标签)"

    write result and checked_at

    if result is high-return or high-retention:
        append history
    else if result is normal and asin_or_url existed in history:
        append history as "标签消失 (恢复普通)"

    wait random delay
```
