如何用 Python 获取 Instagram 用户帖子并实现分页

发布于 2026年10月7日

做红人筛选、竞品内容监测或社媒聆听时,最常见的需求就是:给定一个 Instagram 账号,把它近期发布的帖子结构化地拉下来——文案、发布时间、点赞与评论数、媒体链接。Instagram 数据接口把这件事变成普通的 GET 请求,不需要 Instagram 账号、Cookie 或手机端环境。本文用 Python 演示完整链路:把用户名解析成数字用户 ID,逐页拉取帖子并按媒体 ID 去重,同时处理一个容易踩的坑——账号不存在时接口返回的仍然是 HTTP 200。

接口一览

本文涉及四个操作,完整参数见 Instagram 数据 API 文档页:

操作必填参数用途
user-infousername_or_id按用户名或 ID 解析资料,得到稳定的数字用户 ID
user-postsusername_or_id分页拉取账号的近期帖子(图片、视频、轮播混合)
user-reelsusername_or_id只拉 Reels 短视频,省去自己过滤混合动态
post-detailcode_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_type1=图片,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 的参考文档。单次调用费用见定价。