问答
抓公众号文章一直报 200013 freq control,怎么解决?
简短回答
200013 是微信对你工具所用的公众号后台账号限流,放慢速度或轮换账号只能推迟它。改用 API key 抓取,你自己的账号就不在请求链路里了:公众号历史文章每页 10 篇,每页 $0.04,回溯 40 个号、每个 300 篇共 1,200 次调用,$48.00。
开始回溯 40 个公众号的历史文章,前二十分钟一切正常,然后每个请求都回 ret=200013, freq control,接下来到第二天都动不了。真正管用的办法,是别再用你自己的微信账号发这些请求。用公众号历史文章接口按页拉每个号的文章,只需要 API key,你这边没有后台登录,也就没有账号可被限流。
200013 到底是什么
从 mp.weixin.qq.com 读文章列表的爬虫,先用你名下的公众号登录后台,再以这个账号的身份调用后台的文章搜索。微信按账号计数,超过阈值就返回 200013,把这个功能封上几个小时。换 IP 没用,因为限制挂在账号上。常见的绕法是拉长请求间隔、准备好几个后台账号轮换,结果一次回溯要拖好几天,还多出一堆会过期的登录。
改写后的回溯脚本
历史文章接口每次返回 10 篇和下一页的 cursor。按你 key 的限速来调用,遇到可以重试的错误就重试:
import os, time, uuid, requests
API = "https://api.okdata.online"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
def call(path, **params):
key = str(uuid.uuid4()) # 重试用同一个 key,不会重复扣费
for _ in range(5):
r = requests.get(API + path, params=params, timeout=90,
headers={**H, "Idempotency-Key": key})
body = r.json()
if body["code"] == "ok":
return body
if body.get("retry") in ("yes", "same_key"):
time.sleep(body.get("retry_after_seconds") or 5)
continue
raise RuntimeError(f"{body['code']}: {body['message']}")
raise RuntimeError("重试 5 次仍失败")
def history(gh):
cursor = None
while True:
body = call("/v1/wechat_mp/accounts/articles/list", id=gh, **({"cursor": cursor} if cursor else {}))
yield from body["data"]["items"]
if not body["page"]["has_more"]:
return
cursor = body["page"]["next_cursor"]
回溯一次要多少钱
| 任务 | 调用次数 | 费用 |
|---|---|---|
| 1 个号,300 篇,只要列表 | 30 | $1.20 |
| 40 个号,每个 300 篇,只要列表 | 1,200 | $48.00 |
| 同上,再取其中 2,000 篇全文 | 1,200 + 2,000 | {{cost:wechat_mp.accounts.articles.list1200+wechat_mp.articles.get2000}} |
出错的调用一律不收费,包括上面脚本会重试的繁忙和超时。全文用公众号文章详情取。
仍然存在的限制
- 每个 key 有自己的限速。超了会返回
rate_limited并告诉你等几秒,响应头X-RateLimit-Remaining能看出还剩多少余量。全部错误码见错误说明。 - 数据源繁忙时返回
upstream_busy,不扣费,稍后重试即可。 - 只能拿到公开文章。阅读数和点赞数能拿到就有,拿不到是 null。
想继续自己抓的话
请求之间隔几秒,每个后台账号每天的量控制得保守一点,碰到第一个 200013 当天就停手,硬顶只会让封禁更久。慢慢存档一个号,这样够用。如果是你自己的号,微信官方接口能直接返回已发布的图文,根本不走后台搜索。几十个号又赶时间的话,走 API 更快,具体做法见竞品监测。
更新于