图片上传(UploadApi)¶
1. 概述¶
UploadApi 提供底层图片上传能力,将图片数据上传到 Instagram 的上传服务器,返回 upload_id,用于后续的发帖配置(configure)步骤。
UploadApi 支持 Android 端 和 Web 端 两条路径:
- Android 端(
photo()+configure()):通过Client或WebClient均可构造,使用 Android 移动端上传协议,支持location_id位置标签 - Web 端(
photo_web()+configure_web()):仅限WebClient构造的UploadApi实例,使用 Web rupload 协议,需要额外提供图片尺寸
在大多数发帖场景中,如果使用 Web 平台,推荐直接使用 PostApi.create_photo()(一步完成上传 + 发布)。UploadApi 适合以下高级场景:
- 在上传和发布之间插入自定义逻辑(如进度显示)
- 使用 Android 平台客户端发帖
- 需要添加
location_id位置标签
说明:
UploadApi本身只负责上传,不会发布帖子。上传完成后需要调用configure()或configure_web()才能将图片发布为帖子。
2. 快速开始¶
Android 端上传并发布¶
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||")
client = igapi.Client(account=account)
upload_api = igapi.UploadApi(client)
with open("photo.jpg", "rb") as f:
image_data = f.read()
# 步骤 1:上传图片
result = await upload_api.photo(image_data)
print(f"上传成功,upload_id: {result.upload_id}")
# 步骤 2:配置并发布
media = await upload_api.configure(
upload_id=result.upload_id,
caption="第一张通过 igapi 发布的照片"
)
print(f"发帖成功,帖子 ID: {media.id}")
asyncio.run(main())
Web 端上传并发布¶
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
web = igapi.WebClient(account=account)
upload_api = igapi.UploadApi(web)
with open("photo.jpg", "rb") as f:
image_data = f.read()
# Web 端上传需要提供尺寸
result = await upload_api.photo_web(image_data, width=1080, height=1080)
print(f"Web 上传成功,upload_id: {result.upload_id}")
# Web 端配置发布
media = await upload_api.configure_web(
upload_id=result.upload_id,
caption="Web 端发布的照片"
)
print(f"发帖成功,帖子 ID: {media.id}")
asyncio.run(main())
3. API 参考¶
3.1 UploadApi(client)¶
构造 UploadApi 实例。
签名
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
client |
Client \| WebClient |
是 | Android 平台客户端或 Web 平台客户端均可 |
说明
构造时传入的客户端类型决定了哪些方法可用:
- 传入
Client(Android):可调用photo()和configure();调用photo_web()或configure_web()会抛出TypeError - 传入
WebClient:全部四个方法均可调用
3.2 upload_api.photo()¶
Android 端图片上传,将图片数据上传到 Instagram 上传服务器。
签名
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
image_data |
bytes |
是 | 图片的二进制数据,建议使用 JPEG 格式 |
返回值
返回 UploadResult 对象(详见第 4 节)。
异常
| 异常类型 | 触发条件 |
|---|---|
PermissionError |
客户端未登录 |
RuntimeError |
网络错误或上传失败 |
示例
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||")
client = igapi.Client(account=account)
upload_api = igapi.UploadApi(client)
with open("photo.jpg", "rb") as f:
data = f.read()
result = await upload_api.photo(data)
print(result)
# UploadResult(upload_id='...', status='ok', offset=...)
asyncio.run(main())
3.3 upload_api.configure()¶
将已上传的图片(Android 端上传)配置为帖子并发布。
签名
upload_api.configure(
upload_id: str,
caption: str | None = None,
location_id: int | None = None,
disable_comments: bool = False,
hide_like_count: bool = False
) -> Media
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
upload_id |
str |
是 | — | photo() 返回的 upload_id |
caption |
str \| None |
否 | None |
帖子文案,支持 emoji 和换行符 \n,最多 2200 字符 |
location_id |
int \| None |
否 | None |
位置标签 ID(可选,configure_web() 不支持此参数) |
disable_comments |
bool |
否 | False |
是否禁用评论 |
hide_like_count |
bool |
否 | False |
是否隐藏点赞数 |
返回值
| 类型 | 说明 |
|---|---|
Media |
发布成功的帖子信息 |
异常
| 异常类型 | 触发条件 |
|---|---|
PermissionError |
客户端未登录 |
ValueError |
upload_id 无效或已过期 |
RuntimeError |
发布失败或网络错误 |
3.4 upload_api.photo_web()¶
Web 端图片上传,仅限通过 WebClient 构造的 UploadApi 实例调用。
签名
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
image_data |
bytes |
是 | JPEG 格式图片的二进制数据 |
width |
int |
是 | 图片宽度(像素),必须与实际尺寸匹配 |
height |
int |
是 | 图片高度(像素),必须与实际尺寸匹配 |
返回值
返回 UploadResult 对象(字段与 photo() 相同)。
异常
| 异常类型 | 触发条件 |
|---|---|
TypeError |
通过 Client(Android 平台)构造的 UploadApi 调用时抛出,错误信息:photo_web() requires WebClient |
PermissionError |
客户端未登录 |
RuntimeError |
网络错误或上传失败 |
示例
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
# 正确用法:通过 WebClient 构造
web = igapi.WebClient(account=account)
upload_api = igapi.UploadApi(web)
result = await upload_api.photo_web(data, width=1080, height=1080)
# 错误用法:通过 Client 构造(会抛出 TypeError)
# client = igapi.Client(account=account)
# upload_api = igapi.UploadApi(client)
# result = await upload_api.photo_web(data, 1080, 1080)
# TypeError: photo_web() requires WebClient
asyncio.run(main())
3.5 upload_api.configure_web()¶
将已上传的图片(Web 端上传)配置为帖子并发布,仅限通过 WebClient 构造的实例。
签名
upload_api.configure_web(
upload_id: str,
caption: str | None = None,
disable_comments: bool = False,
hide_like_count: bool = False
) -> Media
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
upload_id |
str |
是 | — | photo_web() 返回的 upload_id |
caption |
str \| None |
否 | None |
帖子文案 |
disable_comments |
bool |
否 | False |
是否禁用评论 |
hide_like_count |
bool |
否 | False |
是否隐藏点赞数 |
返回值
| 类型 | 说明 |
|---|---|
Media |
发布成功的帖子信息 |
异常
| 异常类型 | 触发条件 |
|---|---|
TypeError |
通过 Client 构造的 UploadApi 调用时抛出,错误信息:configure_web() requires WebClient |
PermissionError |
客户端未登录 |
RuntimeError |
发布失败或网络错误 |
4. 数据类型¶
4.1 UploadResult¶
上传操作的返回结果,包含后续 configure() 所需的 upload_id。
| 字段 | 类型 | 说明 |
|---|---|---|
upload_id |
str |
上传标识符,传给 configure() 或 configure_web() |
status |
str |
上传状态,成功时为 "ok" |
offset |
int |
已上传字节偏移量(分片上传时使用) |
示例
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||")
client = igapi.Client(account=account)
upload_api = igapi.UploadApi(client)
with open("photo.jpg", "rb") as f:
data = f.read()
result = await upload_api.photo(data)
print(result)
# UploadResult(upload_id='1234567890_123', status='ok', offset=204800)
print(result.upload_id) # '1234567890_123'
print(result.status) # 'ok'
print(result.offset) # 204800
asyncio.run(main())
4.2 UploadProgress¶
上传进度信息(供进度回调使用)。
| 字段 | 类型 | 说明 |
|---|---|---|
uploaded_bytes |
int |
已上传字节数 |
total_bytes |
int |
总字节数 |
stage |
str |
当前阶段描述(中文,如 "上传中"、"已完成") |
percentage |
float |
完成比例,范围 0.0 至 1.0 |
方法
| 方法 | 返回值 | 说明 |
|---|---|---|
is_complete() |
bool |
当 stage == "已完成" 时返回 True |
示例
print(progress)
# UploadProgress(stage='上传中', progress=45.3%)
print(f"进度: {progress.percentage * 100:.1f}%")
print(f"已完成: {progress.is_complete()}")
4.3 Media¶
发布成功的帖子信息。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
str |
帖子 ID |
media_type |
int |
媒体类型(1 = 图片,2 = 视频,8 = 轮播) |
caption_text |
str |
帖子文案文本 |
like_count |
int |
点赞数 |
comment_count |
int |
评论数 |
5. 上传流程图¶
Android 端上传流程¶
flowchart TD
A([开始]) --> B[构造 UploadApi\nigapi.UploadApi\x28client\x29]
B --> C[读取图片 bytes]
C --> D[调用 photo\x28image_data\x29]
D --> E{上传成功?}
E -- 否 --> F([抛出 RuntimeError])
E -- 是 --> G[获取 UploadResult\nupload_id / status / offset]
G --> H[调用 configure\x28upload_id, ...\x29]
H --> I{发布成功?}
I -- 否 --> J([抛出 RuntimeError])
I -- 是 --> K[返回 Media 对象]
K --> L([结束])
Web 端上传流程¶
flowchart TD
A([开始]) --> B[构造 UploadApi\nigapi.UploadApi\x28web\x29]
B --> C[读取图片 bytes\n获取 width / height]
C --> D[调用 photo_web\x28image_data, width, height\x29]
D --> E{是 WebClient?}
E -- 否 --> F([抛出 TypeError])
E -- 是 --> G{上传成功?}
G -- 否 --> H([抛出 RuntimeError])
G -- 是 --> I[获取 UploadResult]
I --> J[调用 configure_web\x28upload_id, ...\x29]
J --> K{发布成功?}
K -- 否 --> L([抛出 RuntimeError])
K -- 是 --> M[返回 Media 对象]
M --> N([结束])
6. 代码示例¶
6.1 Android 端完整上传发布流程¶
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||")
client = igapi.Client(account=account)
upload_api = igapi.UploadApi(client)
with open("landscape.jpg", "rb") as f:
image_data = f.read()
# 上传
result = await upload_api.photo(image_data)
print(f"上传完成,upload_id: {result.upload_id},状态: {result.status}")
# 发布
media = await upload_api.configure(
upload_id=result.upload_id,
caption="清晨的山谷,薄雾轻绕。\n\n#nature #landscape",
disable_comments=False,
hide_like_count=False
)
print(f"发帖成功!媒体 ID: {media.id},点赞数: {media.like_count}")
asyncio.run(main())
6.2 Web 端完整上传发布流程¶
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||", platform="web")
web = igapi.WebClient(account=account)
upload_api = igapi.UploadApi(web)
with open("portrait.jpg", "rb") as f:
image_data = f.read()
# Web 端上传(需要提供尺寸)
result = await upload_api.photo_web(image_data, width=1080, height=1350)
print(f"Web 上传成功,upload_id: {result.upload_id}")
# Web 端发布
media = await upload_api.configure_web(
upload_id=result.upload_id,
caption="人像摄影作品 #portrait #photography",
hide_like_count=True
)
print(f"发帖成功!媒体 ID: {media.id}")
asyncio.run(main())
6.3 使用 Pillow 自动读取图片尺寸¶
Web 端上传需要提供准确的图片尺寸,推荐使用 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)
upload_api = igapi.UploadApi(web)
img_path = "photo.jpg"
with Image.open(img_path) as img:
width, height = img.size
# 确保是 JPEG 格式,PNG 需要转换
if img.format != "JPEG":
buf = io.BytesIO()
img.convert("RGB").save(buf, format="JPEG", quality=95)
image_data = buf.getvalue()
else:
with open(img_path, "rb") as f:
image_data = f.read()
result = await upload_api.photo_web(image_data, width=width, height=height)
media = await upload_api.configure_web(result.upload_id, caption="自动读取尺寸上传")
print(f"发帖成功:{media.id}")
asyncio.run(main())
6.4 错误处理示例¶
import asyncio
import igapi
async def main():
account = igapi.AccountInfo.parse("用户名:密码||设备信息|cookies||")
client = igapi.Client(account=account)
upload_api = igapi.UploadApi(client)
with open("photo.jpg", "rb") as f:
image_data = f.read()
try:
result = await upload_api.photo(image_data)
media = await upload_api.configure(result.upload_id, caption="测试帖子")
print(f"发帖成功:{media.id}")
except PermissionError:
print("错误:未登录,请先完成登录")
except ValueError as e:
print(f"参数错误(upload_id 无效或已过期):{e}")
except RuntimeError as e:
print(f"运行时错误(网络或服务端问题):{e}")
except TypeError as e:
print(f"类型错误(photo_web/configure_web 使用了错误的客户端类型):{e}")
asyncio.run(main())
7. 平台差异¶
| 对比项 | Android 端(photo() + configure()) |
Web 端(photo_web() + configure_web()) |
|---|---|---|
| 可用客户端 | Client 或 WebClient |
仅 WebClient |
| 上传时是否需要提供尺寸 | 不需要 | 必须提供 width 和 height |
| 上传协议 | Android rupload 协议 | Web rupload 协议 |
是否支持 location_id |
支持(configure() 参数) |
不支持 |
| 推荐使用场景 | 使用 Android 平台客户端,或需要位置标签 | 使用 WebClient 发帖 |
两种上传方式的
UploadResult结构完全相同,均包含upload_id、status、offset三个字段。
8. 注意事项¶
-
photo_web()和configure_web()只能在WebClient上调用:通过Client(Android 平台)构造的UploadApi调用这两个方法会抛出TypeError。 -
上传和配置必须配对:Android 端上传(
photo())必须使用configure()发布;Web 端上传(photo_web())必须使用configure_web()发布。混用协议会导致发布失败。 -
upload_id有效期:upload_id在上传完成后有一定的有效期(通常几小时),超期需重新上传。 -
图片格式建议:建议使用 JPEG 格式,RGB 色彩空间,文件大小不超过 8 MB。Web 端对格式要求更为严格。
-
Web 端尺寸必须准确:
photo_web()要求width和height与图片实际尺寸一致。填写错误的尺寸会导致帖子展示异常或发布失败。 -
简化发帖推荐使用
PostApi:如果使用 Web 平台且无需分步控制,直接使用igapi.PostApi(web).create_photo()更简洁,内部已封装上传和配置两步。
9. 常见问题¶
Q:photo_web() 抛出 TypeError: photo_web() requires WebClient,怎么解决?
原因是通过 Client(Android 平台)构造了 UploadApi,而非 WebClient。请确认使用正确的客户端:
# 错误
client = igapi.Client(account=account)
upload_api = igapi.UploadApi(client)
upload_api.photo_web(data, 1080, 1080) # TypeError!
# 正确
web = igapi.WebClient(account=account)
upload_api = igapi.UploadApi(web)
upload_api.photo_web(data, 1080, 1080) # 正常
Q:Web 端上传时不知道图片尺寸怎么办?
使用 Pillow 库读取图片尺寸(参考第 6.3 节示例)。传入错误的尺寸值会导致帖子展示异常或发布失败,建议始终从实际图片数据中读取尺寸,不要手动估算。
Q:UploadApi 和 PostApi.create_photo() 应该用哪个?
| 场景 | 推荐方案 |
|---|---|
| Web 平台发布单张图片,无特殊需求 | PostApi.create_photo(),更简洁 |
| Android 平台客户端发帖 | UploadApi.photo() + configure() |
需要添加位置标签(location_id) |
UploadApi.configure() |
| 需要在上传和发布之间插入自定义逻辑 | UploadApi,分步控制 |
Q:configure() 中的 location_id 如何获取?
location_id 是 Instagram 地点的数字 ID,可以通过 Instagram 地点搜索接口获取。当前版本暂未提供直接的位置搜索 API,可通过 Web 端 GraphQL 或第三方工具查询特定地点的 ID 后传入。