如何用 Python 获取 Instagram 用户帖子并实现分页
做红人筛选、竞品内容监测或社媒聆听时,最常见的需求就是:给定一个 Instagram 账号,把它近期发布的帖子结构化地拉下来——文案、发布时间、点赞与评论数、媒体链接。Instagram 数据接口把这件事变成普通的 GET 请求,不需要 Instagram 账号、Cookie 或手机端环境。本文用 Python 演示完整链路:把用户名解析成数字用户 ID,逐页拉取帖子并按媒体 ID 去重,同时处理一个容易踩的坑——账号不存在时接口返回的仍然是 HTTP 200。
接口一览
本文涉及四个操作,完整参数见 Instagram 数据 API 文档页:
| 操作 | 必填参数 | 用途 |
|---|---|---|
user-info | username_or_id | 按用户名或 ID 解析资料,得到稳定的数字用户 ID |
user-posts | username_or_id | 分页拉取账号的近期帖子(图片、视频、轮播混合) |
user-reels | username_or_id | 只拉 Reels 短视频,省去自己过滤混合动态 |
post-detail | code_or_url | 按帖子短码或完整链接补全单条详情 |
准备工作
注册并在控制台复制自己的 API Key,新账号自带免费测试额度。本文示例只访问公开账号的公开内容。请把 Key 放在服务端环境变量里(下文用 OPENDATA_API_KEY),不要写进前端代码或公开仓库;Key 出现在查询参数中,可能进入访问日志,注意脱敏。
第一步:用户名转用户 ID
user-info 同时接受用户名(如 instagram)和数字 ID(如 25025320),两者解析到同一份资料:
curl --get 'https://api.opendata-api.com/api/project/instagram/user-info?key=YOUR_API_KEY' \
--data-urlencode 'username_or_id=instagram'
响应外层是 {code, message, data} 信封:code=200 只表示平台受理了请求,业务结果要看 data。命中时 data 内含 id、username、biography、follower_count、is_verified、is_private 等字段。需要特别注意:用户名不存在时 HTTP 仍是 200,而 data 里只有 {"detail":"Not found"}——这是业务层的"未找到",应当停止后续流程,既不能当成一份资料入库,也不要原地重试。
用户名可能更改,数字 ID 稳定。建议解析一次后把 ID 存下来,后续接口全部复用它。
第二步:拉取第一页帖子
user-posts 的第一页请求不带 pagination_token:
curl --get 'https://api.opendata-api.com/api/project/instagram/user-posts?key=YOUR_API_KEY' \
--data-urlencode 'username_or_id=25025320'
data 内包含本页条数 count、帖子数组 items[]、作者摘要 user,以及用于翻页的 pagination_token。每条帖子的关键字段:
| 字段 | 含义 |
|---|---|
id | 数字媒体 ID,跨接口关联与去重的键 |
code | 帖子短码,可拼接 URL 或传给 post-detail |
taken_at | 发布时间(Unix 秒) |
caption.text | 文案,含提取出的话题标签与提及 |
media_type | 1=图片,2=视频,8=轮播 |
like_count / comment_count / play_count | 点赞、评论、播放数快照(视频才有播放数) |
image_versions2 / video_versions / carousel_media | 各规格媒体 URL |
第三步:Python 连续翻页并去重
pagination_token 是不透明令牌:原样带回,不要解析或自行拼接;响应里不再返回它,就说明没有更多页了。相邻页之间可能重叠,必须按媒体 id 去重:
import os
import time
import requests
BASE = "https://api.opendata-api.com/api/project/instagram"
KEY = os.environ["OPENDATA_API_KEY"].strip()
def call(operation_id, params):
resp = requests.get(
f"{BASE}/{operation_id}",
params={"key": KEY, **params},
timeout=30,
)
resp.raise_for_status() # HTTP layer: 4xx/5xx means transport or quota trouble
envelope = resp.json()
if str(envelope.get("code")) != "200":
raise RuntimeError(f"Platform rejected the request: {envelope.get('message')}")
return envelope.get("data")
def resolve_user_id(username):
data = call("user-info", {"username_or_id": username})
# Business-level miss: HTTP 200 but data is {"detail": "Not found"}
if not isinstance(data, dict) or data.get("detail"):
return None
return str(data.get("id"))
def iter_posts(username_or_id, max_pages=10):
token = None # First request goes out without pagination_token
seen = set()
for _ in range(max_pages):
params = {"username_or_id": username_or_id}
if token:
params["pagination_token"] = token # Opaque: pass it back verbatim
data = call("user-posts", params)
if not isinstance(data, dict) or data.get("detail"):
return
for item in data.get("items") or []:
media_id = item.get("id")
if not media_id or media_id in seen:
continue # Adjacent pages can overlap; deduplicate by media id
seen.add(media_id)
yield item
token = data.get("pagination_token")
if not token:
return # No token means no more pages
time.sleep(1) # Pause between pages
user_id = resolve_user_id("instagram") or "instagram"
for post in iter_posts(user_id):
caption = (post.get("caption") or {}).get("text", "")
print(post.get("code"), post.get("taken_at"), post.get("like_count"), caption[:30])
翻页与媒体链接的注意事项
- 页大小不是承诺:每页条数以实际返回的
items长度为准,合并多页时始终按id去重,不要假设固定页长。 - 媒体 URL 有时效:
image_versions2、video_versions里的链接会过期,需要长期展示时应另存素材并在过期后重新拉取;要在跨域页面直接嵌入时,请求里加url_embed_safe=true。 - 只覆盖公开内容:私密账号的资料摘要(用户名、头像、粉丝数、
is_private标记)可见,但其帖子、Reels、Stories 等内容不可获取;采集任务应先看is_private再决定是否排队。 - 各接口翻页字段略有差异:粉丝列表、关注列表、Reels、评论等同样使用
pagination_token;但话题标签的 top 信息流用next_page,帖子点赞列表用end_cursor,接入新接口前先确认字段名,不要混用。
典型使用场景
- 红人筛选:
user-search按关键词找候选账号,user-info读粉丝数与认证标记,user-posts看近期发帖频率和互动水平,最后人工复核。 - 竞品内容监测:定时任务逐页拉取竞品账号新帖,按媒体
id落库,第一时间发现活动帖上线。 - 短视频选题研究:用
user-reels只看 Reels 样本,结合post-detail返回的音频元数据研究选题与配乐趋势。 - 社媒聆听:帖子入库后接
post-comments与评论回复接口,做话题与口碑的聚合分析。
常见问题
如何用 Python 根据用户名获取 Instagram 帖子并连续翻页?
先用 user-info 把用户名解析成数字 ID(也可以直接传用户名),再循环调用 user-posts:第一次不传 pagination_token,之后每次把上一页返回的令牌原样带回,直到响应里不再出现该字段。完整代码见上文。
Instagram 帖子分页重复返回时如何去重?
翻页过程中相邻页可能重叠。把每条帖子的数字媒体 ID(id 字段)放入集合,已见过的直接跳过;不要按"每页条数 × 页数"估算总量。
如何通过 Instagram 用户名查询数字用户 ID?
调用 user-info 并传 username_or_id=用户名,返回 data.id 就是数字用户 ID。建议解析一次后入库,后续所有接口复用这个稳定 ID。
如何根据 Instagram 帖子链接或 shortcode 获取详情?
截取链接中 /p/、/reel/ 或 /tv/ 后面的短码(或直接传整条 URL)给 post-detail 的 code_or_url 参数,返回的 data 含数字媒体 ID、作者、文案、互动数与媒体链接。
接口返回 HTTP 200 但 detail 是 Not found 应该怎样处理? 这是业务层的"未找到",不是故障:停止重试,不入库、不展示,按"账号不存在或已改名"走自己的业务流程,必要时隔一段时间再复核。
Instagram 私密账号有哪些资料可见,哪些内容不能获取?
私密账号的公开资料字段(用户名、头像、粉丝数、is_private 标记等)可见,但帖子、Reels、Stories、Highlights 等内容不可获取。本服务只覆盖公开内容,私密内容没有任何接口可以绕过。
出海品牌如何用 Instagram 用户资料和近期帖子筛选红人? 关键词搜索找候选 → 资料接口看粉丝量级、认证与简介 → 帖子接口看近期互动快照与内容调性 → 人工复核后进入候选名单。注意这些是公开数据快照,不是官方账号后台的 Insights 数据。
更多支持
如果你还需要 TikTok、YouTube、Facebook、Threads、X、Pinterest、Snapchat、Reddit、小红书、抖音等相似平台的数据接口,可以通过联系我们页面提出需求,我们会按需评估新增。对数据安全或调用量有更高要求的团队,也支持源码级私有化部署。
立即试用
在 Instagram 数据 API 页面在线测试,或阅读 user-posts、user-info、user-reels 与 post-detail 的参考文档。单次调用费用见定价。