跳转至

图片发帖(PostApi)

1. 概述

PostApiWeb 平台专属 的 API,只能通过 WebClient 构造,提供两个核心功能:

  • 获取用户帖子列表:通过 Instagram Web GraphQL 接口查询公开账号的帖子,无需登录
  • 发布图片帖子:将图片一步上传并发布为 Instagram 帖子,需要已登录的 WebClient

与 Android 平台 API 的区别:PostApi 使用 Web GraphQL 接口,FeedApi.user() 使用 Android 移动端接口。查询公开账号帖子推荐使用 PostApi.user_posts(),无需登录更便捷。

重要PostApi 只能接受 WebClient 实例。传入 Client(Android 平台)会导致构造失败。


2. 快速开始

获取公开账号的帖子(无需登录)

import asyncio
import igapi

async def main():
    web = igapi.WebClient()  # 无需登录
    post_api = igapi.PostApi(web)

    items, next_cursor = await post_api.user_posts("natgeo", count=12)

    for media in items:
        print(media.id, media.caption_text)

asyncio.run(main())

发布图片帖子(需要登录)

import asyncio
import igapi

async def main():
    account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
    web = igapi.WebClient(account=account)

    post_api = igapi.PostApi(web)

    with open("photo.jpg", "rb") as f:
        image_data = f.read()

    media = await post_api.create_photo(
        image_data=image_data,
        width=1080,
        height=1080,
        caption="这是一条测试帖子 #igapi"
    )

    print(f"发帖成功,帖子 ID: {media.id}")

asyncio.run(main())

3. API 参考

3.1 PostApi(client)

构造 PostApi 实例。

签名

igapi.PostApi(client: WebClient) -> PostApi

参数

参数 类型 必填 说明
client WebClient 必须是 WebClient 实例,不接受 Client(Android 平台)

异常

异常类型 触发条件
TypeError 传入的不是 WebClient 实例

3.2 post_api.user_posts()

通过 GraphQL 查询获取指定用户的帖子列表,无需登录,适用于公开账号。

签名

post_api.user_posts(
    username: str,
    count: int = 12,
    after: str | None = None
) -> tuple[list[Media], str | None]

参数

参数 类型 必填 默认值 说明
username str 目标用户名(字符串形式,非数字 ID)
count int 12 每页返回的帖子数量,建议不超过 50
after str \| None None 分页游标,None 从第一页开始

返回值

返回二元组 (items, next_cursor)

元素 类型 说明
items list[Media] 当前页的帖子列表
next_cursor str \| None 下一页游标,None 表示已到最后一页

异常

异常类型 触发条件
KeyError 用户名不存在或账号已停用
PermissionError 目标账号为私密账号(GraphQL 不返回私密账号内容)
RuntimeError 速率限制触发或网络错误

示例

import asyncio
import igapi

async def main():
    web = igapi.WebClient()
    post_api = igapi.PostApi(web)

    # 获取第一页(12 条)
    items, cursor = await post_api.user_posts("instagram")

    # 翻到下一页
    if cursor:
        items2, cursor2 = await post_api.user_posts("instagram", count=12, after=cursor)

asyncio.run(main())

3.3 post_api.create_photo()

上传图片并将其发布为 Instagram 帖子,需要已登录WebClient。上传与发布一步完成。

签名

post_api.create_photo(
    image_data: bytes,
    width: int,
    height: int,
    caption: str | None = None,
    disable_comments: bool = False,
    hide_like_count: bool = False
) -> Media

参数

参数 类型 必填 默认值 说明
image_data bytes JPEG 格式图片的二进制数据
width int 图片宽度(像素),必须与实际尺寸一致
height int 图片高度(像素),必须与实际尺寸一致
caption str \| None None 帖子文案,支持 emoji 和换行符 \n,最多 2200 字符
disable_comments bool False 是否禁用评论
hide_like_count bool False 是否隐藏点赞数

返回值

类型 说明
Media 发布成功的帖子信息

Media 对象字段:

字段 类型 说明
id str 帖子 ID
media_type int 媒体类型(1 = 图片,2 = 视频,8 = 轮播)
caption_text str 帖子文案文本
like_count int 点赞数
comment_count int 评论数

异常

异常类型 触发条件
PermissionError 客户端未登录
ValueError 图片格式不正确或尺寸不符合要求
RuntimeError 上传失败、发布失败或网络错误

示例

import asyncio
import igapi

async def main():
    account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
    web = igapi.WebClient(account=account)
    post_api = igapi.PostApi(web)

    with open("photo.jpg", "rb") as f:
        data = f.read()

    media = await post_api.create_photo(
        image_data=data,
        width=1080,
        height=1350,          # 4:5 竖版
        caption="秋天的落叶 🍂\n\n#autumn #photography",
        disable_comments=False,
        hide_like_count=False
    )

    print(f"帖子 ID: {media.id}")
    print(f"媒体类型: {media.media_type}")  # 1 = 图片
    print(f"文案: {media.caption_text}")

asyncio.run(main())

4. 发帖流程图

create_photo() 内部执行以下步骤:

flowchart TD
    A([开始]) --> B[读取图片 bytes]
    B --> C[验证登录状态]
    C --> D{已登录?}
    D -- 否 --> E([抛出 PermissionError])
    D -- 是 --> F[生成 upload_id\n计算图片尺寸]
    F --> G[分片上传图片到 rupload 端点]
    G --> H{上传成功?}
    H -- 否 --> I([抛出 RuntimeError])
    H -- 是 --> J[调用 configure_web 发布帖子\n附带 caption / width / height / 选项]
    J --> K{发布成功?}
    K -- 否 --> L([抛出 RuntimeError])
    K -- 是 --> M[返回 Media 对象]
    M --> N([结束])

5. 代码示例

5.1 遍历获取用户全部帖子

import asyncio
import igapi

async def main():
    web = igapi.WebClient()
    post_api = igapi.PostApi(web)

    username = "natgeo"
    all_posts = []
    cursor = None

    while True:
        items, cursor = await post_api.user_posts(username, count=50, after=cursor)
        all_posts.extend(items)
        print(f"已获取 {len(all_posts)} 条帖子...")

        if cursor is None:
            break

    print(f"共获取 {username}{len(all_posts)} 条帖子")

asyncio.run(main())

5.2 发布带文案的图片帖子

import asyncio
import igapi

async def main():
    account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
    web = igapi.WebClient(account=account)
    post_api = igapi.PostApi(web)

    with open("landscape.jpg", "rb") as f:
        data = f.read()

    media = await post_api.create_photo(
        image_data=data,
        width=1920,
        height=1080,
        caption="山间晨雾,岁月静好。\n\n#morning #landscape #travel"
    )

    print(f"发帖成功!帖子 ID: {media.id}")

asyncio.run(main())

5.3 使用 Pillow 自动读取尺寸后发布

发布前需要提供准确的图片尺寸,推荐使用 Pillow 自动读取:

import asyncio
import io
import igapi
from PIL import Image

async def main():
    account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
    web = igapi.WebClient(account=account)
    post_api = igapi.PostApi(web)

    img_path = "photo.png"
    with Image.open(img_path) as img:
        width, height = img.size
        # PNG 转换为 JPEG
        buf = io.BytesIO()
        img.convert("RGB").save(buf, format="JPEG", quality=95)
        image_data = buf.getvalue()

    media = await post_api.create_photo(
        image_data=image_data,
        width=width,
        height=height,
        caption="自动读取尺寸发布"
    )
    print(f"发帖成功:{media.id}")

asyncio.run(main())

5.4 批量查询多个用户帖子并处理异常

import asyncio
import igapi

async def main():
    web = igapi.WebClient()
    post_api = igapi.PostApi(web)

    usernames = ["user1", "user2", "user3"]

    for username in usernames:
        try:
            items, _ = await post_api.user_posts(username, count=12)
            print(f"{username}: {len(items)} 条帖子")
            for m in items:
                print(f"  [{m.media_type}] {m.id} 赞: {m.like_count}")
        except KeyError:
            print(f"{username}: 用户不存在")
        except PermissionError:
            print(f"{username}: 私密账号,无法访问")
        except RuntimeError as e:
            print(f"{username}: 请求失败 - {e}")

asyncio.run(main())

6. PostApi 与 UploadApi 的区别

对比项 PostApi.create_photo() UploadApi.photo() + configure()
适用平台 仅 WebClient Client(Android)或 WebClient
操作步骤 一步完成(上传 + 发布) 两步分离(先上传,再发布)
是否需要传尺寸 是(widthheight Android 端不需要,Web 端需要
是否支持 location_id Android 端 configure() 支持
适用场景 简单发帖,无需额外控制 需要在上传和发布之间插入自定义逻辑

7. 图片要求

项目 要求
格式 JPEG(.jpg)。PNG 需先转换为 JPEG
最小尺寸 320 x 320 像素
最大尺寸 1440 x 1440(正方形)、1440 x 1800(竖向)、1440 x 813(横向)
常用宽高比 1:1(正方形)、4:5(竖版,推荐)、1.91:1(横版)
文件大小 建议不超过 8 MB
色彩空间 RGB(不支持 CMYK)

8. 注意事项

  1. 构造时只接受 WebClientPostApi(client)client 必须是 WebClient 实例,传入 Client(Android 平台)会导致构造失败。

  2. 登录区分user_posts() 查询公开账号不需要登录;create_photo() 发布帖子必须已登录,否则抛出 PermissionError

  3. GraphQL 速率限制user_posts() 基于 Instagram Web GraphQL 接口,每个 IP 的调用频率受限。批量查询时建议在相邻请求之间加入适当延时(如 2 至 5 秒)。

  4. 图片尺寸必须准确create_photo() 要求传入正确的 widthheight。错误的尺寸信息可能导致发布失败或帖子展示异常,建议通过 Pillow 读取实际尺寸。

  5. 文案长度限制caption 最长支持 2200 个字符,超出会被 Instagram 服务端拒绝。


9. 常见问题

Q:user_posts()FeedApi.user() 有什么区别?

两者都能获取用户帖子,但底层接口不同:

对比项 PostApi.user_posts() FeedApi.user()
平台 Web(GraphQL) Android(移动 API)
是否需要登录 不需要(公开账号) 需要
参数类型 用户名(字符串) 用户 ID(整数)
返回数据丰富度 基础字段 更丰富的字段

Q:发帖后如何获取帖子的公开链接?

Instagram 帖子链接格式为 https://www.instagram.com/p/<shortcode>/shortcodemedia.id 是不同的标识符,当前 Media 对象暂未直接提供 shortcode 字段。可通过帖子 id 调用 MediaApi.info() 获取完整信息。


Q:如何发布多张图片(轮播帖)?

当前版本的 create_photo() 只支持单张图片。多图轮播帖(Carousel)功能在后续版本中规划支持。