如何用 TwexAPI 分页获取 Donald Trump 的 tweets 和 replies
如果你需要可复现地导出 @realDonaldTrump 的公开帖子,最好使用 cursor 分页,而不是一次性请求一个很大的 count。分页更容易恢复、审计,也更适合边抓边保存。
TwexAPI 的 Get All Tweets and Replies by User by Page 端点会按页返回某个用户自己发布的 tweets 和 replies。对于 Trump 研究、监控或数据集构建,调用 POST /twitter/tweets-replies/page,传入 screen_name: "realDonaldTrump",保存 raw results,然后用 next_cursor 继续翻页。
这个端点返回的是目标账号自己写的 tweets 和 replies。它不会收集其他用户在这些帖子下面的所有回复。如果要分析公众反应,需要先选定具体 tweet_id,再使用 tweet-level reply endpoint。
适合什么场景
当你的问题是“这个账号发过什么、回复过什么”时,用这个 workflow。
适合的 Trump 场景包括:
- 分页导出
@realDonaldTrump的公开 tweets 和 replies。 - 为 timeline analysis、media review 或内部研究构建数据集。
- 区分 standalone posts 和该账号写出的 replies。
- 在做 summary、dashboard 或分类前保存 raw tweet objects。
- 创建失败后可恢复的导出任务。
如果你需要关键词查询,例如 from:realDonaldTrump economy -filter:retweets,应该使用 Advanced Search by Page。这个 endpoint 更适合以账号历史为中心的导出。
API Endpoint
发送 POST 请求:
https://api.twexapi.io/twitter/tweets-replies/page请求体:
| 字段 | 必填 | 说明 |
|---|---|---|
screen_name | 是 | 目标 X 用户名,可以带或不带 @。本场景使用 realDonaldTrump。 |
next_cursor | 否 | 上一页返回的 cursor。第一页不传。 |
每个响应包含当前页数据和分页 metadata:
| 字段 | 含义 |
|---|---|
data | 当前页 tweets 和 replies,通常最多约 20 条。 |
has_next_page | 是否还有下一页。 |
next_cursor | 下一次请求要传的 cursor。 |
code / msg | 标准状态字段。 |
请求第一页
第一次请求只需要 screen_name。
curl --request POST \
--url https://api.twexapi.io/twitter/tweets-replies/page \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"screen_name": "realDonaldTrump"
}'简化后的响应结构如下:
1{
2 "code": 200,
3 "msg": "success",
4 "data": [
5 {
6 "tweet_id": "1803006263529541838",
7 "text": "<post text returned by the API>",
8 "created_at": "Mon Jun 17 03:51:48 +0000 2024",
9 "created_at_datetime": "2024-06-17T03:51:48.000Z",
10 "favorite_count": 0,
11 "retweet_count": 0,
12 "reply_count": 0,
13 "quote_count": 0,
14 "in_reply_to": null,
15 "in_reply_to_screen_name": null,
16 "is_quote_status": false,
17 "user": {
18 "screen_name": "realDonaldTrump"
19 }
20 }
21 ],
22 "has_next_page": true,
23 "next_cursor": "1234567890"
24}继续翻页时传入返回的 cursor:
curl --request POST \
--url https://api.twexapi.io/twitter/tweets-replies/page \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"screen_name": "realDonaldTrump",
"next_cursor": "1234567890"
}'不要把 cursor 复用到另一个账号。cursor 属于产生它的请求序列。
Python 导出脚本
下面的脚本把 raw tweets/replies 写入 JSONL,用 tweet_id 去重,并把 cursor 进度写入 metadata 文件。
1import json
2import os
3import time
4from datetime import datetime, timezone
5from pathlib import Path
6from typing import Any
7
8import requests
9
10TOKEN = os.environ["TWEXAPI_BEARER_TOKEN"]
11API_URL = "https://api.twexapi.io/twitter/tweets-replies/page"
12SCREEN_NAME = "realDonaldTrump"
13
14OUT = Path("realDonaldTrump-tweets-replies.jsonl")
15META = Path("realDonaldTrump-tweets-replies-meta.json")
16
17HEADERS = {
18 "Authorization": f"Bearer {TOKEN}",
19 "Content-Type": "application/json",
20}
21
22def fetch_page(next_cursor: str | None = None) -> dict[str, Any]:
23 payload: dict[str, Any] = {"screen_name": SCREEN_NAME}
24 if next_cursor:
25 payload["next_cursor"] = next_cursor
26
27 response = requests.post(
28 API_URL,
29 headers=HEADERS,
30 json=payload,
31 timeout=45,
32 )
33 response.raise_for_status()
34 body = response.json()
35
36 if body.get("code") not in (None, 200):
37 raise RuntimeError(body.get("msg") or "TwexAPI request failed")
38
39 return body
40
41seen_ids: set[str] = set()
42cursor: str | None = None
43page_count = 0
44saved_count = 0
45cursor_log = []
46
47with OUT.open("w", encoding="utf-8") as output:
48 while True:
49 body = fetch_page(cursor)
50 page_count += 1
51
52 items = [item for item in body.get("data", []) if item]
53 for tweet in items:
54 tweet_id = tweet.get("tweet_id") or tweet.get("id")
55 if not tweet_id or tweet_id in seen_ids:
56 continue
57
58 seen_ids.add(tweet_id)
59 output.write(json.dumps(tweet, ensure_ascii=False) + "\n")
60 saved_count += 1
61
62 next_cursor = body.get("next_cursor")
63 cursor_log.append(
64 {
65 "page": page_count,
66 "fetched_items": len(items),
67 "saved_unique_items": saved_count,
68 "has_next_page": body.get("has_next_page"),
69 "next_cursor": next_cursor,
70 }
71 )
72
73 print(
74 f"page={page_count} fetched={len(items)} "
75 f"saved_unique={saved_count}"
76 )
77
78 if not body.get("has_next_page") or not next_cursor:
79 break
80
81 cursor = next_cursor
82 time.sleep(0.5)
83
84META.write_text(
85 json.dumps(
86 {
87 "screen_name": SCREEN_NAME,
88 "exported_at": datetime.now(timezone.utc).isoformat(),
89 "unique_items": saved_count,
90 "pages": cursor_log,
91 },
92 ensure_ascii=False,
93 indent=2,
94 ),
95 encoding="utf-8",
96)
97
98print(f"Saved {saved_count} unique tweets and replies to {OUT}")运行:
export TWEXAPI_BEARER_TOKEN="<token>"
python export_trump_tweets_replies.py长任务建议在请求下一页之前,先把最后成功的 next_cursor 保存到数据库。这样失败后不用从第一页重跑。
生成 CSV 复核表
Raw JSONL 是 source of truth,CSV 是给人看的 review layer。
1import csv
2import json
3
4INPUT_PATH = "realDonaldTrump-tweets-replies.jsonl"
5OUTPUT_PATH = "realDonaldTrump-tweets-replies.csv"
6
7FIELDS = [
8 "tweet_id",
9 "post_type",
10 "created_at_datetime",
11 "text",
12 "lang",
13 "favorite_count",
14 "retweet_count",
15 "reply_count",
16 "quote_count",
17 "view_count",
18 "in_reply_to",
19 "in_reply_to_screen_name",
20 "is_quote_status",
21 "url",
22]
23
24def post_type(tweet: dict) -> str:
25 if tweet.get("in_reply_to") or tweet.get("in_reply_to_screen_name"):
26 return "reply"
27 if tweet.get("is_quote_status") or tweet.get("quoted_status_id_str"):
28 return "quote"
29 return "original"
30
31with open(INPUT_PATH, encoding="utf-8") as source, open(
32 OUTPUT_PATH,
33 "w",
34 encoding="utf-8",
35 newline="",
36) as target:
37 writer = csv.DictWriter(target, fieldnames=FIELDS)
38 writer.writeheader()
39
40 for line in source:
41 tweet = json.loads(line)
42 tweet_id = tweet.get("tweet_id") or tweet.get("id")
43 row = {
44 "tweet_id": tweet_id,
45 "post_type": post_type(tweet),
46 "created_at_datetime": tweet.get("created_at_datetime"),
47 "text": tweet.get("full_text") or tweet.get("text") or "",
48 "lang": tweet.get("lang"),
49 "favorite_count": tweet.get("favorite_count") or 0,
50 "retweet_count": tweet.get("retweet_count") or 0,
51 "reply_count": tweet.get("reply_count") or 0,
52 "quote_count": tweet.get("quote_count") or 0,
53 "view_count": tweet.get("view_count"),
54 "in_reply_to": tweet.get("in_reply_to"),
55 "in_reply_to_screen_name": tweet.get("in_reply_to_screen_name"),
56 "is_quote_status": tweet.get("is_quote_status"),
57 "url": f"https://x.com/realDonaldTrump/status/{tweet_id}",
58 }
59 writer.writerow(row)
60
61print(f"Wrote {OUTPUT_PATH}")post_type 只是一个复核标签。raw 字段仍然要保留,因为 quote 和 reply metadata 的形态可能变化。
区分 tweets、replies 和 quotes
对 Trump 相关工作流来说,standalone posts 和 replies 通常回答不同问题。
| 类型 | 判断方式 | 常见用途 |
|---|---|---|
| Original post | 没有 in_reply_to,也没有 quote metadata | 时间线、公告、叙事追踪 |
| Reply | 有 in_reply_to 或 in_reply_to_screen_name | 补充上下文、澄清、对话 |
| Quote | 有 is_quote_status 或 quoted_status_id_str | 对其他帖子的评论 |
不要自动丢弃 replies。公众人物经常在 replies 里补充语境、回应问题或修正表述。
应该保存什么
至少保存:
- TwexAPI 返回的 raw tweet object。
tweet_id或id,用于去重。created_at和created_at_datetime,用于时间线分析。text或full_text,用于复核。in_reply_to、in_reply_to_screen_name和 quote 字段,用于分类。favorite_count、retweet_count、reply_count、quote_count、view_count等互动字段。hashtags、cashtags、urls、media,用于 enrichment。- 导出 metadata:
screen_name、导出时间、page count 和 cursor log。
互动数据会随时间变化。如果后续刷新,建议保存新的 snapshot timestamp,而不是默默覆盖原始数据。
Endpoint 怎么选
TwexAPI 里常见的 Trump 相关采集有三种:
| 目标 | 推荐 endpoint |
|---|---|
导出 @realDonaldTrump 自己写的 tweets 和 replies | POST /twitter/tweets-replies/page |
| 按关键词或日期搜索 Trump posts | POST /twitter/advanced_search/page |
| 只监控新出现的公开帖 | 轮询 POST /twitter/advanced_search/page,sortBy: "Latest" |
账号历史是主对象时,用 tweets-and-replies page endpoint。查询逻辑更重要时,用 Advanced Search。
相关资源
- Get All Tweets and Replies by User by Page API Reference
- 如何用 Advanced Search 收集 Donald Trump X 帖子
- 如何用 TwexAPI 实时监控 Donald Trump 新推文
- 如何下载某个 X 用户的全部 tweets 和 replies
小结
用 TwexAPI 获取 Trump tweets 和 replies 的核心流程是:调用 POST /twitter/tweets-replies/page,传入 screen_name: "realDonaldTrump",保存 raw data,并在 has_next_page 为 true 时继续传 next_cursor。
这样得到的是可复现的账号级数据集,也能保留足够 metadata 来区分 original posts、replies 和 quote-style commentary。