ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

python 淘宝爬虫小实例

python 淘宝爬虫小实例

前言


这个标题的写法会让人以为「照着抄一段代码就能把淘宝商品抓下来」。实际情况恰好相反:淘宝这类电商平台,用通用爬虫去抓既不合规、也几乎没有可行性。它的 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 说不,当内容需要登录,当平台提供了官方接口——这些信号出现时,正确的做法都已经定了。把精力放在拿到授权之后的数据处理上,才是这段代码值得保留的部分。




RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进