问答

snscrape 抓推特一直报 blocked (404),有能用的替代吗?

简短回答

snscrape 的推特模块依赖未登录也能访问的页面,X 关掉之后项目里一直没有修复。可以改用 X 帖子搜索(每页约 20 条,$0.006)和 X 账号帖子列表(每页 $0.006)重建采集。1 万条帖子的研究语料约 $3.00。

论文代码里还在调用 snscrape twitter-search 的话,大概从 2023 年起就只会打印 blocked (404) 了。snscrape 的推特模块读的是 X 以前对未登录访客开放的页面,X 不再开放之后,GitHub 上那个 issue 一直开着,没有修复。好在后面的流程不用推倒重来:把采集这一步换成返回同类记录的接口,清洗和分析代码基本照用。

命令怎么替换

snscrape 换成
twitter-search "关键词" X 帖子搜索,搜索语法相同
twitter-user 用户名 X 账号帖子列表,从新到旧
用户资料字段 X 账号信息

原来的字段这样对应:content 对应 text,date 对应 published_at,likeCount 对应 metrics.likes,retweetCount 对应 metrics.reposts,replyCount 对应 metrics.comments,viewCount 对应 metrics.views。引用数在 ext.quotes 里,需要加 view=full。

照 snscrape 的习惯写 JSONL

import json, os, requests

API = "https://api.okdata.online/v1"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}

def pages(path, params, limit):
    cursor = None
    for _ in range(limit):
        body = requests.get(f"{API}/{path}", headers=H, timeout=60,
                            params={**params, **({"cursor": cursor} if cursor else {})}).json()
        if body["code"] != "ok":
            raise RuntimeError(f"{body['code']}: {body['message']}")
        yield from body["data"]["items"]
        if not body["page"]["has_more"]:
            return
        cursor = body["page"]["next_cursor"]

query = "碳中和 lang:zh since:2026-10-01 until:2026-11-01"
with open("tweets.jsonl", "w", encoding="utf-8") as out:
    for post in pages("x/posts/search", {"query": query, "sort": "latest", "view": "full"}, limit=50):
        out.write(json.dumps(post, ensure_ascii=False) + "\n")

预算

语料规模 页数 费用
1,000 条 50 $0.30
1 万条 500 $3.00
50 个账号,每个 5 页 250 $1.50

每页算一次调用。失败不扣费,所以跑到一半崩了,也只付已经拿到的那些页。建议每个月的检索单独设一个页数上限,先跑一个月看看数据量,再决定要不要扩大范围。

方法部分最好写清楚的限制

  • 拿到的是 X 搜索对公众返回的结果,既不是随机抽样,也不是全量。
  • 受保护账号和已删除的帖子拿不到,这一点和 snscrape 时代一样。
  • 关注网络和回复树没有对应接口。

其他路线

X 官方的全量历史搜索可以回溯到 2006 年,按量付费账号就能用,每读一条帖子 0.005 美元。审稿人会追问抽样方式的话,官方数据的来源最好交代。snscrape 本身对另外一些网站仍然能用。如果只缺几百条帖子,在 X 网页上手动搜索、逐条整理进表格也完全可行,只是费时间。不管走哪条路,都把具体的检索式和采集日期跟数据一起保存。类似的采集流程可以参考趋势研究方案。

更新于