news 2026/9/26 13:07:54

用 requests 爬了 Steam 畅销榜 3000 条评论后,我把反爬策略和 TaoToken 配置一起整理成了骨架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 requests 爬了 Steam 畅销榜 3000 条评论后,我把反爬策略和 TaoToken 配置一起整理成了骨架

1. 从畅销榜评论里挖玩家口味,为什么我选了 requests 而不是浏览器自动化

Steam 畅销榜的评论数据,能直接反映玩家最近在夸什么、骂什么、为什么买单。对做独立游戏、做市场调研、或者单纯想练爬虫的人来说,这是一条性价比很高的数据链路:接口返回 JSON、字段结构稳定、评论量足够大。但真上手你会发现,Steam 的反爬不是靠一个 User-Agent 就能糊弄过去的——请求频率一高就返回空数据、部分接口不带 cookie 只给精简版、评论分页的 cursor 还会莫名其妙中断。

这篇就把我爬 Steam 畅销榜 3000 条评论的完整骨架整理出来:从榜单接口拿 appid,到评论接口翻页,再到限流、重试、请求头这些反爬细节,最后给一份可复制的config.toml,以及用 TaoToken 统一管理 Key 和 API 通道的配置骨架。目标很明确——让你从数据采集到口味分析跑通一条可复现的链路,而不是卡在“为什么第 8 页突然空了”这种问题上。

适合谁看:会一点 Python、用过 requests、想抓 Steam 数据但被反爬劝退的人。全程不需要浏览器自动化,requests 够用。

2. 前置准备:TaoToken 统一 Key 与 API 通道配置

爬虫本身不依赖大模型,但这条链路的后半段——评论清洗、关键词提取、口味聚类——用模型来做会省很多事。问题在于,如果你同时用几家模型服务,Key 管理、额度、调用格式会变得很碎。我这次的做法是用 TaoToken 把模型调用统一到一个通道里,爬虫只管抓数据,分析阶段直接走同一个 Key。

TaoToken 官网入口在这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个不加 UTM)。它的定位是统一 Key / API 通道,不是替代你的编辑器,也不是什么灰色中转,就是把你散落各处的模型调用收敛成一套配置。

先拿 Key:进控制台创建 API Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。创建完复制出来,后面写进config.toml。如果你还想先验证模型通不通,可以用模型对话页面快速试一条:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。

这一步的核心不是“注册”,而是把后面所有模型调用都指向同一个 base_url 和同一个 Key,这样爬虫脚本里就不用到处塞不同厂商的密钥了。

3. 可复制配置:config.toml 与请求骨架

3.1 config.toml 骨架

把下面这份存成config.toml,爬虫参数和模型通道参数放一起,改起来只动一个文件。

[crawler] # 榜单接口 top_sellers_url = "https://store.steampowered.com/api/stats/globaltopsellers" # 评论接口模板 reviews_url = "https://store.steampowered.com/appreviews/{appid}" # 每页评论数,默认 20 容易触发分页中断,调到 100 num_per_page = 100 # 请求间隔(秒),配合 IP 轮换使用 request_interval = 1.5 # 单游戏目标评论数 target_reviews = 100 # 语言:schinese 中文 / english 英文,中英文要分开抓 language = "schinese" # 只拉近一年评论 day_range = 365 # 超时 timeout = 15 # 重试次数 max_retries = 3 # 重试等待(秒) retry_wait = 3 [headers] User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0 Safari/537.36" Accept-Language = "zh-CN,zh;q=0.9" Accept = "application/json, text/plain, */*" [llm] # TaoToken 统一通道 base_url = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" model = "claude-sonnet"

3.2 读取配置与请求封装

import time import toml import requests cfg = toml.load("config.toml") HEADERS = cfg["headers"] CRAWL = cfg["crawler"] session = requests.Session() session.headers.update(HEADERS) def safe_get(url, params=None, retries=None): """带重试的 GET,返回 JSON 或 None""" retries = retries or CRAWL["max_retries"] for i in range(retries): try: resp = session.get(url, params=params, timeout=CRAWL["timeout"]) if resp.status_code == 200: return resp.json() except requests.RequestException: pass time.sleep(CRAWL["retry_wait"]) return None

这里没有把代理写死在代码里,是因为代理属于运行环境配置,不该混进业务逻辑。如果你确实需要出口 IP 轮换,把代理参数从环境变量读进来传给session.proxies就行,代码结构不用动。

3.3 抓榜单拿 appid

def get_top_sellers(): """抓全球畅销榜,返回 appid 列表""" data = safe_get(CRAWL["top_sellers_url"]) if not data: return [] # 接口返回结构里取榜单条目 items = data.get("items", data.get("ranks", [])) appids = [] for it in items: appid = it.get("appid") or it.get("id") if appid: appids.append(str(appid)) return appids

国区数据在请求里带cc=CN参数,实测国区前 50 和全球前 50 重合度不到 60%,国产独立游戏在国区明显更靠前。中英文数据一定要分开抓、分开分析,混在一起结论大概率不准。

3.4 评论分页抓取

def get_reviews(appid, target=None): """抓指定游戏评论,cursor 分页 + 异常重试""" target = target or CRAWL["target_reviews"] reviews = [] cursor = "*" url = CRAWL["reviews_url"].format(appid=appid) while len(reviews) < target: params = { "json": 1, "filter": "all", "language": CRAWL["language"], "day_range": CRAWL["day_range"], "review_type": "all", "purchase_type": "all", "num_per_page": CRAWL["num_per_page"], "cursor": cursor, } data = safe_get(url, params=params) if not data or data.get("success") != 1: break batch = data.get("reviews", []) reviews.extend(batch) # 本页数据量异常偏小,等一会再试 if len(batch) < 20 and cursor != "*": time.sleep(CRAWL["retry_wait"]) continue cursor = data.get("cursor", "") if not cursor: break time.sleep(CRAWL["request_interval"]) return reviews

关键点有三个:num_per_page调到 100 减少分页次数;cursor为空就停;单页返回数量异常时不要直接 break,等 3 秒重试。我翻到第 8 页突然中断过好几次,返回空列表但success还是 1,就是靠这个重试兜住的。

4. 验证请求:跑通一条最小链路

先别急着跑 74 款游戏,拿一个 appid 验证链路通不通。

if __name__ == "__main__": appids = get_top_sellers() print(f"榜单拿到 {len(appids)} 个 appid") test_appid = appids[0] reviews = get_reviews(test_appid, target=100) print(f"appid={test_appid} 抓到 {len(reviews)} 条评论") if reviews: r = reviews[0] print("字段示例:", list(r.keys())) print("语言:", r.get("language")) print("好评:", r.get("voted_up"))

成功的话你会看到类似输出:

榜单拿到 100 个 appid appid=xxxxxx 抓到 100 条评论 字段示例: ['recommendationid', 'author', 'language', 'review', 'timestamp_created', 'voted_up', 'votes_up', 'votes_funny', 'weighted_vote_score', 'comment_count'] 语言: schinese 好评: True

字段里review是评论文本,voted_up是好差评,votes_up是这条评测被顶的次数,author里还有玩家的游戏时长。这几个字段足够做口味分析了。

验证模型通道是否通,可以单独跑一段:

import openai client = openai.OpenAI( base_url=cfg["llm"]["base_url"], api_key=cfg["llm"]["api_key"], ) resp = client.chat.completions.create( model=cfg["llm"]["model"], messages=[{"role": "user", "content": "把这句话里的关键词提取出来:这游戏自由度很高,但优化太差"}], ) print(resp.choices[0].message.content)

能正常返回,说明 TaoToken 通道配好了,后面评论清洗直接复用这个 client。

5. 本篇常见错排查

5.1 返回空数据但 success=1

最常见。原因通常是请求频率过高,或者 cursor 翻页到了边界。先确认request_interval不低于 1.5 秒,再把num_per_page调到 100。如果还是空,检查是不是同一接口被并行请求了——同时跑多个游戏时千万不要并行打同一个接口,宁可串行慢一点。

5.2 拿回来的全是英文评论

language参数没生效或者写错了。Steam 评论接口默认返回英文,中文要显式写language=schinese。我第一次全跑完发现全是英文,白等两小时。中英文分开抓,别偷懒。

5.3 cursor 翻到一半中断

接口本身的分页实现不稳定。处理方式就是上面代码里的逻辑:单页返回数量小于预期时sleep后continue,不要直接退出循环。另外cursor为空字符串是正常结束,不是错误。

5.4 请求头缺 Accept-Language 导致返回精简版

部分页面不带 cookie 或语言头时,返回的数据里评论数、标签权重是缺的。把Accept-Language和Accept都补上,能拿到完整字段。

5.5 模型调用报 401 或 base_url 错误

检查config.toml里的base_url是不是https://taotoken.net/api,注意结尾不要多加/v1之类的路径。Key 从控制台复制时别带空格。如果还报错,去接入文档对一下参数格式:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。

5.6 长时间跑批被限流

单 IP 高频访问迟早被注意。要么拉长request_interval,要么在运行环境层面做出口 IP 轮换,代码里通过session.proxies注入即可,业务逻辑不用改。四小时跑 74 款游戏、每款 50 到 100 条评论,间隔 1.5 秒,实测能稳定跑完。

6. 把链路收尾:从采集到分析的下一步

数据抓下来只是开始。真正花时间的是清洗和关键词提取——把review字段里的文本去重、分句、提关键词,再按voted_up分组对比好差评的用词差异。这一步用模型批量处理比写正则省事得多,直接复用前面配好的 TaoToken client 就行。

如果你后面要长期跑这类采集 + 分析任务,或者想把它做成一个定时跑的 Agent,可以看下 Coding Plan 的配置方式:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。Key 管理统一在 API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。

最后说个实际体会:这套骨架里最容易被低估的是request_interval和重试逻辑,很多人卡在“为什么抓一半就空了”,其实加个 sleep 和 continue 就解决了。爬虫不难,难的是稳定地跑完。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 13:06:49

用A4纸草图加豆包,二十分钟零代码生成可交互网页

先把丑话说在前面&#xff1a;这标题不是我吹的&#xff0c;是真事。上周我坐在电脑前想做一个活动报名页&#xff0c;琢磨了半天连 HTML 是啥都记不全。后来实在没辙&#xff0c;从打印机里抽了张 A4 纸&#xff0c;拿笔画了个粗糙的页面框架&#xff0c;拍成照片扔给豆包&…

作者头像 李华
网站建设 2026/9/26 13:06:19

DBX命令行工具安装与实战:Dropbox CLI高效文件同步指南

1. DBX 是什么&#xff1f;它真不是数据库工具&#xff0c;而是开发者日常离不开的“代码快照机” 刚看到“DBX 安装”这个标题&#xff0c;很多人第一反应是&#xff1a;又一个类似 Navicat、DBeaver 的数据库管理工具&#xff1f;尤其当搜索热词里混着“dbx数据库管理工具下载…

作者头像 李华
网站建设 2026/9/26 13:06:18

Unity运行时动态加载3D模型:TriLib实战指南

简介&#xff1a;本资源是一套基于Unity 2021.3.27&#xff08;Standard Render Pipeline&#xff09;实现运行时3D模型动态加载与预览的完整工程&#xff0c;面向Unity中级开发者及AR/VR、场景编辑器、实时模型替换等应用方向的技术实践者。它依托TriLib 2.3.7插件&#xff0c…

作者头像 李华
网站建设 2026/9/26 13:05:14

SpringBoot+Vue电商项目:Redis布隆过滤器防穿透与路由守卫鉴权实战

简介&#xff1a;这是一套已调试通过的SpringBootVueRedis前后端分离网上商城毕业设计项目&#xff0c;面向计算机科学与技术、人工智能等专业的本科生及初阶Java全栈学习者&#xff0c;解决电商系统核心模块开发与架构实践问题。资源含2034个文件&#xff0c;主体为1358个Mark…

作者头像 李华