|
|
há 1 semana atrás | |
|---|---|---|
| .. | ||
| README.md | há 1 semana atrás | |
| YamlLoader.py | há 1 semana atrás | |
| application.yml | há 1 semana atrás | |
| ddl.sql | há 1 semana atrás | |
| mysql_pool.py | há 1 semana atrás | |
| requirements.txt | há 1 semana atrás | |
| snk_jp_spider.py | há 1 semana atrás | |
宝可梦单卡「已售出品 + 售买履历」采集。列表页(表1) + 详情页(表1) + 交易记录(表2)。
/search?keywords=Pokemon+Card+Game...&brandIds=pokemon&searchCategoryIds=6/33&sort=launch&itemConditions=...&page=1/apparels/{apparel_id}/used/{used_item_id},如 /apparels/835482/used/47574548credentials: omit 可直接访问)。仅账号相关接口(/v1/accounts/me、EN 站 trading-histories)需登录态snkrdunk_jp_used_item(二手出品 / 商品信息)列表页来源:apparel_id / used_item_id / name_ja / condition_grade(品相) / price / is_sold / primary_image_url / detail_url
详情页补全:product_id / product_number(品番) / name_en / brand / category / quantity_text(枚数) / condition_desc / sale_status / image_urls(多图逗号分割) / released_at
snkrdunk_jp_trading_history(买卖历史 / 交易记录)product_id / apparel_id / product_number / sold_price / condition_grade(品相) / quantity_text(枚数) / sold_at / trade_index_in_second
联合唯一索引 (product_id, sold_at, sold_price, condition_grade, quantity_text, trade_index_in_second) 配合 INSERT IGNORE 累积去重(详见第6节踩坑)。
建表脚本见
ddl.sql。字段一律按原意翻译、避开 MySQL 保留字(condition→condition_grade、status→sale_status); 时间字段统一gmt_create_time / gmt_modified_time,成交/发售时间转为北京时间(UTC+8)与现有管线一致。
| 用途 | 接口 | 说明 |
|---|---|---|
| 列表页(表1) | GET /search?...&page=N |
无独立 JSON 接口,数据内嵌在 SSR HTML 的 self.__next_f flight 里 |
| 详情(表1) | GET /v1/apparels/{apparel_id}/used/{used_item_id} |
返回完整 apparelUsedItem,无需 cookie |
| 交易记录(表2) | GET /v3/products/{product_id}/trading-history?range=all |
返回 trades[],无需 cookie,一次性全量 |
三个 ID 的区别(易踩坑):
apparel_id(URL 中 /apparels/{}):款式IDused_item_id(URL 中 /used/{}):某个具体二手出品IDproduct_id = 详情里的 apparel.productId(商品目录 catalog ID):交易记录接口用的是它,不是 apparel_id。需先抓详情拿到 product_id,再抓交易记录。关键字段来源:
displayCardPattern(含 SoldOut 即已售)、title、link(含 apparel_id+used_item_id)、imageUrl、salePrice、condition、favoriteCounttrades[]:price→成交价、soldAt→成交时间(UTC)、title→品相(A/B/C..)、label→枚数(1枚..)/search 是 Next.js SSR,商品卡片在客户端由 flight 数据 hydration,页面 raw HTML 里没有 <a href> 卡片 DOM,_rsc 接口又依赖动态 token。最终定位:数据以 self.__next_f.push([1,"..."]) 分片写入 HTML。json.loads('"'+p+'"') 反转义 → 拼接成完整文本 → 「花括号配平」抠出每个 {"displayCardPattern"...} 卡片对象 → json.loads。(见 _extract_flight_cards)displayCardPattern 含 SoldOut = 已售(对应网页左上角 SOLD 角标)。credentials:'omit' 实测三类接口均 200,确认可纯 requests 抓取。无加密 / 无签名。唯一「非直观」点是列表页的 Next.js flight data 解析:
# 1) 抽取并拼接所有 flight 分片
parts = re.findall(r'self\.__next_f\.push\(\[1,"((?:[^"\\]|\\.)*)"\]\)', html)
flight = "".join(json.loads('"' + p + '"') for p in parts) # 反转义后拼接
# 2) 花括号配平(尊重字符串内的转义引号)逐个抠出 {"displayCardPattern"...} 对象 → json.loads
trading-history 接口的 trades[] 是平台聚合/匿名化数据,每条只有价格/时间/品相/枚数,故意不含图片和出品ID——不是遗漏。若要给每条配图,复用现有管线 match_data 思路:以 (product_id + 品相 + 价格) 关联表1、取 primary_image_url 回填(届时给表2 加一个 image_url 字段)。range=all/oneWeek/oneMonth/...、page/perPage/limit/offset/size 各种参数都固定返回最近 20 条。所以不能用「先删后插」(会永远丢老历史),必须跨天累积。soldAt 秒可能出现多条完全相同的真实成交(如 6 笔 ¥1000 / B / 1枚 / 2026-07-17T03:50:03Z)。爬虫端解析时给同签名记录编 trade_index_in_second = 0/1/2/... 位序,配合联合唯一索引 + INSERT IGNORE 累积去重,既能保留真实重复、又能跨天自动跳过已存在的。前提:接口对 trades 的排序在同秒内相对稳定(实测成立)。product_id 聚合,而一张卡可能有几十个不同的 used_item_id 出品。批量模式下用 seen_product_ids 集合,每次 run 每个 product_id 只调一次交易记录接口。/v1/trading-cards/used:EN 站有、JP 站 404,故 JP 列表只能解析搜索页 flight。/en/v1/products/SW---{id}/trading-histories 返回 401;JP 的 /v3/.../trading-history 无需 cookie,优先用后者。...jpeg?size=m,入库前 split("?")[0] 去掉,保留原图。apparel.productId。application.yml:务必在项目目录下运行脚本。# 依赖:requests / parsel / user_agent / schedule / loguru / tenacity / charley-utils(mysql_pool)
# 1) 先执行 ddl.sql 建表(snkrdunk_jp_used_item / snkrdunk_jp_trading_history)
# 2) 确保项目目录下有 application.yml(mysql 连接配置)
# 3) 运行 snk_jp_spider.py,按底部开关选择任务:
| 场景 | 调用 |
|---|---|
| 任务2 单条详情+交易记录 | detail_main(logger, apparel_id=835482, used_item_id=47574548) |
| 任务1 列表页全部已售 → 表1 | list_main(log=logger) |
| 批量补全表1 列表记录的详情+交易记录 | detail_main(logger)(先跑完 list_main) |
| 每日定时(列表+详情+交易记录) | schedule_task() |
LIST_URL_TEMPLATE 的 keywords / brandIds / searchCategoryIds / itemConditions 即可,解析逻辑通用。self.__next_f 分片 + 花括号配平抠对象。/en/v1/...)已有独立 JSON 接口,若需英文数据可直接调用,无需解析 flight。