Twitter 图片爬虫:下载每一张图片
Twitter 图片爬虫会找出账号或搜索结果中带照片的帖子,读取每张照片的 pbs.twimg.com 链接,并用 name=orig 下载,因为 X 提供的不带参数的链接最宽只有 1,200 像素。
简短回答: 未登录时,x.com 只显示一个主页最新的几条帖子,所以浏览器扩展和下载器都是通过你的登录或会话 cookie 来读取 X。想在不危及账号的前提下收集每一张图片,可以用 Sorsa API:带 filter:images 的 POST /search-tweets 每次请求返回约 20 条图片帖子,每 1,000 条 $0.07 起,100 次免费请求可以覆盖前 2,000 条。
- 接口
POST /search-tweets- 每页数量
- 每次请求约 20 条帖子
- 价格
- 每 1,000 条 $0.07 起
#Twitter 图片爬虫是如何工作的
无论运行在什么上,每个 Twitter 图片爬虫都在做同样的三件事。如果只是一条帖子,这三件事你都不需要:把它的链接粘贴到免费的媒体下载工具里,就能以原始大小保存每张照片。如果是几百几千张图片,这项工作是这样的:
-
找出带照片的帖子。 X 的搜索运算符
filter:images只保留至少带一张图片的帖子,可以用from:限定在某个账号,也可以针对任意话题。 -
读取每张照片的链接。 每张图片都放在 X 的媒体服务器上,链接形如
https://pbs.twimg.com/media/G4kQ2vXbIAAm7Rz.jpg。 -
下载原图。 请求那个链接时加上
?format=jpg&name=orig;不加的话,X 发来的是一份适配 1,200 像素的副本。
各种工具的差别在第 1 步,也就是帖子从哪里来。x.com 对未登录的访客只显示单条帖子和一个主页最新的几条帖子,并把搜索导向登录页面。所以浏览器扩展、桌面下载器和命令行工具都要通过已登录的账号读取 X。它们自己的文档就是这么说的:一个广泛使用的批量下载器会要求你提供 auth_token cookie,并警告这可能导致账号被封禁。
只有 API 才能拿到整个账号的图片:要么是带开发者账号和 OAuth 的官方 X API,要么是只需一个密钥的 Twitter/X 数据 API。
如何抓取 Twitter指南涵盖了每条途径以及会被什么打断,最佳 Twitter 爬虫盘点则逐个为这些工具标价。
#下载 X 账号的每一张图片
Sorsa API 是一款 Twitter(X)API 与爬虫,它把账号的图片帖子以 JSON 返回,每张照片的链接都在其中,每次请求约 20 条帖子,可回溯到 2006 年。四步就能从一个用户名走到一个装满原始文件的文件夹。
-
获取密钥。 注册后从控制台复制 API 密钥。前 100 次请求免费,无需绑卡。
-
搜索账号的图片帖子。 把
from:加上filter:images发给POST /search-tweets(在新标签页打开),并把order设为latest,让最新的排在前面。Terminal curl -X POST "https://api.sorsa.io/v3/search-tweets" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "from:northfield_photo_lab filter:images", "order": "latest"}'Response200 OK { "tweets": [ { "id": "1984213377309245441", "created_at": "2026-10-30T14:12:09Z", "full_text": "Fog over the north ridge this morning.", "likes_count": 412, "entities": [ { "type": "photo", "link": "https://pbs.twimg.com/media/G4kQ2vXbIAAm7Rz.jpg", "preview": "https://pbs.twimg.com/media/G4kQ2vXbIAAm7Rz.jpg" }, { "type": "photo", "link": "https://pbs.twimg.com/media/G4kQ2vZaQAEw9Lp.jpg", "preview": "https://pbs.twimg.com/media/G4kQ2vZaQAEw9Lp.jpg" } ], "user": { "username": "northfield_photo_lab", "display_name": "Northfield Photo Lab", "media_count": 1840 } } ], "next_cursor": "DAADDAABCgABG8vZ0Qm__-oKAAIbuv3MBVvA0AgAAwAAAAIAAA" } -
沿着 cursor 往下读。 cursor 是标记下一页从哪里开始的令牌:把
next_cursor放进下一次请求的请求体里发回去,直到它返回为空。每次请求返回约 20 条图片帖子和指向下一页的 cursor;每个照片链接都变成一个原始尺寸的文件,以及 images.csv 中的一行。 -
以原始尺寸保存每张照片。 脚本只保留
photo实体,把每个链接改写为name=orig,并把文件命名为<post id>_<n>.jpg。它把每个文件记录在images.csv里,并在每一页之后保存 cursor,所以中途停下的任务可以从断点继续。import csv import os import sys import time from urllib.parse import parse_qs, urlparse import requests API = "https://api.sorsa.io/v3/search-tweets" HEADERS = {"ApiKey": "YOUR_API_KEY"} def orig(url): """Rewrite a pbs.twimg.com image link to its original size.""" u = urlparse(url) path = u.path fmt = parse_qs(u.query).get("format", ["jpg"])[0] if "." in path.rsplit("/", 1)[-1]: path, fmt = path.rsplit(".", 1) fmt = fmt.split(":")[0] return f"https://pbs.twimg.com{path}?format={fmt}&name=orig", fmt def photo_urls(tweet): """Original-size links of every photo in a post.""" for e in tweet.get("entities") or []: if e.get("type") != "photo": continue for link in (e.get("link"), e.get("preview")): if link and "pbs.twimg.com/media/" in link: yield orig(link) break def post(body): while True: r = requests.post(API, headers=HEADERS, json=body, timeout=60) if r.status_code == 429: # over 20 requests a second: wait time.sleep(1) continue r.raise_for_status() # 403: no key, or no requests left return r.json() def download(query, folder): os.makedirs(folder, exist_ok=True) state = os.path.join(folder, "cursor.txt") body = {"query": query, "order": "latest"} if os.path.exists(state): # resume a stopped run body["next_cursor"] = open(state).read().strip() log_path = os.path.join(folder, "images.csv") with open(log_path, "a", newline="", encoding="utf-8") as f: log = csv.writer(f) while True: page = post(body) for t in page.get("tweets", []): for n, (url, fmt) in enumerate(photo_urls(t), 1): path = os.path.join(folder, f"{t['id']}_{n}.{fmt}") if os.path.exists(path): continue img = requests.get(url, timeout=60) img.raise_for_status() with open(path, "wb") as out: out.write(img.content) log.writerow([t["id"], t["created_at"], url, path]) cursor = page.get("next_cursor") if not cursor: break body["next_cursor"] = cursor with open(state, "w") as s: s.write(cursor) if os.path.exists(state): os.remove(state) if __name__ == "__main__": download(sys.argv[1], sys.argv[2])
用一个搜索词和一个文件夹名运行它:python twitter_images.py "from:northfield_photo_lab filter:images" northfield_photo_lab。返回 403 表示密钥缺失或请求额度已用完;返回 429 表示一秒内超过 20 次请求,脚本会等一秒再重试。磁盘上已有的文件会被跳过,所以再次运行同一条命令只会补上新的图片。
须知
引用或转发带的是别人的照片:它们在
quoted_status.entities和retweeted_status.entities里,而脚本把它们排除在外。想保留它们,就把这两个列表加进photo_urls。想先估算任务规模,GET /info(在新标签页打开) 会返回账号的media_count,也就是它发布过的照片和视频数量。
想逐条帖子遍历账号的时间线而不是搜索它,可以把用户名发给 POST /user-tweets(在新标签页打开)。它返回同样的推文对象,每次请求 20 条,所以 photo_urls 无需改动就能读取其中的图片;纯文字帖子也会一并返回,所以任务需要更多请求。
#抓取话题标签或搜索结果中的图片
同一个脚本可以收集任意话题的图片:只需更换查询。filter:images 可以与所有 X 搜索运算符组合,搜索可回溯到 2006 年。
| 任务 | 查询 |
|---|---|
| 一个话题标签 | #nightphotography filter:images |
| 某个关键词在一个月内 | aurora filter:images since:2026-10-01 until:2026-11-01 |
| 只要热门图片 | #interiordesign filter:images min_faves:500 |
| 不含回复的账号 | from:northfield_photo_lab filter:images -filter:replies |
| 同时查几个账号 | (from:northfield_photo_lab OR from:harborline_studio_co) filter:images |
一个话题标签
- 查询
#nightphotography filter:images
某个关键词在一个月内
- 查询
aurora filter:images since:2026-10-01 until:2026-11-01
只要热门图片
- 查询
#interiordesign filter:images min_faves:500
不含回复的账号
- 查询
from:northfield_photo_lab filter:images -filter:replies
同时查几个账号
- 查询
(from:northfield_photo_lab OR from:harborline_studio_co) filter:images
对于大话题,每个月一个日期窗口,各放进自己的文件夹运行。每次运行都不长,失败的那次也容易重来,而这些窗口还能在一个密钥上并行运行,所有套餐都是每秒最多 20 次请求。把 order 设为 popular 可以先看 X 的热门帖子,设为 latest 则是最新的。每个运算符及其示例,都在 Twitter 搜索运算符指南里。
话题标签下的图片会随着人们发帖而变化,所以监控任务要按计划重新运行最新的窗口。由于脚本会跳过已有的文件,并且只记录新文件,每次重跑只会补上自上次以来发布的图片。
#获取每一张图片的原始尺寸
X 完整保存每一次上传,但提供的是较小的副本,你拿到哪个副本,只取决于链接里的 name 值。2026 年 11 月 2 日,我们以 X 提供的所有尺寸请求了四张公开图片,并测量了返回的结果;下面的阶梯是 2026 年 10 月下旬发布的一张照片。
- 不带参数的链接是
medium副本。 没有name的 pbs.twimg.com 链接,返回的是适配 1,200 像素的图片。 large最大到 2,048 像素,4096x4096最大到 4,096。 比这更小的上传,会以它自己的尺寸返回。name=orig返回上传的原文件。 对上面那张照片来说,就是 8,000 × 4,501 像素,像素数是不带参数链接的 44 倍。format=png不会增加任何细节。 它把 JPG 上传重新编码成大好几倍的 PNG,而且对 JPG 使用format=png&name=orig会返回 404,所以请保留链接原有的格式。
从 pbs.twimg.com 下载只是一次普通的 HTTP 请求,不需要密钥,所以文件本身不消耗 API 请求:只有查找帖子才消耗。
#不写代码批量下载图片
不写代码的话,这项工作分成两步:先导出图片链接,再下载文件。
-
导出近期帖子的链接。 免费的导出推文工具会把账号最新的帖子保存为带
media_urls列的 CSV、Excel 或 JSON;请打开“仅含媒体的帖子”。 -
导出整个账号或搜索结果。 在 API Playground 里,用你的密钥和
from:northfield_photo_lab filter:images这样的查询运行POST /search-tweets;它会替你沿着 cursor 往下读,并把每一页导出为 CSV 或 JSON。 -
把链接变成原图。 把图片链接粘贴到下面的转换器里,复制它返回的原始尺寸链接。
-
下载这份列表。 把链接载入任意接受 URL 列表的下载管理器,它一次运行就能保存全部文件。
每行一个,或粘贴整列 CSV。
3 个链接粘贴任意数量的 pbs.twimg.com 链接,每行一个,或直接从 CSV 列粘贴;内容不会离开你的浏览器。
#批量下载图片要花多少钱
在 Sorsa API 上,图片帖子每 1,000 条 $0.07 起,因为约 20 条帖子的一页搜索结果就是一次请求,不管其中有多少张照片。在官方 X API 上,图片链接只能通过它的媒体扩展字段获得。从 2026 年 11 月 22 日起,X 对每张展开的图片计费(在新标签页打开) $0.005,叠加在 $0.005 的帖子读取之上:带一张图片的帖子每 1,000 条 $10.00,每条带四张时是 $25.00。
并排来看,获取这些帖子的不同方式,对你的要求和价格一样各不相同。
| 获取帖子的方式 | 需要什么 | 每 1,000 条图片帖子的成本 |
|---|---|---|
| Sorsa API | 一个 API 密钥 | $0.07 起 |
| 官方 X API | 开发者账号和 OAuth | 自 2026 年 11 月 22 日起 $10.00(帖子加一张图片) |
| 扩展和下载器 | 你的 X 登录或会话 cookie | 免费或订阅制,账号有风险 |
Sorsa API
- 需要什么
- 一个 API 密钥
- 每 1,000 条图片帖子的成本
- $0.07 起
官方 X API
- 需要什么
- 开发者账号和 OAuth
- 每 1,000 条图片帖子的成本
- 自 2026 年 11 月 22 日起 $10.00(帖子加一张图片)
扩展和下载器
- 需要什么
- 你的 X 登录或会话 cookie
- 每 1,000 条图片帖子的成本
- 免费或订阅制,账号有风险
官方 X API
$10,000
100 万 条图片帖子
带一张图片的帖子每条 $0.010,自 11 月 22 日起
Sorsa API
$70 起
100 万 条图片帖子
每 1,000 条 $0.07 起
便宜 140 倍
官方 X API 的按量付费定价(在新标签页打开)同样在每个计费周期 300 万条帖子读取时封顶;超过之后,X 只出售 Enterprise 合同。在 Sorsa API 上,100 次免费请求可以覆盖约 2,000 条图片帖子,失败的请求从不收费,并且你所选套餐每 1,000 次请求的费率适用于所有接口。
时间由速率限制决定,所有套餐的每个密钥都是每秒 20 次请求。100 万条图片帖子需要 5 万次请求,在日期窗口并行运行时约 42 分钟;之后图片文件直接从 X 的服务器下载,速度取决于你的网络。
#获取指定帖子的图片
要获取你手上已有帖子的图片,可以把它们的链接或 ID 发给 POST /tweet-info-bulk(在新标签页打开):每次请求最多 100 条帖子,每 1,000 条 $0.014 起,返回的 entities 列表与搜索结果相同。
curl -X POST "https://api.sorsa.io/v3/tweet-info-bulk" \
-H "ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tweet_links": [
"https://x.com/northfield_photo_lab/status/1984213377309245441",
"1983850021164118016"]}'
单条帖子则带着 tweet_link 发给 POST /tweet-info(在新标签页打开)。把每张照片的链接交给同一个 orig 函数处理,就得到了原始大小的文件。在官方 X API 上,同样的查询需要 bearer 令牌加上 expansions=attachments.media_keys 和 media.fields=url,并且从 11 月 22 日起,返回结果中的每张图片还要加收 $0.005。
视频和 GIF 会在同一个 entities 列表里,以各自的 type 返回。它们的 MP4 文件和各种画质版本还要多一步,详见用 API 下载 Twitter 媒体指南;想要带媒体列的完整文本导出,请看如何下载用户的全部推文。
#开始你的第一次图片下载
要以完整尺寸下载 X 账号或搜索结果的每一张图片,可以用 Sorsa API:每次请求约 20 条图片帖子,每 1,000 条 $0.07 起,对你的 X 账号没有风险。复制脚本,填上你的密钥,100 次免费请求就能下载前 2,000 条图片帖子。
#常见问题
有免费的 Twitter 图片爬虫吗?
有,适合小任务。免费的媒体下载工具能以原始大小保存一条帖子里的每张照片,免费的导出推文工具能导出账号最新的帖子及其图片链接。针对整个账号和搜索,每个新的 Sorsa API 账号都有 100 次免费请求:约 2,000 条图片帖子,无需绑卡,请求也永不过期。
抓取 Twitter 图片应该用哪个 API?
用 Sorsa API:带 filter:images 的 POST /search-tweets 每次请求返回约 20 条图片帖子,每 1,000 条 $0.07 起,而官方 X API 从 2026 年 11 月 22 日起,带一张图片的帖子是每 1,000 条 $10.00。它只需一个 API 密钥,不需要开发者账号和 OAuth,搜索可回溯到 2006 年,所有套餐统一每秒 20 次请求,并且从不索要你的 X 登录信息。
从 Twitter 抓取图片合法吗?
是的。为研究或存档抓取公开图片,既合法又常见。每张图片的版权仍属于创作者,所以发布或出售这些文件之前,要先取得所有者的许可。Sorsa API 从不索要你的 X 登录信息,所以你的 X 账号不会被牵扯进来。
能下载私密 Twitter 账号的图片吗?
不能。受保护账号的帖子只对它批准的关注者可见,所以没有任何 API 会返回它们,Sorsa API 也不例外,对这类请求也不收费。声称能做到的工具,读取的是一个关注了该账号的账号的登录状态。
账号主人会看到我下载了他们的图片吗?
不会。X 不会告诉任何人他们的帖子或图片被查看或下载过。使用 Sorsa API 时,请求是从 Sorsa API 发往 X 的,所以无论 X 还是账号主人,都不会知道是谁请求的,你自己的 X 账号也没有参与其中。
能用同样的方式下载 Twitter 视频和 GIF 吗?
部分可以。视频和 GIF 与照片在同一个 entities 列表里,以各自的 type 出现,缩略图放在 preview 中,所以搜索和 cursor 的用法相同。可播放的 MP4 文件及其各种画质版本还要多一步,用 API 下载 Twitter 媒体的指南里有完整说明。
#我们如何核实
2026 年 11 月 2 日,我们以所有尺寸和格式从 pbs.twimg.com 请求了四张公开图片,并记录了每次返回的宽度和文件大小。我们还让一条公开帖子经过我们基于 Sorsa API 运行的免费媒体下载工具,查看它返回的照片链接。我们用一个 API 替身和真实的 pbs.twimg.com 文件,测试了两个脚本的链接处理和文件保存。接口、字段和每页大小来自线上的 Sorsa API 参考文档;官方 X API 的价格来自 docs.x.com 和 X 在 2026 年 10 月 23 日的通知,均在同一天查阅。文中展示的 API 响应是根据参考文档缩短的示例。