前言
这个标题的写法会让人以为「照着抄一段代码就能把淘宝商品抓下来」。实际情况恰好相反:淘宝这类电商平台,用通用爬虫去抓既不合规、也几乎没有可行性。它的 robots.txt 与用户协议对通用抓取有严格限制(具体限制内容请以你自己实际查到的 robots.txt 和协议为准),页面的商品数据大量由脚本动态加载,并且平台有一整套针对异常访问的识别与处置机制。所以这篇「小实例」的正确写法,不是教你写一段能和风控对抗的代码,而是把合规的那条路走一遍:先判断能不能抓,不能抓就走开放平台的正规接口,实在只是练手就换成练习站点。
有几句需要直说。本文不提供、也不演示任何绕过登录、绕过验证码、绕过风控的做法——包括但不限于伪造登录态、破解签名、模拟完整浏览器指纹来规避识别。这类「技术」即便写出来,也是把你和平台都置于风险之中。本文只讲三件合规的事:怎么判断一个站点让不让你抓;官方开放接口的调用形态长什么样;在不能抓的情况下,怎么用练习数据把采集流程练熟。
代码基于 Python 3(Python 2.7 已于 2020 年 1 月 1 日停止维护,老教程里的print语句、urllib2、dict.iteritems()都要按 Python 3 的写法重写)。
一、先判断「能不能抓」,再谈「怎么抓」
任何采集任务的第一步都是一个布尔判断:目标站点让不让抓。标准库里的urllib.robotparser.RobotFileParser就是干这个的:set_url(robots_url)设定 robots.txt 地址,read()抓取并解析,can_fetch(useragent, url)返回布尔值。
对电商平台,这一步的结论往往就是「不允许」——那结论就是不抓,而不是「换个 UA 再试试」。要注意分清两种情况:服务器上根本没有 robots.txt(标准库按 RFC 9309 视为「没有规则」),和服务器出错、取不到 robots.txt(应当保守地假定禁止)。后者稳妥的做法是暂停,等下次重试成功再说,而不是趁机开抓。
除了 robots.txt,还要看用户协议。robots.txt 表达的是「爬虫礼仪」,协议与著作权法表达的是「让你做与不让你做」,后者优先级更高。
二、合规路径:走开放平台
电商平台通常会为开发者提供开放平台(Open Platform)接口:申请应用、拿到 appkey 与 appsecret,按平台规定的算法对请求参数做签名,再调用接口获取结构化数据。这条路的特点是:
- 有明确的授权与配额,你抓什么、抓多少都是约定好的;
- 数据是结构化的 JSON,不用解析 HTML;
- 平台会限流,超额会被拒绝——这是设计,不是障碍。
一个通用的带签名参数的 GET 请求长这样(具体签名算法、参数名、接口地址以开放平台官方文档为准):
| 环节 | 做什么 | 注意 |
|---|
| 组装参数 | 把 appkey、时间戳、接口名、业务参数等放一个字典 | 参数名以文档为准 |
| 排序拼接 | 按平台规定对参数排序、拼接成待签名字符串 | 顺序错则验签失败 |
| 生成签名 | 用 appsecret 配合平台指定的哈希算法算出 sign | 私钥绝不能进前端或公开仓库 |
| 发起请求 | 拼成 URL 或放进请求体发出 | 遵守平台限流 |
| 解析响应 | 响应是 JSON,用json.loads解析 | 先判断错误字段再取数据 |
生成签名属于防御侧的正当用法:标准库里hashlib提供各种哈希,hmac提供基于密钥的消息认证码(比如hmac.new(key, msg, hashlib.sha256).hexdigest()),需要做恒定时间比较时用hmac.compare_digest,而不是用==。具体用哪个算法、拼串顺序如何,一律以平台文档为准,不要凭印象拼。
三、只是练手:换成练习数据
如果你的目的只是学会「请求—解析—落盘」这条链路,完全没必要去碰真实平台的边界。两条更合适的路径:
- 用专门为练习搭建的站点(例如
quotes.toscrape.com这类站点,页面结构稳定、明确用于教学); - 用自己构造的 JSON 或本地 HTML 文件,把解析和落盘逻辑练熟。
下面这个「小实例」就按这条思路写:它先老老实实检查 robots.txt,发现不允许就退出;然后演示怎么处理一份开放接口返回的 JSON(这里用本地字符串模拟,因为你拿到的应当是授权接口的数据),最后输出规整的记录。
实战:合规前提下的完整小实例
# 适用于 Python 3.8+
import json
import urllib.parse
import urllib.request
import urllib.robotparser
UA = "taobao-api-demo/0.1 (contact: demo@example.net)"
def robots_allowed(url):
parts = urllib.parse.urlsplit(url)
robots_url = urllib.parse.urlunsplit(
(parts.scheme, parts.netloc, "/robots.txt", "", "")
)
parser = urllib.robotparser.RobotFileParser()
parser.set_url(robots_url)
try:
parser.read()
except Exception:
return False # 读不到就按不允许处理
return parser.can_fetch(UA, url)
def call_open_api(base_url, params, timeout=15):
"""带签名参数的 GET 调用骨架。
注意:签名算法(参数排序、拼接格式、哈希方式)由开放平台文档规定,
这里只演示"把参数编码后发出去"这一形态,sign 需要你按文档生成。
"""
query = urllib.parse.urlencode(sorted(params.items()))
req = urllib.request.Request(
base_url + "?" + query, headers={"User-Agent": UA}
)
with urllib.request.urlopen(req, timeout=timeout) as resp:
charset = resp.headers.get_content_charset() or "utf-8"
return resp.read().decode(charset, errors="replace")
def parse_items(payload):
"""把接口返回的 JSON 转成规整记录。"""
data = json.loads(payload)
records = []
for item in data.get("items", []):
records.append({
"title": item.get("title", "").strip(),
"price": item.get("price", ""),
"sold": int(item.get("sold", 0)),
})
return records
def main():
target = "https://www.taobao.com/"
if not robots_allowed(target):
print("robots.txt 不允许抓取该站点,改用官方开放接口或练习数据。")
return
# 只有 robots 允许、且已获得平台授权时,才应该调用真实接口:
# payload = call_open_api(BASE, {"appkey": "...", "sign": "...", ...})
# 下面用一段本地 JSON 演示解析逻辑,无需联网。
payload = json.dumps({
"items": [
{"title": "示例商品 A", "price": "39.90", "sold": 128},
{"title": "示例商品 B", "price": "199.00", "sold": 7},
]
}, ensure_ascii=False)
for record in parse_items(payload):
print("%-10s %8s 已售 %d" % (record["title"], record["price"], record["sold"]))
if __name__ == "__main__":
main()跑起来后,如果 robots.txt 判定不允许,程序会打印提示并主动退出——这个「主动退出」就是本实例最重要的行为。剩下的parse_items演示的是通用能力:不管数据来自官方接口还是你自己的文件,JSON 到记录的转换逻辑都是一样的。注意json.dumps(..., ensure_ascii=False)让中文以汉字本身呈现,json.loads则把文本还原成字典与列表。
常见坑点
1. 把「能不能抓」当成技术问题
❌ 看到 robots.txt 禁止,就开始研究怎么伪装。 ✅ robots.txt 禁止就是不抓。合规问题没有技术解法。
2. 用脚本去对抗风控
❌ 研究验证码、指纹、登录态的绕过手法。 ✅ 需要平台数据就申请开放平台权限;本文不提供任何对抗性代码。
3. appsecret 写死在会被公开的地方
❌ 把密钥硬编码进开源仓库或前端页面。 ✅ 密钥放环境变量或配置中心,不进版本库;泄露了立刻在平台重置。
4. 用==比较签名
❌if got_sign == expect_sign:—— 普通比较可能提前返回,理论上可被计时攻击利用。 ✅ 用hmac.compare_digest(a, b)做恒定时间比较。
5. 猜测签名算法
❌ 凭印象拼一个「参数排序 + md5(secret+buf+secret)」就当通用公式用。 ✅ 不同平台算法不同,以该平台官方文档为准,用文档给的样例自测。
6. 无限流地调接口
❌ 循环里不设间隔,把配额瞬间打满。 ✅ 遵守平台的速率约定,对限流错误退避后重试。
7. 把授权数据再分发
❌ 通过开放接口拿到数据后转手公开售卖。 ✅ 平台授权的是「你按约定使用」,不是「你自由处置」,使用范围以协议为准。
8. 拿练习站当真站练
❌ 在练习站上调通了,就以为能原样套到电商平台。 ✅ 练习站结构简单稳定,真实平台数据形态、鉴权、限流完全不同,别把两件事混为一谈。
总结
| 场景 | 合规做法 | 明确不做的事 |
|---|
| 想抓电商平台商品页 | 走开放平台接口,申请授权 | 不抓、不绕过风控 |
| robots.txt 禁止抓取 | 停止 | 不换 UA 硬闯 |
| 需要登录/付费的内容 | 放弃 | 不伪造登录态 |
| 只是想练技术 | 用练习站或本地数据 | 不拿真实平台当靶子 |
| 调用开放接口 | 按文档签名、限流、比签名用compare_digest | 不硬编码密钥、不猜算法 |
写「淘宝爬虫」这样一个实例,真正学到的不是某段代码,而是一个判断顺序:先合规、再授权、最后才是技术。当 robots.txt 说不,当内容需要登录,当平台提供了官方接口——这些信号出现时,正确的做法都已经定了。把精力放在拿到授权之后的数据处理上,才是这段代码值得保留的部分。