Add cross-border-ecommerce/competitor-price-monitor
This commit is contained in:
@@ -0,0 +1,315 @@
|
|||||||
|
---
|
||||||
|
name: competitor-price-monitor
|
||||||
|
description: "竞品价格监控 — 多平台竞品价格自动采集、对比分析、定时监控。覆盖 1688、Ozon、Temu、淘宝、京东。一句话触发完整流水线。"
|
||||||
|
version: 1.0.0
|
||||||
|
author: Hermes Agent
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
hermes:
|
||||||
|
tags: [price-monitoring, competitor-analysis, scraping, cron, cross-border, 1688, ozon, temu, taobao, jd]
|
||||||
|
related_skills:
|
||||||
|
- web-scraping-toolkit
|
||||||
|
- proxy-management
|
||||||
|
- drissionpage-toolkit
|
||||||
|
- 1688-cross-border-sourcing
|
||||||
|
- ozon-operations
|
||||||
|
- camoufox-google-2fa-login
|
||||||
|
platforms: [1688, ozon, temu, taobao, jd]
|
||||||
|
cron_compatible: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# 竞品价格监控 (Competitor Price Monitor)
|
||||||
|
|
||||||
|
一句话触发:**"监控 [平台] 上 [品类/关键词] 的竞品价格"**,Agent 自动走完整条采集→分析→报告链。
|
||||||
|
|
||||||
|
## 触发条件
|
||||||
|
|
||||||
|
当用户提到以下任一关键词组合时,加载本 Skill:
|
||||||
|
- "竞品价格" / "价格监控" / "比价" / "价格采集"
|
||||||
|
- "监控 [平台] 价格" / "[品类] 竞品分析"
|
||||||
|
- "对比 [平台A] 和 [平台B] 价格"
|
||||||
|
- "定时采集 [平台] 价格"
|
||||||
|
|
||||||
|
## 平台适配矩阵
|
||||||
|
|
||||||
|
| 平台 | 首选方法 | 备选方法 | 反爬强度 | 代理需求 |
|
||||||
|
|------|---------|---------|---------|---------|
|
||||||
|
| **1688** | Firecrawl API | CamouFox + 国内住宅代理 | 极高 (阿里云WAF+指纹) | 必须 |
|
||||||
|
| **淘宝** | CamouFox + 国内代理 | Jina Reader (限简单页) | 高 | 建议 |
|
||||||
|
| **京东** | Jina Reader / Crawl4AI | CamouFox | 中 | 可选 |
|
||||||
|
| **Ozon** | CamouFox + 俄罗斯代理 | CDP Auto-Surf | 高 (Cloudflare+滑块) | 必须 |
|
||||||
|
| **Temu** | Jina Reader / web_extract | CamouFox (卖家端用CDP) | 中 | 可选 |
|
||||||
|
|
||||||
|
## 完整工作流
|
||||||
|
|
||||||
|
### Phase 1: 参数解析与任务规划
|
||||||
|
|
||||||
|
从用户输入中提取:
|
||||||
|
|
||||||
|
```python
|
||||||
|
task_params = {
|
||||||
|
"platforms": [], # 目标平台列表
|
||||||
|
"keywords": [], # 搜索关键词 / 品类
|
||||||
|
"competitor_urls": [], # 指定竞品链接(可选,优先级高于关键词搜索)
|
||||||
|
"our_prices": {}, # 我方价格对照表(可选)
|
||||||
|
"max_items": 20, # 每个平台最多采集条数
|
||||||
|
"output_format": "csv",# csv / json / markdown / hindsight
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: 平台路由 — 自动选择最佳采集方法
|
||||||
|
|
||||||
|
```python
|
||||||
|
from hermes_tools import terminal, web_extract
|
||||||
|
|
||||||
|
def scrape_platform(platform: str, keyword: str, max_items: int = 20):
|
||||||
|
"""根据平台自动选择最佳采集方法"""
|
||||||
|
|
||||||
|
if platform == "1688":
|
||||||
|
return scrape_1688_firecrawl(keyword, max_items)
|
||||||
|
elif platform == "taobao":
|
||||||
|
return scrape_taobao_camoufox(keyword, max_items)
|
||||||
|
elif platform == "jd":
|
||||||
|
return scrape_jd_jina(keyword, max_items)
|
||||||
|
elif platform == "ozon":
|
||||||
|
return scrape_ozon_camoufox(keyword, max_items)
|
||||||
|
elif platform == "temu":
|
||||||
|
return scrape_temu_jina(keyword, max_items)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: 数据标准化
|
||||||
|
|
||||||
|
所有平台采集结果统一为:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class CompetitorPrice:
|
||||||
|
platform: str # 平台名
|
||||||
|
product_name: str # 商品名称
|
||||||
|
brand: str # 品牌
|
||||||
|
price: float # 价格(统一为人民币)
|
||||||
|
currency: str # 原始币种
|
||||||
|
sales_volume: int # 销量(估算)
|
||||||
|
rating: float # 评分
|
||||||
|
shop_name: str # 店铺名
|
||||||
|
product_url: str # 商品链接
|
||||||
|
image_url: str # 主图链接
|
||||||
|
scraped_at: str # 采集时间 ISO8601
|
||||||
|
shipping_info: str # 运费/物流信息
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: 价格对比分析
|
||||||
|
|
||||||
|
如果提供了我方价格:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def compare_prices(our_items: list, competitor_items: list):
|
||||||
|
"""对比分析,输出差距和调整建议"""
|
||||||
|
for our in our_items:
|
||||||
|
matches = find_similar_products(our["name"], competitor_items)
|
||||||
|
for match in matches:
|
||||||
|
diff_pct = (match.price - our["price"]) / our["price"] * 100
|
||||||
|
status = "⬆️ 我方偏低" if diff_pct > 5 else ("⬇️ 我方偏高" if diff_pct < -5 else "≈ 持平")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: 报告生成
|
||||||
|
|
||||||
|
输出到 `/tmp/competitor_price_report_{timestamp}.{format}`:
|
||||||
|
|
||||||
|
**CSV 列:** 平台, 商品名称, 品牌, 价格(¥), 销量, 评分, 店铺, 链接, 采集时间, 与我市价差%
|
||||||
|
|
||||||
|
**Markdown 报告包含:**
|
||||||
|
- 概览摘要(各平台均价、价格区间)
|
||||||
|
- 详细对比表
|
||||||
|
- 调价建议(偏高/偏低商品清单)
|
||||||
|
- 趋势标注(如为定时监控)
|
||||||
|
|
||||||
|
## 各平台具体采集方案
|
||||||
|
|
||||||
|
### 1688 — Firecrawl API(首选)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# API Key: fc-5536880adfa44fb78a3bd9da8dd52b53
|
||||||
|
import requests, json, re
|
||||||
|
|
||||||
|
FIRECRAWL_KEY = "fc-5536880adfa44fb78a3bd9da8dd52b53"
|
||||||
|
|
||||||
|
def scrape_1688_firecrawl(keyword: str, max_items: int = 20):
|
||||||
|
url = f"https://s.1688.com/s/company_offer_page/0_-1_-1.html?keyword={keyword}"
|
||||||
|
|
||||||
|
resp = requests.post("https://api.firecrawl.dev/v1/scrape", json={
|
||||||
|
"url": url,
|
||||||
|
"formats": ["markdown"],
|
||||||
|
"waitFor": 5000,
|
||||||
|
}, headers={
|
||||||
|
"Authorization": f"Bearer {FIRECRAWL_KEY}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
})
|
||||||
|
|
||||||
|
markdown = resp.json()["data"]["markdown"]
|
||||||
|
products = parse_1688_markdown(markdown)
|
||||||
|
return products[:max_items]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 淘宝 — CamouFox 隐身浏览器
|
||||||
|
|
||||||
|
```python
|
||||||
|
from camoufox.sync_api import Camoufox
|
||||||
|
|
||||||
|
def scrape_taobao_camoufox(keyword: str, max_items: int = 20):
|
||||||
|
with Camoufox(headless=True, geoip=True, humanize=True,
|
||||||
|
proxy={"server": "http://RESIDENTIAL_PROXY:PORT"}) as browser:
|
||||||
|
page = browser.new_page()
|
||||||
|
page.goto(f"https://s.taobao.com/search?q={keyword}", timeout=30000)
|
||||||
|
page.wait_for_selector(".item.J_MouserOnverReq", timeout=15000)
|
||||||
|
|
||||||
|
items = page.query_selector_all(".item.J_MouserOnverReq")
|
||||||
|
results = []
|
||||||
|
for item in items[:max_items]:
|
||||||
|
results.append({
|
||||||
|
"product_name": item.query_selector(".title").inner_text(),
|
||||||
|
"price": extract_price(item.query_selector(".price").inner_text()),
|
||||||
|
"shop_name": item.query_selector(".shopname").inner_text(),
|
||||||
|
"sales_volume": extract_sales(item.query_selector(".deal-cnt").inner_text()),
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意:** 淘宝需要国内住宅代理,服务器 IP 会被直接封。如无可用代理,降级为 Jina Reader 尝试(成功率约 40%)。
|
||||||
|
|
||||||
|
### 京东 — Jina Reader(快速)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def scrape_jd_jina(keyword: str, max_items: int = 20):
|
||||||
|
url = f"https://search.jd.com/Search?keyword={keyword}&enc=utf-8"
|
||||||
|
resp = requests.get(
|
||||||
|
f"https://r.jina.ai/{url}",
|
||||||
|
headers={"Accept": "text/plain", "X-With-Links-Summary": "true"}
|
||||||
|
)
|
||||||
|
# 从 Markdown 提取商品信息
|
||||||
|
return parse_jd_markdown(resp.text)[:max_items]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ozon — CamouFox + 俄罗斯代理
|
||||||
|
|
||||||
|
```python
|
||||||
|
def scrape_ozon_camoufox(keyword: str, max_items: int = 20):
|
||||||
|
with Camoufox(headless=True, humanize=True,
|
||||||
|
proxy={"server": "socks5://RU_PROXY:1080"}) as browser:
|
||||||
|
page = browser.new_page()
|
||||||
|
page.goto(f"https://www.ozon.ru/search/?text={keyword}", timeout=30000)
|
||||||
|
# 处理滑块验证码(如触发)
|
||||||
|
# 提取商品卡片
|
||||||
|
items = page.query_selector_all("[data-widget='searchResultsV2'] > div")
|
||||||
|
results = []
|
||||||
|
for item in items[:max_items]:
|
||||||
|
price_text = item.query_selector("[data-widget='price']").inner_text()
|
||||||
|
results.append({
|
||||||
|
"product_name": item.query_selector("a.tile-hover-target").inner_text(),
|
||||||
|
"price_rub": extract_number(price_text),
|
||||||
|
"price_cny": convert_rub_to_cny(extract_number(price_text)),
|
||||||
|
"rating": extract_rating(item),
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
```
|
||||||
|
|
||||||
|
### Temu — Jina Reader / web_extract(轻量快速)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def scrape_temu_web_extract(keyword: str, max_items: int = 20):
|
||||||
|
"""Temu 页面用 web_extract 直接抓取"""
|
||||||
|
from hermes_tools import web_extract
|
||||||
|
results = web_extract([f"https://www.temu.com/search_result.html?search_key={keyword}"])
|
||||||
|
return parse_temu_markdown(results["results"][0]["content"])[:max_items]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 定时监控(Cron Job)
|
||||||
|
|
||||||
|
创建定期监控任务,每日/每周自动执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 通过 Hermes cron 创建
|
||||||
|
# 示例:每天早 8 点监控 1688 宠物用品竞品价格
|
||||||
|
cronjob create --name "宠物用品竞品监控" \
|
||||||
|
--schedule "0 8 * * *" \
|
||||||
|
--prompt "加载 competitor-price-monitor skill,监控 1688 和 Temu 上「宠物用品」品类的竞品价格,对比我方价格表 /tmp/our_pet_prices.json,生成对比报告并发送到钉钉" \
|
||||||
|
--deliver "dingtalk"
|
||||||
|
```
|
||||||
|
|
||||||
|
**推荐监控频率:**
|
||||||
|
- 日更品类:快消品、数码配件(每天)
|
||||||
|
- 周更品类:家居、工具、服装(每周一)
|
||||||
|
- 事件驱动:大促前后、竞品上新时(手动触发)
|
||||||
|
|
||||||
|
## 代理资源池
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 国内代理(1688/淘宝)
|
||||||
|
PROXY_CN = "http://RESIDENTIAL_PROXY_CN:PORT"
|
||||||
|
|
||||||
|
# 俄罗斯代理(Ozon)
|
||||||
|
PROXY_RU = "socks5://RESIDENTIAL_PROXY_RU:1080"
|
||||||
|
|
||||||
|
# 免费代理回退(低可靠性,仅京东等低反爬平台)
|
||||||
|
def get_free_proxy():
|
||||||
|
resp = requests.get(
|
||||||
|
"https://api.proxyscrape.com/v2/?request=displayproxies&protocol=http&timeout=5000&country=all"
|
||||||
|
)
|
||||||
|
proxies = resp.text.strip().split("\r\n")
|
||||||
|
return random.choice(proxies) if proxies else None
|
||||||
|
```
|
||||||
|
|
||||||
|
> **注意:** 免费代理可用率约 5-6%,寿命小时级。重要采集使用付费住宅代理或 Firecrawl。
|
||||||
|
|
||||||
|
## 输出文件规范
|
||||||
|
|
||||||
|
| 文件 | 路径 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 原始数据 JSON | `/tmp/competitor_raw_{ts}.json` | 完整采集数据 |
|
||||||
|
| CSV 报告 | `/tmp/competitor_report_{ts}.csv` | Excel 可直接打开 |
|
||||||
|
| Markdown 报告 | `/tmp/competitor_report_{ts}.md` | 含分析建议 |
|
||||||
|
| Hindsight 存档 | hgents05.9webs.online | 长期存储(>7天数据) |
|
||||||
|
|
||||||
|
## 快速上手示例
|
||||||
|
|
||||||
|
### 示例 1:单平台竞品扫描
|
||||||
|
|
||||||
|
> "监控 1688 上「蓝牙耳机」竞品价格,前 20 个"
|
||||||
|
|
||||||
|
Agent 自动:Firecrawl → 解析 → 标准化 → CSV 报告
|
||||||
|
|
||||||
|
### 示例 2:跨平台比价
|
||||||
|
|
||||||
|
> "对比 1688、淘宝、京东上「厨房置物架」的价格,输出对比表"
|
||||||
|
|
||||||
|
Agent 自动:三平台并行采集 → 统一货币 → 对比分析 → Markdown 报告
|
||||||
|
|
||||||
|
### 示例 3:我方价格对标
|
||||||
|
|
||||||
|
> "监控 Ozon 上宠物用品竞品价格,跟我方价格表对比,标出偏高/偏低的"
|
||||||
|
|
||||||
|
Agent 自动:读取我方价格文件 → 采集竞品 → 逐项对比 → 调价建议
|
||||||
|
|
||||||
|
### 示例 4:定时监控
|
||||||
|
|
||||||
|
> "建一个 cron,每天早上 8 点监控 1688 和 Temu 日用百货竞品价格"
|
||||||
|
|
||||||
|
Agent 自动:创建 cron job → 定时执行 → 报告推送到钉钉
|
||||||
|
|
||||||
|
## 关键 Pitfalls
|
||||||
|
|
||||||
|
1. **1688 反爬是第一优先级难题** — 不要用浏览器直接访问(IP 秒封),必须走 Firecrawl API 或 CamouFox + 国内住宅代理
|
||||||
|
2. **淘宝搜索结果页 JS 渲染复杂** — Jina Reader 对淘宝成功率仅约 40%;CamouFox + 代理是唯一稳定方案,但需要可用的国内住宅代理
|
||||||
|
3. **Ozon 有滑块 CAPTCHA** — CamouFox 需要 `humanize=True`,代理必须是俄罗斯 IP
|
||||||
|
4. **代理是消耗品** — 免费代理随时可能失效,关键任务要有 fallback 机制
|
||||||
|
5. **价格需要统一币种** — 1688/淘宝/京东是 CNY,Ozon 是 RUB,Temu 是 USD/CNY,必须归一化
|
||||||
|
6. **避免频繁请求** — 每个平台请求间隔至少 3-5 秒,必要时加随机延迟
|
||||||
|
7. **Temu 页面结构频繁变化** — 选择器需要定期维护,建议用 web_extract + LLM 解析代替固定 CSS selector
|
||||||
|
8. **数据时效性** — 采集时间必须记录,7 天前的价格参考价值大幅降低,超过 30 天的数据归档到 Hindsight
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- Python: `requests`, `re`, `json`, `datetime`, `csv`, `camoufox`, `scrapling`
|
||||||
|
- API Keys: Firecrawl (`fc-5536880adfa44fb78a3bd9da8dd52b53`)
|
||||||
|
- 代理: 国内住宅代理(1688/淘宝),俄罗斯 SOCKS5(Ozon)
|
||||||
|
- Hermes 集成: `web_extract`, `cronjob`, `send_message`(钉钉)
|
||||||
Reference in New Issue
Block a user