ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

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

用 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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。它的定位是统一 Key / API 通道不是替代你的编辑器也不是什么灰色中转就是把你散落各处的模型调用收敛成一套配置。先拿 Key进控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完复制出来后面写进config.toml。如果你还想先验证模型通不通可以用模型对话页面快速试一条https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。这一步的核心不是“注册”而是把后面所有模型调用都指向同一个 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;q0.9 Accept application/json, text/plain, */* [llm] # TaoToken 统一通道 base_url https://taotoken.net/api api_key sk-你的TaoTokenKey model claude-sonnet3.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, paramsNone, retriesNone): 带重试的 GET返回 JSON 或 None retries retries or CRAWL[max_retries] for i in range(retries): try: resp session.get(url, paramsparams, timeoutCRAWL[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 抓榜单拿 appiddef 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国区数据在请求里带ccCN参数实测国区前 50 和全球前 50 重合度不到 60%国产独立游戏在国区明显更靠前。中英文数据一定要分开抓、分开分析混在一起结论大概率不准。3.4 评论分页抓取def get_reviews(appid, targetNone): 抓指定游戏评论cursor 分页 异常重试 target target or CRAWL[target_reviews] reviews [] cursor * url CRAWL[reviews_url].format(appidappid) 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, paramsparams) 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, target100) print(fappid{test_appid} 抓到 {len(reviews)} 条评论) if reviews: r reviews[0] print(字段示例:, list(r.keys())) print(语言:, r.get(language)) print(好评:, r.get(voted_up))成功的话你会看到类似输出榜单拿到 100 个 appid appidxxxxxx 抓到 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_urlcfg[llm][base_url], api_keycfg[llm][api_key], ) resp client.chat.completions.create( modelcfg[llm][model], messages[{role: user, content: 把这句话里的关键词提取出来这游戏自由度很高但优化太差}], ) print(resp.choices[0].message.content)能正常返回说明 TaoToken 通道配好了后面评论清洗直接复用这个 client。5. 本篇常见错排查5.1 返回空数据但 success1最常见。原因通常是请求频率过高或者 cursor 翻页到了边界。先确认request_interval不低于 1.5 秒再把num_per_page调到 100。如果还是空检查是不是同一接口被并行请求了——同时跑多个游戏时千万不要并行打同一个接口宁可串行慢一点。5.2 拿回来的全是英文评论language参数没生效或者写错了。Steam 评论接口默认返回英文中文要显式写languageschinese。我第一次全跑完发现全是英文白等两小时。中英文分开抓别偷懒。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_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。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_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。Key 管理统一在 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。最后说个实际体会这套骨架里最容易被低估的是request_interval和重试逻辑很多人卡在“为什么抓一半就空了”其实加个 sleep 和 continue 就解决了。爬虫不难难的是稳定地跑完。
返回列表