图片发帖(PostApi)¶
1. 概述¶
PostApi 是 Web 平台专属 的 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 实例。
签名
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
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 |
| 操作步骤 | 一步完成(上传 + 发布) | 两步分离(先上传,再发布) |
| 是否需要传尺寸 | 是(width、height) |
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. 注意事项¶
-
构造时只接受
WebClient:PostApi(client)中client必须是WebClient实例,传入Client(Android 平台)会导致构造失败。 -
登录区分:
user_posts()查询公开账号不需要登录;create_photo()发布帖子必须已登录,否则抛出PermissionError。 -
GraphQL 速率限制:
user_posts()基于 Instagram Web GraphQL 接口,每个 IP 的调用频率受限。批量查询时建议在相邻请求之间加入适当延时(如 2 至 5 秒)。 -
图片尺寸必须准确:
create_photo()要求传入正确的width和height。错误的尺寸信息可能导致发布失败或帖子展示异常,建议通过Pillow读取实际尺寸。 -
文案长度限制:
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>/。shortcode 与 media.id 是不同的标识符,当前 Media 对象暂未直接提供 shortcode 字段。可通过帖子 id 调用 MediaApi.info() 获取完整信息。
Q:如何发布多张图片(轮播帖)?
当前版本的 create_photo() 只支持单张图片。多图轮播帖(Carousel)功能在后续版本中规划支持。