Skill: zuokun300/zuokun300-amazon-scraper (version 1.0.0)

# Amazon 数据爬虫技能 ## 技能描述 爬取 Amazon 商品数据，生成结构化报告（Markdown + Excel）。支持商品搜索、数据提取、品牌分析、爆款识别。 **适用场景：** - 电商选品调研 - 竞品价格监控 - 爆款商品分析 - 市场趋势研究 --- ## 安装方法 ### 方法 1：ClawHub 安装（推荐） ```bash # 访问 ClawHub 页面 https://clawhub.ai/zuokun300/amazon-scraper # 或使用 CLI 安装 clawhub install zuokun300/amazon-scraper ``` ### 方法 2：手动安装 ```bash # 1. 克隆技能到 workspace git clone https://github.com/zuokun300/amazon-scraper.git ~/.openclaw/workspace/skills/amazon-scraper # 2. 安装依赖 pip3 install openpyxl requests --break-system-packages # 3. 配置 Apify API Key export APIFY_API_KEY="apify_api_xxxxx" ``` --- ## 使用方法 ### 基础用法 对 OpenClaw 说： ``` 帮我爬取 Amazon 上的 "women fashion shoes"，生成 Excel 报告 ``` ### 高级用法 ``` 帮我爬取 Amazon 上的 "[关键词]"，需要： - 抓取前 50 个商品 - 提取价格、评分、评论数 - 生成 Excel 和 Markdown 报告 - 分析品牌分布 ``` ### 自定义参数 ```python # 修改脚本中的参数 KEYWORDS = ["women fashion shoes", "men sneakers"] MAX_PRODUCTS = 50 OUTPUT_DIR = "/path/to/output" ``` --- ## 输出文件 ### 1. Excel 报告 (`amazon-data.xlsx`) 包含两个 Sheet： **Sheet 1: 商品数据** | 列名 | 说明 | |------|------| | 排名 | 搜索排名 | | 商品名称 | 完整标题 | | 品牌 | 自动识别的品牌 | | 类型 | 商品分类 | | ASIN | Amazon 标准 ID | | 价格 | 当前价格（需深度爬取） | | 图片链接 | 可点击的主图链接 | | Amazon 链接 | 可点击的商品页链接 | **Sheet 2: 统计摘要** - 品牌分布统计 - 价格区间分析 - 爆款识别 ### 2. Markdown 报告 (`amazon-report.md`) 包含： - 数据概览 - 爆款商品列表（表格） - 商品详情链接 --- ## 配置说明 ### Apify API Key **获取方法：** 1. 访问 https://console.apify.com/signup 注册 2. 登录后访问 https://console.apify.com/account/integrations 3. 复制 API Key **配置方式：** ```bash # 方式 1：环境变量 export APIFY_API_KEY="apify_api_xxxxx" # 方式 2：修改脚本 APIFY_TOKEN = "apify_api_xxxxx" ``` **免费额度：** $5（约 500-1000 次商品爬取） --- ## 依赖项 ```txt openpyxl>=3.0.0 requests>=2.28.0 ``` --- ## 示例输出 ### 爆款商品表格 | 排名 | 商品 | 品牌 | 类型 | ASIN | |------|------|------|------|------| | 1 | adidas Women's VL Court 3.0 | Adidas | Sneaker | B0C2JY169J | | 2 | Adokoo Women's Fashion Sneakers | Adokoo | Sneaker | B0CH9FJY8V | | 3 | Adidas Women's Vl Court 3.0 | Adidas | Sneaker | B0F1XH7M8F | ### 品牌分布统计 | 品牌 | 商品数量 | 占比 | |------|---------|------| | Adidas | 7 | 35% | | ODOLY | 6 | 30% | | LUCKY STEP | 5 | 25% | --- ## 进阶功能 ### 1. 深度爬取（价格/评分/销量） ```bash # 启用深度爬取模式 python3 amazon_scraper.py --deep ``` **额外数据：** - 当前价格 - 用户评分（1-5 星） - 评论总数 - 销量排名（BSR） - 高清商品图片 ### 2. 定时监控 ```bash # 配置 cron 每天执行 0 3 * * * python3 /path/to/amazon_scraper.py ``` **用途：** - 价格变化监控 - 新品上架提醒 - 竞品动态追踪 ### 3. 多关键词批量爬取 ```python KEYWORDS = [ "women fashion shoes", "men sneakers", "kids boots", "running shoes" ] ``` --- ## 注意事项 ### 1. API 成本 - Apify 免费额度：$5/月 - 单次爬取成本：约 $0.002/商品 - 建议：合理控制爬取数量 ### 2. 反爬措施 - Amazon 反爬严格，建议使用 Apify 等专业服务 - 不要高频爬取同一关键词 - 遵守 Amazon 服务条款 ### 3. 数据准确性 - 价格可能实时变化 - 销量为估算值（基于评论数推算） - 建议定期更新数据 --- ## 常见问题 ### Q: 为什么有些商品价格显示"待爬取"？ A: 基础模式只抓取搜索页数据，价格需要访问商品详情页。使用 `--deep` 参数启用深度爬取。 ### Q: Apify 运行失败怎么办？ A: 检查： 1. API Key 是否正确 2. 网络连接是否正常 3. Apify 账户是否有余额 ### Q: 如何自定义输出格式？ A: 修改 `generate_excel()` 和 `generate_report()` 函数，调整列名和样式。 --- ## 更新日志 ### v1.0.0 (2026-03-03) - ✅ 基础爬取功能 - ✅ Excel + Markdown 报告生成 - ✅ 品牌分布统计 - ✅ 可点击链接 ### TODO - [ ] 深度爬取（价格/评分/销量） - [ ] 图片下载功能 - [ ] 多语言支持 - [ ] 定时监控告警 --- ## 作者与许可 - **作者:** Elden (@zuokun300) - **许可:** MIT License - **问题反馈:** https://github.com/zuokun300/amazon-scraper/issues --- ## 相关技能 - **web-scraping-router** - 爬虫工具路由技能 - **apify-scraper** - Apify 工业级爬虫模板 - **price-monitor** - 价格监控哨兵 --- *最后更新：2026-03-03*

path: README.md
content: # Amazon 数据爬虫技能

🛒 爬取 Amazon 商品数据，生成结构化报告（Markdown + Excel）

## 快速开始

### 1. 获取 Apify API Key

访问 https://console.apify.com/account/integrations 获取

### 2. 安装依赖

```bash
pip3 install -r requirements.txt --break-system-packages
```

### 3. 运行爬虫

```bash
# 修改脚本中的关键词
# 然后运行：
python3 amazon_scraper.py
```

## 输出示例

- `amazon-data.xlsx` - Excel 数据表
- `amazon-report.md` - Markdown 报告

## 文档

完整文档见 [SKILL.md](./SKILL.md)

---

**作者:** Elden (@zuokun300)  
**许可:** MIT


path: USAGE.md
content: # 使用教程

## 快速开始

### 1. 安装依赖

```bash
pip3 install -r requirements.txt --break-system-packages
```

### 2. 获取 Apify API Key

1. 访问 https://console.apify.com/signup 注册账号
2. 登录后访问 https://console.apify.com/account/integrations
3. 复制 API Key（格式：`apify_api_xxxxx`）

### 3. 配置 API Key

**方式 1：环境变量（推荐）**
```bash
export APIFY_API_KEY="apify_api_xxxxx"
```

**方式 2：修改脚本**
编辑 `amazon_scraper.py`，找到这一行：
```python
APIFY_TOKEN = os.getenv("APIFY_API_KEY", "apify_api_xxxxx")
```
替换为你的 API Key。

### 4. 运行爬虫

```bash
# 基础用法（使用默认关键词）
python3 amazon_scraper.py

# 自定义关键词
python3 amazon_scraper.py -k "women sneakers"

# 多关键词
python3 amazon_scraper.py -k "women shoes" "men boots" "kids sneakers"

# 指定数量
python3 amazon_scraper.py -n 100

# 指定输出目录
python3 amazon_scraper.py -o ./my-output
```

## 参数说明

| 参数 | 简写 | 说明 | 默认值 |
|------|------|------|--------|
| --keyword | -k | 搜索关键词（可多个） | women fashion shoes |
| --number | -n | 最大商品数量 | 50 |
| --output | -o | 输出目录 | ./output |

## 输出文件

运行成功后，会在输出目录生成两个文件：

### 1. Excel 文件（amazon-data-YYYYMMDD_HHMMSS.xlsx）

包含两个 Sheet：

**Sheet 1: 商品数据**
- 排名、商品名称、品牌、类型、ASIN
- 价格（预留，可手动填写）
- 图片链接（可点击）
- Amazon 链接（可点击）

**Sheet 2: 统计摘要**
- 品牌分布统计（数量和占比）

### 2. Markdown 文件（amazon-report-YYYYMMDD_HHMMSS.md）

包含：
- 数据概览
- 爆款商品列表（表格）
- 商品详情链接
- 品牌分布统计

## 输出示例

### Excel 预览

| 排名 | 商品名称 | 品牌 | 类型 | ASIN |
|------|---------|------|------|------|
| 1 | adidas Women's VL Court 3.0 | Adidas | Sneaker | B0C2JY169J |
| 2 | Adokoo Women's Fashion Sneakers | Adokoo | Sneaker | B0CH9FJY8V |

### 品牌统计

| 品牌 | 数量 | 占比 |
|------|------|------|
| Adidas | 7 | 35% |
| ODOLY | 6 | 30% |
| LUCKY STEP | 5 | 25% |

## 常见问题

### Q: 为什么报错 "No module named 'openpyxl'"？

A: 没有安装依赖。运行：
```bash
pip3 install -r requirements.txt --break-system-packages
```

### Q: Apify 启动失败？

A: 检查：
1. API Key 是否正确
2. 网络连接是否正常
3. Apify 账户是否有余额（免费额度 $5）

### Q: 爬取的数据不完整？

A: 可能原因：
1. Amazon 反爬拦截 - 建议降低爬取频率
2. Apify 额度不足 - 检查账户余额
3. 关键词太冷门 - 换个热门关键词试试

### Q: 如何自定义输出格式？

A: 编辑 `amazon_scraper.py`：
- 修改 `generate_excel()` 函数调整 Excel 格式
- 修改 `generate_markdown()` 函数调整 Markdown 格式

## 进阶用法

### 批量爬取多个品类

创建脚本 `batch_scrape.py`：
```python
from amazon_scraper import main

keywords_list = [
    ["women fashion shoes"],
    ["men sneakers"],
    ["kids boots"],
    ["running shoes"]
]

for keywords in keywords_list:
    main(keywords=keywords, max_products=50)
```

### 定时监控

配置 cron 每天执行：
```bash
# 编辑 crontab
crontab -e

# 添加（每天凌晨 3 点执行）
0 3 * * * cd /path/to/amazon-scraper && python3 amazon_scraper.py
```

### 数据可视化

用 Excel 或 Python 分析数据：
```python
import pandas as pd

# 读取 Excel
df = pd.read_excel("amazon-data.xlsx", sheet_name="商品数据")

# 品牌分布
brand_dist = df['品牌'].value_counts()
print(brand_dist)

# 绘制图表
brand_dist.plot(kind='bar')
```

## 成本说明

### Apify 定价

- **免费额度**: $5/月（约 500-1000 个商品）
- **付费套餐**: $49/月起（5000 个商品）

### 优化建议

1. 合理控制爬取数量（50-100 个通常够用）
2. 避免重复爬取相同关键词
3. 使用缓存（后续版本支持）

## 技术支持

- **GitHub Issues**: https://github.com/zuokun300/amazon-scraper/issues
- **作者邮箱**: [REDACTED_SECRET]

---

*最后更新：2026-03-03*


path: amazon_scraper.py
content: #!/usr/bin/env python3
"""
Amazon 商品数据爬虫
爬取 Amazon 商品数据，生成 Excel + Markdown 报告

作者：Elden (@zuokun300)
许可：MIT
"""

import json
import re
import urllib.request
import sys
import os
from datetime import datetime

# 尝试导入依赖
try:
    from openpyxl import Workbook
    from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
    from openpyxl.utils import get_column_letter
    HAS_OPENPYXL = True
except ImportError:
    HAS_OPENPYXL = False
    print("❌ 缺少依赖：openpyxl")
    print("请运行：pip3 install openpyxl requests --break-system-packages")
    sys.exit(1)

try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False
    print("❌ 缺少依赖：requests")
    print("请运行：pip3 install openpyxl requests --break-system-packages")
    sys.exit(1)


# ============= 配置区域 =============

# Apify API Key（从环境变量读取）
# 使用前请设置：export APIFY_API_KEY="apify_api_xxxxx"
APIFY_TOKEN = os.getenv("APIFY_API_KEY")

if not APIFY_TOKEN:
    print("❌ 错误：未设置 APIFY_API_KEY 环境变量")
    print("请运行：export APIFY_API_KEY=\"apify_api_xxxxx\"")
    print("获取 API Key: https://console.apify.com/account/integrations")
    sys.exit(1)

# 搜索关键词
DEFAULT_KEYWORDS = ["women fashion shoes"]

# 最大商品数量
MAX_PRODUCTS = 50

# 输出目录
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "output")

# ============= 核心函数 =============

def extract_asin(url):
    """从 Amazon URL 提取 ASIN"""
    match = re.search(r'/dp/([A-Z0-9]{10})', url)
    return match.group(1) if match else None


def fetch_apify_data(keywords, max_products=50):
    """
    从 Apify 获取 Amazon 数据
    
    Args:
        keywords: 搜索关键词列表
        max_products: 最大商品数量
    
    Returns:
        商品数据列表
    """
    print(f"🔍 开始爬取 Amazon 数据...")
    print(f"   关键词：{', '.join(keywords)}")
    print(f"   目标数量：{max_products} 个商品")
    
    # 启动 Apify Actor
    start_url = f"https://www.amazon.com/s?k={keywords[0].replace(' ', '+')}"
    
    apify_url = "https://api.apify.com/v2/acts/apify~web-scraper/runs"
    params = {
        "token": APIFY_TOKEN
    }
    
    payload = {
        "startUrls": [{"url": start_url}],
        "linkSelector": "a.a-link-normal",
        "pageFunction": "function pageFunction() { return { title: document.title, url: window.location.href }; }"
    }
    
    try:
        # 启动爬取任务
        response = requests.post(apify_url, params=params, json=payload)
        run_data = response.json()
        
        if "error" in run_data:
            print(f"❌ 启动 Apify 失败：{run_data['error']['message']}")
            return []
        
        run_id = run_data["data"]["id"]
        dataset_id = run_data["data"]["defaultDatasetId"]
        
        print(f"✅ 爬取任务已启动：{run_id}")
        print(f"⏳ 等待爬取完成...")
        
        # 等待爬取完成（最多等待 5 分钟）
        import time
        for i in range(30):
            time.sleep(10)
            
            # 检查状态
            status_url = f"https://api.apify.com/v2/actor-runs/{run_id}"
            status_response = requests.get(status_url, params=params)
            status_data = status_response.json()
            
            status = status_data["data"]["status"]
            if status == "SUCCEEDED":
                print(f"✅ 爬取完成！")
                break
            elif status == "FAILED":
                print(f"❌ 爬取失败")
                return []
            
            print(f"   当前状态：{status} ({i*10}s)")
        
        # 获取数据
        dataset_url = f"https://api.apify.com/v2/datasets/{dataset_id}/items"
        data_response = requests.get(dataset_url, params=params)
        data = data_response.json()
        
        print(f"📦 获取到 {len(data)} 条原始数据")
        return data
        
    except Exception as e:
        print(f"❌ 爬取失败：{e}")
        return []


def generate_report(data, keywords):
    """
    生成商品报告
    
    Args:
        data: 原始数据
        keywords: 搜索关键词
    
    Returns:
        整理后的商品列表
    """
    products = []
    
    for item in data:
        title = item.get('title', '')
        debug = item.get('#debug', {})
        loaded_url = debug.get('loadedUrl', '')
        
        # 跳过搜索页面
        if '/s?' in loaded_url:
            continue
        
        asin = extract_asin(loaded_url)
        if not asin:
            continue
        
        # 从标题提取品牌
        brand = "Unknown"
        title_lower = title.lower()
        if "adidas" in title_lower:
            brand = "Adidas"
        elif "nike" in title_lower:
            brand = "Nike"
        elif "adokoo" in title_lower:
            brand = "Adokoo"
        elif "odoly" in title_lower:
            brand = "ODOLY"
        elif "lucky step" in title_lower:
            brand = "LUCKY STEP"
        elif "new balance" in title_lower:
            brand = "New Balance"
        
        # 判断鞋子类型
        shoe_type = "Sneaker"
        if "boot" in title_lower:
            shoe_type = "Boot"
        elif "sandal" in title_lower:
            shoe_type = "Sandal"
        elif "heel" in title_lower:
            shoe_type = "Heel"
        elif "clog" in title_lower or "mule" in title_lower:
            shoe_type = "Clog"
        
        # 清理标题
        clean_title = title.replace('Amazon.com | ', '').replace(' | Shoes', '').replace(' | Fashion Sneakers', '')
        
        products.append({
            'title': clean_title,
            'asin': asin,
            'brand': brand,
            'type': shoe_type,
            'url': loaded_url,
            'image': f"https://m.media-amazon.com/images/I/61uA2UVnYWL._AC_UL320_.jpg"
        })
    
    # 去重
    seen = set()
    unique_products = []
    for p in products:
        if p['asin'] not in seen:
            seen.add(p['asin'])
            unique_products.append(p)
    
    return unique_products[:max_products]


def generate_excel(products, filepath, keywords):
    """生成 Excel 文件"""
    wb = Workbook()
    ws = wb.active
    ws.title = "商品数据"
    
    # 表头
    headers = ["排名", "商品名称", "品牌", "类型", "ASIN", "价格", "图片链接", "Amazon 链接"]
    for col, header in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col, value=header)
        cell.font = Font(bold=True, color="FFFFFF", size=12)
        cell.alignment = Alignment(horizontal='center')
        cell.fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
    
    # 数据行
    for i, p in enumerate(products, 2):
        ws.cell(row=i, column=1, value=i-1)
        ws.cell(row=i, column=2, value=p['title'])
        ws.cell(row=i, column=3, value=p['brand'])
        ws.cell(row=i, column=4, value=p['type'])
        ws.cell(row=i, column=5, value=p['asin'])
        ws.cell(row=i, column=6, value="待爬取")
        
        img_cell = ws.cell(row=i, column=7, value=p['image'])
        img_cell.hyperlink = p['image']
        img_cell.style = "Hyperlink"
        
        url_cell = ws.cell(row=i, column=8, value=p['url'])
        url_cell.hyperlink = p['url']
        url_cell.style = "Hyperlink"
    
    # 调整列宽
    column_widths = [8, 60, 15, 12, 15, 12, 50, 80]
    for i, width in enumerate(column_widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = width
    
    # 添加边框
    thin_border = Border(
        left=Side(style='thin'),
        right=Side(style='thin'),
        top=Side(style='thin'),
        bottom=Side(style='thin')
    )
    
    for row in ws.iter_rows(min_row=1, max_row=len(products)+1, min_col=1, max_col=len(headers)):
        for cell in row:
            cell.border = thin_border
            cell.alignment = Alignment(wrap_text=True, vertical='center')
    
    # 统计 Sheet
    ws_stats = wb.create_sheet(title="统计摘要")
    
    brand_count = {}
    for p in products:
        brand = p['brand']
        brand_count[brand] = brand_count.get(brand, 0) + 1
    
    ws_stats.cell(row=1, column=1, value="品牌分布统计")
    ws_stats.cell(row=1, column=1).font = Font(bold=True, size=14)
    
    ws_stats.cell(row=3, column=1, value="品牌")
    ws_stats.cell(row=3, column=2, value="商品数量")
    ws_stats.cell(row=3, column=3, value="占比")
    
    total = len(products)
    for i, (brand, count) in enumerate(sorted(brand_count.items(), key=lambda x: x[1], reverse=True), 4):
        ws_stats.cell(row=i, column=1, value=brand)
        ws_stats.cell(row=i, column=2, value=count)
        ws_stats.cell(row=i, column=3, value=f"{count/total*100:.1f}%")
    
    ws_stats.column_dimensions['A'].width = 20
    ws_stats.column_dimensions['B'].width = 15
    ws_stats.column_dimensions['C'].width = 10
    
    wb.save(filepath)
    return filepath


def generate_markdown(products, filepath, keywords):
    """生成 Markdown 报告"""
    md = f"# Amazon 商品数据报告\n\n"
    md += f"## 数据概览\n\n"
    md += f"- **抓取时间**: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n"
    md += f"- **关键词**: {', '.join(keywords)}\n"
    md += f"- **商品数量**: {len(products)}\n\n"
    
    md += f"## 爆款商品列表\n\n"
    md += "| 排名 | 商品 | 品牌 | 类型 | ASIN | 图片 |\n"
    md += "|------|------|------|------|------|------|\n"
    
    for i, p in enumerate(products, 1):
        title_short = p['title'][:40] + "..." if len(p['title']) > 40 else p['title']
        md += f"| {i} | {title_short} | {p['brand']} | {p['type']} | {p['asin']} | ![img]({p['image']}) |\n"
    
    md += f"\n## 商品详情链接\n\n"
    for i, p in enumerate(products, 1):
        md += f"{i}. **{p['title']}** - [Amazon 链接]({p['url']})\n"
    
    # 品牌统计
    brand_count = {}
    for p in products:
        brand = p['brand']
        brand_count[brand] = brand_count.get(brand, 0) + 1
    
    md += f"\n## 品牌分布\n\n"
    md += "| 品牌 | 商品数量 | 占比 |\n"
    md += "|------|---------|------|\n"
    
    total = len(products)
    for brand, count in sorted(brand_count.items(), key=lambda x: x[1], reverse=True):
        md += f"| {brand} | {count} | {count/total*100:.1f}% |\n"
    
    with open(filepath, "w", encoding='utf-8') as f:
        f.write(md)
    
    return filepath


# ============= 主函数 =============

def main(keywords=None, max_products=50, output_dir=None):
    """主函数"""
    if keywords is None:
        keywords = DEFAULT_KEYWORDS
    
    if output_dir is None:
        output_dir = OUTPUT_DIR
    
    # 创建输出目录
    os.makedirs(output_dir, exist_ok=True)
    
    # 获取数据
    data = fetch_apify_data(keywords, max_products)
    
    if not data:
        print("❌ 未获取到数据")
        return
    
    # 生成报告
    products = generate_report(data, keywords)
    print(f"✅ 整理出 {len(products)} 个商品")
    
    # 生成文件
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    excel_path = os.path.join(output_dir, f"amazon-data-{timestamp}.xlsx")
    md_path = os.path.join(output_dir, f"amazon-report-{timestamp}.md")
    
    generate_excel(products, excel_path, keywords)
    generate_markdown(products, md_path, keywords)
    
    print(f"\n✅ 报告已生成：")
    print(f"   📊 Excel: {excel_path}")
    print(f"   📝 Markdown: {md_path}")
    
    # 显示前 5 个商品
    print(f"\n🏆 Top 5 爆款商品:")
    for i, p in enumerate(products[:5], 1):
        print(f"   {i}. {p['brand']} - {p['title'][:50]}...")
    
    return products


# ============= 命令行入口 =============

if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="Amazon 商品数据爬虫")
    parser.add_argument("-k", "--keyword", nargs="+", default=DEFAULT_KEYWORDS, help="搜索关键词")
    parser.add_argument("-n", "--number", type=int, default=MAX_PRODUCTS, help="最大商品数量")
    parser.add_argument("-o", "--output", default=OUTPUT_DIR, help="输出目录")
    
    args = parser.parse_args()
    
    main(keywords=args.keyword, max_products=args.number, output_dir=args.output)


path: requirements.txt
content: openpyxl>=3.0.0
requests>=2.28.0


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)