storewright-catalog-scout 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- storewright_catalog_scout-0.1.0/.env.example +48 -0
- storewright_catalog_scout-0.1.0/.gitignore +55 -0
- storewright_catalog_scout-0.1.0/CHANGELOG.md +11 -0
- storewright_catalog_scout-0.1.0/LICENSE +21 -0
- storewright_catalog_scout-0.1.0/PKG-INFO +252 -0
- storewright_catalog_scout-0.1.0/README.md +216 -0
- storewright_catalog_scout-0.1.0/RELEASING.md +31 -0
- storewright_catalog_scout-0.1.0/SPEC.md +119 -0
- storewright_catalog_scout-0.1.0/alembic.ini +30 -0
- storewright_catalog_scout-0.1.0/examples/shops.example.csv +2 -0
- storewright_catalog_scout-0.1.0/pyproject.toml +104 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/__init__.py +3 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/adapters/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/adapters/base.py +17 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/adapters/taobao.py +558 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/alembic/__init__.py +5 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/alembic/env.py +35 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/alembic/versions/0001_initial.py +25 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/alembic/versions/0002_price_details.py +35 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/alembic/versions/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/cdp.py +182 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/chrome.py +90 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/llm_factory.py +25 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/navigator.py +55 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/page_state.py +39 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/browser/prompts.py +23 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/cli.py +393 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/config.py +85 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/constants.py +8 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/db/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/db/base.py +20 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/db/migrations.py +22 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/db/models.py +254 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/db/repositories.py +615 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/db/session.py +14 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/domain/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/domain/enums.py +90 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/domain/models.py +174 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/domain/protocols.py +40 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/exceptions.py +22 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/extraction/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/extraction/category.py +26 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/extraction/image_url.py +18 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/extraction/url_normalizer.py +40 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/images/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/images/processor.py +75 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/logging.py +20 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/matching/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/matching/rule_engine.py +148 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/matching/shop_policy.py +108 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/orchestration/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/orchestration/archive_rebuilder.py +201 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/orchestration/catalog.py +74 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/orchestration/run_service.py +774 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/reporting/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/reporting/report.py +194 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/reporting/templates/report.html.j2 +51 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/sampling/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/sampling/sampler.py +19 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/vision/__init__.py +1 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/vision/factory.py +14 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/vision/mock.py +78 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/vision/parser.py +65 -0
- storewright_catalog_scout-0.1.0/src/storewright_catalog_scout/vision/serpapi.py +194 -0
- storewright_catalog_scout-0.1.0/tests/conftest.py +15 -0
- storewright_catalog_scout-0.1.0/tests/fixtures/detail/json_ld.html +3 -0
- storewright_catalog_scout-0.1.0/tests/fixtures/detail/og.html +4 -0
- storewright_catalog_scout-0.1.0/tests/fixtures/listing/lazy_listing.html +6 -0
- storewright_catalog_scout-0.1.0/tests/integration/test_mock_e2e.py +297 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_category.py +13 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_cli.py +57 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_images.py +59 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_llm_factory.py +51 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_migrations.py +17 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_page_state.py +14 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_sampling.py +18 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_serpapi_provider.py +205 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_shop_policy.py +60 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_urls_and_adapter.py +210 -0
- storewright_catalog_scout-0.1.0/tests/unit/test_vision_and_rules.py +100 -0
- storewright_catalog_scout-0.1.0/uv.lock +4837 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Application
|
|
2
|
+
APP_ENV=development
|
|
3
|
+
DATABASE_URL=sqlite+aiosqlite:///./runtime/storewright_catalog_scout.db
|
|
4
|
+
ARTIFACTS_DIR=./runtime/artifacts
|
|
5
|
+
|
|
6
|
+
# Chrome / CDP
|
|
7
|
+
CHROME_EXECUTABLE=
|
|
8
|
+
CHROME_USER_DATA_DIR=./runtime/chrome-profile
|
|
9
|
+
CHROME_REMOTE_DEBUGGING_HOST=127.0.0.1
|
|
10
|
+
CHROME_REMOTE_DEBUGGING_PORT=9222
|
|
11
|
+
CHROME_HEADLESS=false
|
|
12
|
+
CHROME_START_TIMEOUT_SECONDS=20
|
|
13
|
+
|
|
14
|
+
# Browser Use
|
|
15
|
+
BROWSER_USE_API_KEY=
|
|
16
|
+
BROWSER_USE_PROVIDER=deepseek
|
|
17
|
+
BROWSER_USE_MODEL=deepseek-chat
|
|
18
|
+
BROWSER_AGENT_MAX_STEPS=20
|
|
19
|
+
BROWSER_USE_VISION_MODE=false
|
|
20
|
+
DEEPSEEK_API_KEY=
|
|
21
|
+
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
|
|
22
|
+
|
|
23
|
+
# Image search (SerpApi Google Lens)
|
|
24
|
+
# Multiple keys, separated by commas.
|
|
25
|
+
SERPAPI_API_KEYS=
|
|
26
|
+
VISION_TIMEOUT_SECONDS=30
|
|
27
|
+
VISION_CONCURRENCY=3
|
|
28
|
+
|
|
29
|
+
# Discovery and extraction
|
|
30
|
+
MAX_POOL_SIZE=2000
|
|
31
|
+
MAX_SCROLL_ROUNDS=50
|
|
32
|
+
STABLE_SCROLL_ROUNDS=3
|
|
33
|
+
PAGE_ACTION_DELAY_MS=1200
|
|
34
|
+
NAVIGATION_TIMEOUT_MS=30000
|
|
35
|
+
MAX_IMAGE_BYTES=15728640
|
|
36
|
+
MIN_IMAGE_WIDTH=300
|
|
37
|
+
MIN_IMAGE_HEIGHT=300
|
|
38
|
+
|
|
39
|
+
# Streaming decision policy
|
|
40
|
+
MAX_QUALIFIED_PRODUCTS_PER_CATEGORY=20
|
|
41
|
+
SHOP_REJECT_RATE_THRESHOLD=0.60
|
|
42
|
+
EARLY_STOP_MIN_SEARCHES=10
|
|
43
|
+
EARLY_STOP_CONFIDENCE=0.90
|
|
44
|
+
MAX_SEARCH_ERROR_RATE=0.20
|
|
45
|
+
EARLY_STOP_ON_REJECT=true
|
|
46
|
+
MAX_DETAIL_PRODUCTS_PER_BATCH=0
|
|
47
|
+
DETAIL_PAGE_INTERVAL_SECONDS=30
|
|
48
|
+
PAUSE_AFTER_SCREENING=false
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.pyright/
|
|
7
|
+
.mypy_cache/
|
|
8
|
+
.hypothesis/
|
|
9
|
+
.coverage
|
|
10
|
+
htmlcov/
|
|
11
|
+
.venv/
|
|
12
|
+
build/
|
|
13
|
+
dist/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
|
|
16
|
+
# Environment and credentials
|
|
17
|
+
.env
|
|
18
|
+
.env.*
|
|
19
|
+
!.env.example
|
|
20
|
+
*.pem
|
|
21
|
+
*.key
|
|
22
|
+
*.p12
|
|
23
|
+
*service-account*.json
|
|
24
|
+
application_default_credentials.json
|
|
25
|
+
client_secret*.json
|
|
26
|
+
credentials*.json
|
|
27
|
+
|
|
28
|
+
# Runtime state and sensitive browser data
|
|
29
|
+
runtime/
|
|
30
|
+
shops*.csv
|
|
31
|
+
!examples/shops.example.csv
|
|
32
|
+
chrome-profile/
|
|
33
|
+
auth.json
|
|
34
|
+
storage_state*.json
|
|
35
|
+
*.sqlite
|
|
36
|
+
*.sqlite-*
|
|
37
|
+
*.db
|
|
38
|
+
*.db-*
|
|
39
|
+
uv-cache/
|
|
40
|
+
|
|
41
|
+
# Generated evidence
|
|
42
|
+
artifacts/
|
|
43
|
+
screenshots/
|
|
44
|
+
downloads/
|
|
45
|
+
recordings/
|
|
46
|
+
traces/
|
|
47
|
+
*.log
|
|
48
|
+
*.har
|
|
49
|
+
test-results/
|
|
50
|
+
|
|
51
|
+
# OS / IDE
|
|
52
|
+
.DS_Store
|
|
53
|
+
Thumbs.db
|
|
54
|
+
.idea/
|
|
55
|
+
.vscode/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to StoreWright Catalog Scout are documented here.
|
|
4
|
+
|
|
5
|
+
## 0.1.0 - 2026-07-19
|
|
6
|
+
|
|
7
|
+
- Add the auditable and resumable product-screening pipeline.
|
|
8
|
+
- Add Taobao and Tmall catalog discovery and detail extraction.
|
|
9
|
+
- Add SerpApi Google Lens exact-match screening with API key-pool failover.
|
|
10
|
+
- Add deterministic category quotas, shop early stopping, reports, and archive rebuilding.
|
|
11
|
+
- Publish the `storewright-scout` CLI as the `storewright-catalog-scout` package.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hugh Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: storewright-catalog-scout
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Auditable, resumable external image-match screening for authorized shops
|
|
5
|
+
Project-URL: Changelog, https://github.com/HughLee824/storewright/releases
|
|
6
|
+
Project-URL: Homepage, https://github.com/HughLee824/storewright
|
|
7
|
+
Project-URL: Issues, https://github.com/HughLee824/storewright/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/HughLee824/storewright
|
|
9
|
+
Author: Hugh Lee
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ecommerce,google-lens,product-research,serpapi
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Requires-Python: <3.14,>=3.12
|
|
20
|
+
Requires-Dist: aiosqlite<1,>=0.20
|
|
21
|
+
Requires-Dist: alembic<2,>=1.13
|
|
22
|
+
Requires-Dist: browser-use==0.13.6
|
|
23
|
+
Requires-Dist: httpx<1,>=0.27
|
|
24
|
+
Requires-Dist: imagehash<5,>=4.3
|
|
25
|
+
Requires-Dist: jinja2<4,>=3.1
|
|
26
|
+
Requires-Dist: pillow<13,>=11
|
|
27
|
+
Requires-Dist: playwright<2,>=1.50
|
|
28
|
+
Requires-Dist: pydantic-settings<3,>=2
|
|
29
|
+
Requires-Dist: pydantic<3,>=2
|
|
30
|
+
Requires-Dist: rich<15,>=13
|
|
31
|
+
Requires-Dist: sqlalchemy<3,>=2
|
|
32
|
+
Requires-Dist: structlog<27,>=24
|
|
33
|
+
Requires-Dist: tldextract<6,>=5
|
|
34
|
+
Requires-Dist: typer<1,>=0.12
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# StoreWright Catalog Scout
|
|
38
|
+
|
|
39
|
+
StoreWright Catalog Scout 是面向已授权店铺数据的商品图片筛选与本地归档工具。它先用商品列表图调用 SerpApi Google Lens 精确匹配,再决定是否读取并保存完整商品详情;按类目最多保留指定数量的合格商品,并在店铺淘汰率显著过高时提前停止,减少 API、页面访问和存储消耗。
|
|
40
|
+
|
|
41
|
+
淘宝/天猫是首个数据源。编排、图片检索、判定、数据库和报告均通过通用接口实现,不把淘宝写死为唯一来源。
|
|
42
|
+
|
|
43
|
+
> 工具不会绕过登录、验证码、滑块或平台风控,不包含 stealth、代理轮换或指纹伪装。真实任务只可用于你拥有、控制或明确获授权访问的数据。SerpApi 会收到公开商品图片 URL;仅在机械导航失败时,页面状态才可能发送给配置的 Browser Use/DeepSeek 模型。
|
|
44
|
+
|
|
45
|
+
## 工作流
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
shops.csv
|
|
49
|
+
→ 发现并确定性打乱商品
|
|
50
|
+
→ 列表图 Serp 精确匹配
|
|
51
|
+
→ 外部精确匹配:淘汰,只保留最小审计证据
|
|
52
|
+
→ 无精确匹配:保存 screened_qualified,不立即打开详情
|
|
53
|
+
→ 每次成功判断后更新店铺淘汰率
|
|
54
|
+
→ Wilson 下界超过阈值:提前淘汰店铺并停止剩余处理
|
|
55
|
+
→ 全店列表图预筛完成后,按标题临时分类并限制候选数
|
|
56
|
+
→ 候选详情页固定间隔 30 秒,每件完成后立即持久化检查点
|
|
57
|
+
→ 详情主图变化:再次 Serp
|
|
58
|
+
→ 类目未满:保存完整详情和可识别商品图
|
|
59
|
+
→ 登录/验证/阻断页:立即 paused,不自动重试
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
核心决策全部是确定性规则。DeepSeek 只在固定商品列表 URL 无法直接打开时作为导航后备,不参与逐商品判定。
|
|
63
|
+
|
|
64
|
+
## 安装
|
|
65
|
+
|
|
66
|
+
需要本机 Chrome。推荐使用 `uv tool` 安装;`uv` 会为命令创建隔离环境,并在需要时安装兼容的 Python。
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uv tool install storewright-catalog-scout
|
|
70
|
+
mkdir catalog-scout-workspace
|
|
71
|
+
cd catalog-scout-workspace
|
|
72
|
+
storewright-scout init
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`init` 会创建当前工作目录下的 `.env`、SQLite 数据库和运行目录,不会覆盖已有 `.env`。填写至少一个 SerpApi Key 后即可运行。升级使用:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv tool upgrade storewright-catalog-scout
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 输入 CSV
|
|
82
|
+
|
|
83
|
+
`shops.csv` 只需要一个字段,可提供多个店铺:
|
|
84
|
+
|
|
85
|
+
```csv
|
|
86
|
+
shop_url
|
|
87
|
+
https://shop-a.taobao.com/
|
|
88
|
+
https://shop-b.tmall.com/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
空 URL 会报错,重复 URL 会去重。店铺名称和 ID 自动识别。
|
|
92
|
+
|
|
93
|
+
## 配置
|
|
94
|
+
|
|
95
|
+
关键配置:
|
|
96
|
+
|
|
97
|
+
```env
|
|
98
|
+
SERPAPI_API_KEYS=key-a,key-b,key-c
|
|
99
|
+
|
|
100
|
+
MAX_POOL_SIZE=2000
|
|
101
|
+
MAX_QUALIFIED_PRODUCTS_PER_CATEGORY=20
|
|
102
|
+
SHOP_REJECT_RATE_THRESHOLD=0.60
|
|
103
|
+
EARLY_STOP_MIN_SEARCHES=10
|
|
104
|
+
EARLY_STOP_CONFIDENCE=0.90
|
|
105
|
+
MAX_SEARCH_ERROR_RATE=0.20
|
|
106
|
+
MAX_DETAIL_PRODUCTS_PER_BATCH=0 # 0 表示本次命令不中途按数量暂停
|
|
107
|
+
DETAIL_PAGE_INTERVAL_SECONDS=30
|
|
108
|
+
PAUSE_AFTER_SCREENING=false
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
每次 SerpApi 查询都会随机排列 Key 池并选择一个 Key。若该 Key 鉴权失败、额度耗尽或触发限流,程序会自动尝试池内其他 Key;仅当全部 Key 均不可用时,当前查询才失败。配置中的空项和重复 Key 会自动清理。
|
|
112
|
+
|
|
113
|
+
淘汰率只使用成功判定作为分母:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
精确匹配淘汰数 / 成功图片检索数
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
至少成功搜索 10 件后才允许提前淘汰。默认使用 90% Wilson 置信区间,下界达到 60% 才停止;最终完成时实际淘汰率达到 60% 也会淘汰店铺。检索错误率超过 20% 或商品发现被安全上限截断时进入人工复核。
|
|
120
|
+
|
|
121
|
+
### DeepSeek 导航后备
|
|
122
|
+
|
|
123
|
+
```env
|
|
124
|
+
BROWSER_USE_PROVIDER=deepseek
|
|
125
|
+
BROWSER_USE_MODEL=deepseek-chat
|
|
126
|
+
BROWSER_USE_VISION_MODE=false
|
|
127
|
+
DEEPSEEK_API_KEY=your-key
|
|
128
|
+
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
程序优先通过 SourceAdapter 生成商品列表 URL,并用 Playwright 直接打开和机械验证。只有失败时才调用 Browser Use + DeepSeek。
|
|
132
|
+
|
|
133
|
+
## 运行
|
|
134
|
+
|
|
135
|
+
先在专用 Chrome Profile 手工登录并诊断:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
storewright-scout browser login
|
|
139
|
+
storewright-scout browser diagnose
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
完全离线 Mock E2E:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
storewright-scout run \
|
|
146
|
+
--shops shops.csv \
|
|
147
|
+
--seed 20260718 \
|
|
148
|
+
--mock-vision \
|
|
149
|
+
--confirm-authorized
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
真实运行:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
storewright-scout run \
|
|
156
|
+
--shops shops.csv \
|
|
157
|
+
--seed 20260718 \
|
|
158
|
+
--confirm-authorized
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
恢复与报告:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
storewright-scout resume --run-id <uuid> --confirm-authorized
|
|
165
|
+
storewright-scout report --run-id <uuid>
|
|
166
|
+
storewright-scout rebuild-archives --run-id <uuid>
|
|
167
|
+
storewright-scout review list --run-id <uuid>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`rebuild-archives` 只读取已保存的 HTML,离线补齐结构化价格、属性和 SKU,重建扁平图片目录,不启动浏览器或调用 SerpApi。
|
|
171
|
+
|
|
172
|
+
成功的查询按 `(provider, normalized_image_sha256, variant)` 缓存;相同图片不会重复消耗 SerpApi。处理顺序由运行 seed 和店铺 canonical key 决定,恢复后保持不变。
|
|
173
|
+
|
|
174
|
+
一次已授权的 `run/resume` 默认连续处理剩余详情,不要求每 5 件重新授权。每件商品独立提交 SQLite,因此仍可从任意检查点恢复。只有显式配置正数 `MAX_DETAIL_PRODUCTS_PER_BATCH`、检测到登录/验证/阻断页或发生真实错误时才暂停;程序不会尝试处理安全验证。
|
|
175
|
+
|
|
176
|
+
## 本地输出
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
runtime/artifacts/<run_id>/
|
|
180
|
+
├── shops.csv
|
|
181
|
+
├── products.csv
|
|
182
|
+
├── summary.json
|
|
183
|
+
├── report.html
|
|
184
|
+
├── vision/
|
|
185
|
+
└── shops/<shop_key>/products/<item_id>/
|
|
186
|
+
├── screening-listing/
|
|
187
|
+
├── screening-detail/
|
|
188
|
+
├── product.json # 仅合格且类目未满的商品
|
|
189
|
+
├── images/ # 001-main.jpg、002-gallery.jpg、003-sku.jpg ...
|
|
190
|
+
└── evidence/
|
|
191
|
+
├── raw.html
|
|
192
|
+
├── image-sources.json
|
|
193
|
+
└── original-images/
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`images/` 只包含按结构化来源筛选、SHA-256/pHash 去重且通过尺寸校验的商品图,不创建逐图子目录。原始页面和图片来源信息放在 `evidence/`,避免干扰人工挑图。
|
|
197
|
+
|
|
198
|
+
被淘汰商品保留商品 ID、URL、列表图和 Serp 证据,不保存完整详情资产,因此不需要删除文件。商品状态包括:
|
|
199
|
+
|
|
200
|
+
- `qualified`
|
|
201
|
+
- `screened_qualified`
|
|
202
|
+
- `rejected`
|
|
203
|
+
- `review`
|
|
204
|
+
- `skipped_category_quota_reached`
|
|
205
|
+
- `skipped_after_shop_rejected`
|
|
206
|
+
|
|
207
|
+
## 判定边界
|
|
208
|
+
|
|
209
|
+
- 自身商品、同店页面和图片 CDN 不作为外部精确匹配。
|
|
210
|
+
- 外部页面完整图片匹配会淘汰商品。
|
|
211
|
+
- 无法映射到外部页面的完整图、局部匹配和搜索错误进入复核。
|
|
212
|
+
- `NO_INDEXED_MATCH_FOUND` 只表示当前 provider 没有返回精确匹配,不证明互联网不存在同款。
|
|
213
|
+
- 图片匹配不证明 SKU、材质、质量或知识产权相同。
|
|
214
|
+
|
|
215
|
+
## 扩展其他平台
|
|
216
|
+
|
|
217
|
+
新增数据源时实现 `SourceAdapter` 与 `CatalogBackend`:
|
|
218
|
+
|
|
219
|
+
- 店铺和商品 URL 识别;
|
|
220
|
+
- 商品列表入口与 DOM 提取;
|
|
221
|
+
- 商品详情结构化解析;
|
|
222
|
+
- 自身商品/同店/外部 URL 关系分类;
|
|
223
|
+
- 页面和图片获取。
|
|
224
|
+
|
|
225
|
+
Serp provider、规则引擎、类目配额、店铺止损、SQLite 与报告无需依赖淘宝。
|
|
226
|
+
|
|
227
|
+
## 源码开发
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
git clone https://github.com/HughLee824/storewright.git
|
|
231
|
+
cd storewright/tools/product-research/catalog-scout
|
|
232
|
+
uv sync
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 质量检查
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
uv run ruff check .
|
|
239
|
+
uv run pyright
|
|
240
|
+
uv run pytest --cov=storewright_catalog_scout
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
发布流程见 [`RELEASING.md`](https://github.com/HughLee824/storewright/blob/main/tools/product-research/catalog-scout/RELEASING.md),版本变化见 [`CHANGELOG.md`](https://github.com/HughLee824/storewright/blob/main/tools/product-research/catalog-scout/CHANGELOG.md)。本项目使用 [MIT License](https://github.com/HughLee824/storewright/blob/main/tools/product-research/catalog-scout/LICENSE)。
|
|
244
|
+
|
|
245
|
+
## 已知限制
|
|
246
|
+
|
|
247
|
+
- 列表图与详情主图不同会多使用一次 Serp 查询。
|
|
248
|
+
- 所有列表图预筛先完成,详情页只按受控速率访问;公开图片下载使用独立 HTTP 客户端,不携带浏览器 Cookie。
|
|
249
|
+
- 类目通常在详情页才能可靠获得,因此部分已通过预筛的商品仍需轻量读取详情后才能判断类目名额。
|
|
250
|
+
- 页面可识别字段会随平台模板变化;原始 HTML 和结构化数据会随合格商品一起保存,便于重新解析。
|
|
251
|
+
- 达到 `MAX_POOL_SIZE` 会标记目录不完整并进入复核,不会声称覆盖全店。
|
|
252
|
+
- 不执行购买、收藏、联系卖家或 Shopify 导入。
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# StoreWright Catalog Scout
|
|
2
|
+
|
|
3
|
+
StoreWright Catalog Scout 是面向已授权店铺数据的商品图片筛选与本地归档工具。它先用商品列表图调用 SerpApi Google Lens 精确匹配,再决定是否读取并保存完整商品详情;按类目最多保留指定数量的合格商品,并在店铺淘汰率显著过高时提前停止,减少 API、页面访问和存储消耗。
|
|
4
|
+
|
|
5
|
+
淘宝/天猫是首个数据源。编排、图片检索、判定、数据库和报告均通过通用接口实现,不把淘宝写死为唯一来源。
|
|
6
|
+
|
|
7
|
+
> 工具不会绕过登录、验证码、滑块或平台风控,不包含 stealth、代理轮换或指纹伪装。真实任务只可用于你拥有、控制或明确获授权访问的数据。SerpApi 会收到公开商品图片 URL;仅在机械导航失败时,页面状态才可能发送给配置的 Browser Use/DeepSeek 模型。
|
|
8
|
+
|
|
9
|
+
## 工作流
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
shops.csv
|
|
13
|
+
→ 发现并确定性打乱商品
|
|
14
|
+
→ 列表图 Serp 精确匹配
|
|
15
|
+
→ 外部精确匹配:淘汰,只保留最小审计证据
|
|
16
|
+
→ 无精确匹配:保存 screened_qualified,不立即打开详情
|
|
17
|
+
→ 每次成功判断后更新店铺淘汰率
|
|
18
|
+
→ Wilson 下界超过阈值:提前淘汰店铺并停止剩余处理
|
|
19
|
+
→ 全店列表图预筛完成后,按标题临时分类并限制候选数
|
|
20
|
+
→ 候选详情页固定间隔 30 秒,每件完成后立即持久化检查点
|
|
21
|
+
→ 详情主图变化:再次 Serp
|
|
22
|
+
→ 类目未满:保存完整详情和可识别商品图
|
|
23
|
+
→ 登录/验证/阻断页:立即 paused,不自动重试
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
核心决策全部是确定性规则。DeepSeek 只在固定商品列表 URL 无法直接打开时作为导航后备,不参与逐商品判定。
|
|
27
|
+
|
|
28
|
+
## 安装
|
|
29
|
+
|
|
30
|
+
需要本机 Chrome。推荐使用 `uv tool` 安装;`uv` 会为命令创建隔离环境,并在需要时安装兼容的 Python。
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv tool install storewright-catalog-scout
|
|
34
|
+
mkdir catalog-scout-workspace
|
|
35
|
+
cd catalog-scout-workspace
|
|
36
|
+
storewright-scout init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`init` 会创建当前工作目录下的 `.env`、SQLite 数据库和运行目录,不会覆盖已有 `.env`。填写至少一个 SerpApi Key 后即可运行。升级使用:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv tool upgrade storewright-catalog-scout
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 输入 CSV
|
|
46
|
+
|
|
47
|
+
`shops.csv` 只需要一个字段,可提供多个店铺:
|
|
48
|
+
|
|
49
|
+
```csv
|
|
50
|
+
shop_url
|
|
51
|
+
https://shop-a.taobao.com/
|
|
52
|
+
https://shop-b.tmall.com/
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
空 URL 会报错,重复 URL 会去重。店铺名称和 ID 自动识别。
|
|
56
|
+
|
|
57
|
+
## 配置
|
|
58
|
+
|
|
59
|
+
关键配置:
|
|
60
|
+
|
|
61
|
+
```env
|
|
62
|
+
SERPAPI_API_KEYS=key-a,key-b,key-c
|
|
63
|
+
|
|
64
|
+
MAX_POOL_SIZE=2000
|
|
65
|
+
MAX_QUALIFIED_PRODUCTS_PER_CATEGORY=20
|
|
66
|
+
SHOP_REJECT_RATE_THRESHOLD=0.60
|
|
67
|
+
EARLY_STOP_MIN_SEARCHES=10
|
|
68
|
+
EARLY_STOP_CONFIDENCE=0.90
|
|
69
|
+
MAX_SEARCH_ERROR_RATE=0.20
|
|
70
|
+
MAX_DETAIL_PRODUCTS_PER_BATCH=0 # 0 表示本次命令不中途按数量暂停
|
|
71
|
+
DETAIL_PAGE_INTERVAL_SECONDS=30
|
|
72
|
+
PAUSE_AFTER_SCREENING=false
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
每次 SerpApi 查询都会随机排列 Key 池并选择一个 Key。若该 Key 鉴权失败、额度耗尽或触发限流,程序会自动尝试池内其他 Key;仅当全部 Key 均不可用时,当前查询才失败。配置中的空项和重复 Key 会自动清理。
|
|
76
|
+
|
|
77
|
+
淘汰率只使用成功判定作为分母:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
精确匹配淘汰数 / 成功图片检索数
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
至少成功搜索 10 件后才允许提前淘汰。默认使用 90% Wilson 置信区间,下界达到 60% 才停止;最终完成时实际淘汰率达到 60% 也会淘汰店铺。检索错误率超过 20% 或商品发现被安全上限截断时进入人工复核。
|
|
84
|
+
|
|
85
|
+
### DeepSeek 导航后备
|
|
86
|
+
|
|
87
|
+
```env
|
|
88
|
+
BROWSER_USE_PROVIDER=deepseek
|
|
89
|
+
BROWSER_USE_MODEL=deepseek-chat
|
|
90
|
+
BROWSER_USE_VISION_MODE=false
|
|
91
|
+
DEEPSEEK_API_KEY=your-key
|
|
92
|
+
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
程序优先通过 SourceAdapter 生成商品列表 URL,并用 Playwright 直接打开和机械验证。只有失败时才调用 Browser Use + DeepSeek。
|
|
96
|
+
|
|
97
|
+
## 运行
|
|
98
|
+
|
|
99
|
+
先在专用 Chrome Profile 手工登录并诊断:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
storewright-scout browser login
|
|
103
|
+
storewright-scout browser diagnose
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
完全离线 Mock E2E:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
storewright-scout run \
|
|
110
|
+
--shops shops.csv \
|
|
111
|
+
--seed 20260718 \
|
|
112
|
+
--mock-vision \
|
|
113
|
+
--confirm-authorized
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
真实运行:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
storewright-scout run \
|
|
120
|
+
--shops shops.csv \
|
|
121
|
+
--seed 20260718 \
|
|
122
|
+
--confirm-authorized
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
恢复与报告:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
storewright-scout resume --run-id <uuid> --confirm-authorized
|
|
129
|
+
storewright-scout report --run-id <uuid>
|
|
130
|
+
storewright-scout rebuild-archives --run-id <uuid>
|
|
131
|
+
storewright-scout review list --run-id <uuid>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`rebuild-archives` 只读取已保存的 HTML,离线补齐结构化价格、属性和 SKU,重建扁平图片目录,不启动浏览器或调用 SerpApi。
|
|
135
|
+
|
|
136
|
+
成功的查询按 `(provider, normalized_image_sha256, variant)` 缓存;相同图片不会重复消耗 SerpApi。处理顺序由运行 seed 和店铺 canonical key 决定,恢复后保持不变。
|
|
137
|
+
|
|
138
|
+
一次已授权的 `run/resume` 默认连续处理剩余详情,不要求每 5 件重新授权。每件商品独立提交 SQLite,因此仍可从任意检查点恢复。只有显式配置正数 `MAX_DETAIL_PRODUCTS_PER_BATCH`、检测到登录/验证/阻断页或发生真实错误时才暂停;程序不会尝试处理安全验证。
|
|
139
|
+
|
|
140
|
+
## 本地输出
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
runtime/artifacts/<run_id>/
|
|
144
|
+
├── shops.csv
|
|
145
|
+
├── products.csv
|
|
146
|
+
├── summary.json
|
|
147
|
+
├── report.html
|
|
148
|
+
├── vision/
|
|
149
|
+
└── shops/<shop_key>/products/<item_id>/
|
|
150
|
+
├── screening-listing/
|
|
151
|
+
├── screening-detail/
|
|
152
|
+
├── product.json # 仅合格且类目未满的商品
|
|
153
|
+
├── images/ # 001-main.jpg、002-gallery.jpg、003-sku.jpg ...
|
|
154
|
+
└── evidence/
|
|
155
|
+
├── raw.html
|
|
156
|
+
├── image-sources.json
|
|
157
|
+
└── original-images/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`images/` 只包含按结构化来源筛选、SHA-256/pHash 去重且通过尺寸校验的商品图,不创建逐图子目录。原始页面和图片来源信息放在 `evidence/`,避免干扰人工挑图。
|
|
161
|
+
|
|
162
|
+
被淘汰商品保留商品 ID、URL、列表图和 Serp 证据,不保存完整详情资产,因此不需要删除文件。商品状态包括:
|
|
163
|
+
|
|
164
|
+
- `qualified`
|
|
165
|
+
- `screened_qualified`
|
|
166
|
+
- `rejected`
|
|
167
|
+
- `review`
|
|
168
|
+
- `skipped_category_quota_reached`
|
|
169
|
+
- `skipped_after_shop_rejected`
|
|
170
|
+
|
|
171
|
+
## 判定边界
|
|
172
|
+
|
|
173
|
+
- 自身商品、同店页面和图片 CDN 不作为外部精确匹配。
|
|
174
|
+
- 外部页面完整图片匹配会淘汰商品。
|
|
175
|
+
- 无法映射到外部页面的完整图、局部匹配和搜索错误进入复核。
|
|
176
|
+
- `NO_INDEXED_MATCH_FOUND` 只表示当前 provider 没有返回精确匹配,不证明互联网不存在同款。
|
|
177
|
+
- 图片匹配不证明 SKU、材质、质量或知识产权相同。
|
|
178
|
+
|
|
179
|
+
## 扩展其他平台
|
|
180
|
+
|
|
181
|
+
新增数据源时实现 `SourceAdapter` 与 `CatalogBackend`:
|
|
182
|
+
|
|
183
|
+
- 店铺和商品 URL 识别;
|
|
184
|
+
- 商品列表入口与 DOM 提取;
|
|
185
|
+
- 商品详情结构化解析;
|
|
186
|
+
- 自身商品/同店/外部 URL 关系分类;
|
|
187
|
+
- 页面和图片获取。
|
|
188
|
+
|
|
189
|
+
Serp provider、规则引擎、类目配额、店铺止损、SQLite 与报告无需依赖淘宝。
|
|
190
|
+
|
|
191
|
+
## 源码开发
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
git clone https://github.com/HughLee824/storewright.git
|
|
195
|
+
cd storewright/tools/product-research/catalog-scout
|
|
196
|
+
uv sync
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 质量检查
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
uv run ruff check .
|
|
203
|
+
uv run pyright
|
|
204
|
+
uv run pytest --cov=storewright_catalog_scout
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
发布流程见 [`RELEASING.md`](https://github.com/HughLee824/storewright/blob/main/tools/product-research/catalog-scout/RELEASING.md),版本变化见 [`CHANGELOG.md`](https://github.com/HughLee824/storewright/blob/main/tools/product-research/catalog-scout/CHANGELOG.md)。本项目使用 [MIT License](https://github.com/HughLee824/storewright/blob/main/tools/product-research/catalog-scout/LICENSE)。
|
|
208
|
+
|
|
209
|
+
## 已知限制
|
|
210
|
+
|
|
211
|
+
- 列表图与详情主图不同会多使用一次 Serp 查询。
|
|
212
|
+
- 所有列表图预筛先完成,详情页只按受控速率访问;公开图片下载使用独立 HTTP 客户端,不携带浏览器 Cookie。
|
|
213
|
+
- 类目通常在详情页才能可靠获得,因此部分已通过预筛的商品仍需轻量读取详情后才能判断类目名额。
|
|
214
|
+
- 页面可识别字段会随平台模板变化;原始 HTML 和结构化数据会随合格商品一起保存,便于重新解析。
|
|
215
|
+
- 达到 `MAX_POOL_SIZE` 会标记目录不完整并进入复核,不会声称覆盖全店。
|
|
216
|
+
- 不执行购买、收藏、联系卖家或 Shopify 导入。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Releasing StoreWright Catalog Scout
|
|
2
|
+
|
|
3
|
+
Releases are published from tags named `catalog-scout-v<version>`.
|
|
4
|
+
|
|
5
|
+
## One-time PyPI setup
|
|
6
|
+
|
|
7
|
+
Create a pending Trusted Publisher for:
|
|
8
|
+
|
|
9
|
+
- PyPI project: `storewright-catalog-scout`
|
|
10
|
+
- GitHub owner: `HughLee824`
|
|
11
|
+
- Repository: `storewright`
|
|
12
|
+
- Workflow: `catalog-scout-release.yml`
|
|
13
|
+
- Environment: `pypi`
|
|
14
|
+
|
|
15
|
+
Create a matching `pypi` environment in the GitHub repository. The release workflow uses
|
|
16
|
+
OpenID Connect and does not require a stored PyPI API token.
|
|
17
|
+
|
|
18
|
+
## Release
|
|
19
|
+
|
|
20
|
+
1. Update `version` in `pyproject.toml` and `CHANGELOG.md`.
|
|
21
|
+
2. Run the local quality checks documented in `README.md`.
|
|
22
|
+
3. Merge the release commit into `main`.
|
|
23
|
+
4. Create and push an annotated tag:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git tag -a catalog-scout-v0.1.0 -m "StoreWright Catalog Scout 0.1.0"
|
|
27
|
+
git push origin catalog-scout-v0.1.0
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The workflow verifies that the tag and package versions match, tests the package, builds the
|
|
31
|
+
source and wheel distributions, publishes them to PyPI, and creates the GitHub Release.
|