想做个比价工具,官方 API 不给我,那我自己爬
起因
事情的起因很简单:我想买个东西,想知道同一个商品在淘宝、京东、拼多多上各卖多少钱、有什么券。人肉开三个 App 搜三遍实在太蠢了,程序员干不出这种事。
正经的路线当然是官方 API。然后我去查了查:淘宝开放平台,个人开发者拿不到商品 API 权限;拼多多、京东,大差不差。这些数据是平台的命根子,凭什么白给你。第三方聚合(大淘客之类)倒是可以白嫖,但要注册、要申请应用、要填 key——流程走到一半我突然想:要不别要 API 了,我自己爬?
反正是给自己用的工具,丑一点、慢一点都无所谓。说干就干。
仓库在这里:nbsheep/price-compare,Python + Flask + Playwright,两百来行核心代码。
第一步就撞墙:三家全是登录墙
我的预设是「京东不用登录就能看搜索结果」——早年确实可以。拿 Playwright 开了个无头 Chromium 挨个探测,结果:
| 平台 | 不登录能搜吗 |
|---|---|
| 京东 PC | ❌ 302 到 passport.jd.com |
| 京东移动版 | ❌ 302 到 plogin.m.jd.com |
| 拼多多 | ❌ 302 到 login.html |
| 淘宝 | ⚠️ 不跳转,但商品列表永远不渲染 |
淘宝这个最有意思:URL 不变、页面正常、标题都对,就是死活不出商品。翻 HTML 才发现里面嵌着 login.taobao.com 的登录框——它不赶你走,就是不给你看东西,属于是软磨硬泡型反爬。
三家全墙,纯游客路线宣告死亡。那就换思路:登录一次,把登录态存下来,以后每次复用。
做法很朴素——一个 login.py,弹出两个有头浏览器窗口(淘宝+京东一个,拼多多手机模拟一个),你手动登录,cookie 持久化到本地 profile 文件夹。之后每次比价,无头浏览器带着这份 cookie 出场,平台只当是个老用户。
中间还出了个插曲:我那个登录向导最后要按回车再优雅关闭,结果我手快直接把窗口叉了,脚本当场报错 TargetClosedError。修的时候才发现——虚惊一场,登录过程中 cookie 是持续写盘的,叉窗口根本不丢。所谓「优雅退出」纯属仪式感。
每家的抓取姿势都不一样
登录态搞定之后,才是真正好玩的部分:三个平台的页面结构,一家一个样。
淘宝:搜索结果一半是 item.taobao.com,一半是天猫 detail.tmall.com。我只写了一种链接的抽取规则,直接漏掉一半商品,还纳闷结果怎么这么少。另外它的价格在 innerText 里是被换行拆开的——¥、497、.25 各占一行,正则 ¥\s*(\d+) 只能抓到 497。最后用 [¥¥]\s*(\d[\d,]*)(?:\s*\.\s*(\d{1,2}))? 把拆散的小数拼回来才对。
京东:最老实,标准的 <a href="item.jd.com/...">,通用抽取器直接能用。但风控最凶——搜索稍微频繁一点就给你弹「抱歉由于访问频繁导致无法搜索」,而且狠起来连登录态一起踢:某次限流之后再进去,页面右上角赫然写着「你好,请登录」。所以代码里专门检测这两句话,把「请等十几分钟」和「请重新跑 login.py」分开提示。
拼多多:最绝。我拿选择器数 <a> 标签,结果是——0 个。整页全是 div + JS 点击跳转,通用抽取器当场失业。但柳暗花明:翻全局变量发现它把完整的商品数据埋在 window.rawData 里:
1 | window.rawData.stores.store.data.ssrListData.list |
反爬把 DOM 做成了铁板一块,数据却老老实实躺在 JSON 里——像是把门焊死了,但钥匙挂在门内侧。
¥499 的 AirPods 给我上了一课
第一版跑通之后我搜了个 AirPods,结果工具兴冲冲告诉我:淘宝 ¥499 拿下。正常价格 1000 往下基本是假的,这明显不对。
排查之后发现是匹配算法的经典翻车:我拿字符二元组的 Jaccard 相似度从搜索结果里挑「同款」,而「华强北AirPodsPro3代」这种短标题,跟查询词的重叠比例天然就高;正经标题「Apple/苹果 AirPods Pro 2 二代 融合」字多,union 一大分数反而低。垃圾商品在算法眼里个个都是满分卷王。
光调分数没用,得认出垃圾。于是加了一版特征词库:
1 | 华强北 | 平替 | 高仿 | 议价 | 二手 | 99新 | 翻新 | 私模 | 洛达 | 悦虎 | 适用 | 兼容 … |
(洛达、悦虎是华强北耳机常用芯片名,比「华强北」三个字本身还可靠。)
命中特征词的商品:界面上标红「疑似山寨/平替/二手」,挑同款时优先跳过。改完同一个搜索,淘宝从 ¥499 变成 ¥1308——这基本是港版/美版 AirPods Pro 2 的真实价位(国行官方 1899)。工具第一次给出了一个我能信的价格。
当然,识别永远不可能完美。比价工具能做的只是把明显离谱的滤掉,剩下的版本差异(国行/港版/美版)还是得人自己长个心眼。
最终长什么样
单页应用,粘贴淘宝链接或直接输关键词:
- 每个平台一张卡片:价格、划线原价、优惠券(拼多多的「立减X元」能抓到)、销量、直达链接
- 全网最低的卡片标绿丝带
- 疑似山寨的商品标红,且不当「同款代表」
- 三数据源可切:爬虫(默认)/ 大淘客 API(填 key 自动切)/ 演示数据
诚实的局限也得说:券的「已领/剩余」数量抓不到——那玩意只在领券页和 App 里展示;淘口令没有免费解析接口,暂不支持;每次比价要真开浏览器跑一圈,15~30 秒,快不起来。
用
1 | pip install playwright flask requests |
仅供个人比价自用,别拿去高频批量抓——记住京东的教诲:它真的会踢人。
