Python 小红书 API:`XhsClient` 接口文档
概览
XhsClient 是小红书 Web/创作平台的底层 API 客户端,通过多个 Mixin 组合了阅读、互动、创作、社交、通知、认证六大领域的端点方法。客户端内部负责:
- 自动签名:主站 API 使用
sign_main_api,创作者 API 使用sign_creator - 限速:带高斯抖动的随机延迟,模拟人类浏览行为
- 重试:对网络错误和 429/5xx 进行指数退避重试
- 验证码处理:遇到 461/471 时冷却并抛出
NeedVerifyError - Cookie 回写:响应中的 Set-Cookie 会合并回内存会话
class XhsClient(
ReadingEndpointsMixin,
InteractionEndpointsMixin,
CreatorEndpointsMixin,
SocialEndpointsMixin,
NotificationEndpointsMixin,
AuthEndpointsMixin,
):
安装及导入
pip install git+https://github.com/jackwener/xiaohongshu-cli
from xhs_cli.client import XhsClient
1. 构造函数
XhsClient(
cookies: dict[str, str],
timeout: float = 30.0,
request_delay: float = 1.0,
max_retries: int = 3,
)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cookies |
dict[str, str] |
— | 登录态 Cookie 字典,至少需包含 a1(创作者 API 签名依赖) |
timeout |
float |
30.0 |
HTTP 请求超时(秒) |
request_delay |
float |
1.0 |
请求间最小间隔(秒),设为 0 关闭限速 |
max_retries |
int |
3 |
网络错误/5xx 的最大重试次数 |
实例属性
| 属性 | 类型 | 说明 |
|---|---|---|
cookies |
dict[str, str] |
当前会话 Cookie,会随响应动态更新 |
_http |
httpx.Client |
底层 HTTP 会话 |
_request_delay |
float |
当前生效的请求间隔(验证码后会自动翻倍) |
_base_request_delay |
float |
初始请求间隔 |
_max_retries |
int |
最大重试次数 |
_last_request_time |
float |
上次请求时间戳 |
_verify_count |
int |
连续触发验证码的次数 |
_request_count |
int |
累计请求数 |
2. 生命周期方法
close() -> None
关闭底层 httpx.Client。建议在程序结束时调用。
__enter__() / __exit__(*args)
支持 with 语句,退出时自动调用 close()。
with XhsClient(cookies) as client:
info = client.get_self_info()
3. 读取类接口(ReadingEndpointsMixin)
resolve_xsec_context(note_id, preferred_token="", preferred_source="") -> tuple[str, str]
解析笔记的 xsec_token / xsec_source。优先级:显式传入 → 本地缓存 → 抓取笔记 HTML 页面解析。成功解析后会写入缓存。
resolve_xsec_token(note_id, preferred_token="") -> str
同上,仅返回 xsec_token。
get_self_info() -> dict[str, Any]
获取当前登录用户信息(/api/sns/web/v2/user/me)。
get_user_info(user_id: str) -> dict[str, Any]
获取指定用户资料(/api/sns/web/v1/user/otherinfo)。
get_user_notes(user_id: str, cursor: str = "") -> dict[str, Any]
获取用户发布的笔记列表(/api/sns/web/v1/user_posted),每页 30 条。
search_notes(keyword, page=1, page_size=20, sort="general", note_type=0) -> Any
搜索笔记(/api/sns/web/v1/search/notes)。
- 内部维护搜索会话缓存(按
keyword + sort + note_type维度,TTL 600 秒,最多 128 条,持久化到配置目录下的search_sessions.json),同一会话复用search_id - 新会话会先调用
search/onebox和search/filter预热,搜索后再预取search/recommend(预热失败仅记录 debug 日志,不中断)
get_note_by_id(note_id, xsec_token="", xsec_source="pc_feed") -> Any
通过 feed API 获取笔记详情(/api/sns/web/v1/feed)。若提供 xsec_token 会写入缓存。
get_note_from_html(note_id, xsec_token="", xsec_source="pc_feed") -> dict[str, Any]
抓取笔记 HTML 页面并用 extract_note_from_html 解析,无需 xsec_token。
get_note_detail(note_id, xsec_token="", xsec_source="") -> dict[str, Any]
读取笔记的"最佳通道"封装:
- 有
xsec_token(显式或缓存)→ 先尝试 feed API,失败回退 HTML - 无
xsec_token→ 直接走 HTML - 若使用的是缓存 token 且 feed API 失败 → 使缓存失效后重试
get_home_feed(category="homefeed_recommend") -> dict[str, Any]
获取首页推荐流(/api/sns/web/v1/homefeed),每页 40 条。
get_hot_feed(category="homefeed.fashion_v3") -> dict[str, Any]
热点流,实际调用 get_home_feed(category=...)。
get_comments(note_id, cursor="", xsec_token="", top_comment_id="", xsec_source="") -> Any
获取笔记一级评论(/api/sns/web/v2/comment/page)。
- 必须能解析出
xsec_token,否则抛出XhsApiError - 使用缓存 token 失败时会自动刷新 token 重试一次
get_all_comments(note_id, xsec_token="", xsec_source="", max_pages=20) -> dict[str, Any]
自动翻页聚合所有评论,返回:
{
"comments": list[dict],
"has_more": False,
"cursor": "",
"total_fetched": int,
"pages_fetched": int,
}
get_sub_comments(note_id, root_comment_id, num=30, cursor="") -> Any
获取子评论(/api/sns/web/v2/comment/sub/page)。
4. 互动类接口(InteractionEndpointsMixin)
post_comment(note_id, content) -> dict[str, Any]
发表评论(/api/sns/web/v1/comment/post)。
reply_comment(note_id, target_comment_id, content) -> Any
回复指定评论,底层同为 comment/post,附带 target_comment_id。
like_note(note_id) -> dict[str, Any]
点赞(/api/sns/web/v1/note/like,参数名 note_oid)。
unlike_note(note_id) -> dict[str, Any]
取消点赞(/api/sns/web/v1/note/dislike)。
favorite_note(note_id) -> dict[str, Any]
收藏(/api/sns/web/v1/note/collect)。
unfavorite_note(note_id) -> dict[str, Any]
取消收藏(/api/sns/web/v1/note/uncollect,参数名 note_ids)。
delete_comment(note_id, comment_id) -> dict[str, Any]
删除自己的评论(/api/sns/web/v1/comment/delete)。
5. 创作类接口(CreatorEndpointsMixin)
这些方法走创作者 API 签名(
sign_creator),部分端点位于CREATOR_HOST(/api/galaxy/开头),其余位于EDITH_HOST。
search_topics(keyword) -> dict[str, Any]
搜索话题(/web_api/sns/v1/search/topic,每页 20 条)。
search_users(keyword) -> dict[str, Any]
搜索用户(/web_api/sns/v1/search/user_info,每页 20 条)。
get_upload_permit(file_type="image", count=1) -> dict[str, str]
申请上传许可(/api/media/v1/upload/web/permit),返回:
{"fileId": str, "token": str}
upload_file(file_id, token, file_path, content_type=None) -> None
以 PUT 方式上传文件到 UPLOAD_HOST/{file_id}。content_type 未指定时按文件扩展名推断。HTTP 状态码 ≥ 400 时抛出 XhsApiError。
create_image_note(title, desc, image_file_ids, topics=None, is_private=False) -> Any
发布图文笔记(/web_api/sns/v2/note)。topics 为 [{"name": ..., "id": ...}, ...] 形式的 hashtag 列表;is_private=True 时设置隐私类型。
delete_note(note_id) -> dict[str, Any]
删除笔记(/api/galaxy/creator/note/delete)。注意:若返回 404,会抛出 UnsupportedOperationError,提示该端点当前不可用(实验性功能)。
get_creator_note_list(tab=0, page=0) -> dict[str, Any]
创作者平台"我的笔记"列表(/api/galaxy/v2/creator/note/user/posted)。
6. 社交类接口(SocialEndpointsMixin)
follow_user(user_id) -> dict[str, Any]
关注用户(/api/sns/web/v1/user/follow)。
unfollow_user(user_id) -> dict[str, Any]
取消关注(/api/sns/web/v1/user/unfollow)。
get_user_favorites(user_id, cursor="") -> dict[str, Any]
获取用户的收藏列表(/api/sns/web/v2/note/collect/page,每页 30 条)。
get_user_likes(user_id, cursor="") -> dict[str, Any]
获取用户点赞过的笔记(/api/sns/web/v1/note/like/page,每页 30 条)。
7. 通知类接口(NotificationEndpointsMixin)
get_unread_count() -> dict[str, Any]
未读消息计数(/api/sns/web/unread_count)。
get_notification_mentions(cursor="", num=20) -> dict[str, Any]
@ 我的通知(/api/sns/web/v1/you/mentions)。
get_notification_likes(cursor="", num=20) -> dict[str, Any]
赞和收藏通知(/api/sns/web/v1/you/likes)。
get_notification_connections(cursor="", num=20) -> dict[str, Any]
新关注通知(/api/sns/web/v1/you/connections)。
8. 认证类接口(AuthEndpointsMixin)
login_activate() -> dict[str, Any]
激活登录会话(/api/sns/web/v1/login/activate)。
create_qr_login() -> dict[str, Any]
创建扫码登录二维码(/api/sns/web/v1/login/qrcode/create,qr_type=1)。
check_qr_status(qr_id, code) -> dict[str, Any]
轮询扫码状态(POST /api/qrcode/userinfo),附加请求头 service-tag: webcn。
complete_qr_login(qr_id, code) -> dict[str, Any]
完成扫码登录(GET /api/sns/web/v1/login/qrcode/status)。
9. 异常体系
所有异常定义在 xhs_cli/exceptions.py:
| 异常 | 触发条件 |
|---|---|
XhsApiError |
通用 API 错误(非 JSON 响应、未知错误码、重试耗尽、上传失败等),携带 code 与 response 属性 |
IpBlockedError |
响应 code == 300012(IP 被封禁) |
SignatureError |
响应 code == 300015(签名错误) |
SessionExpiredError |
响应 code == -100(登录态失效) |
NeedVerifyError |
HTTP 461/471(触发验证码),携带 verify_type 与 verify_uuid |
UnsupportedOperationError |
功能端点不可用(如 delete_note 返回 404) |
验证码冷却机制:连续触发 461/471 时,_verify_count 递增,冷却时间按 min(30, 5 * 2^(count-1)) 指数增长,同时 _request_delay 翻倍;成功响应后计数归零。
重试机制:对 httpx.TimeoutException / httpx.NetworkError 以及 HTTP 429/500/502/503/504 进行重试,等待时间为 2^attempt + uniform(0, 1) 秒。
10. 模块级辅助函数
来自 client_mixins.py,虽非类方法但常配合使用:
get_search_session_stats() -> dict[str, Any]
返回搜索会话缓存的调试统计:
{
"active_count": int,
"last_keyword": str,
"last_sort": str,
"last_note_type": int | None,
}
11. 使用示例
from xhs_cli.client import XhsClient
cookies = {"a1": "...", "web_session": "...", ...}
with XhsClient(cookies, request_delay=1.0, max_retries=3) as client:
# 读取
me = client.get_self_info()
results = client.search_notes("咖啡", page=1, page_size=20)
note = client.get_note_detail("笔记ID", xsec_source="pc_feed")
comments = client.get_all_comments("笔记ID", max_pages=10)
# 互动
client.like_note("笔记ID")
client.post_comment("笔记ID", "写得真好!")
# 创作
permit = client.get_upload_permit("image", 1)
client.upload_file(permit["fileId"], permit["token"], "/path/to/img.jpg")
client.create_image_note(
title="标题",
desc="正文",
image_file_ids=[permit["fileId"]],
topics=[{"name": "咖啡", "id": "..."}],
)
12. 内部机制摘要(非公开 API)
以下为 XhsClient 的私有方法,供实现参考,不建议外部直接调用:
| 方法 | 作用 |
|---|---|
_rate_limit_delay() |
高斯抖动限速(5% 概率额外 2–5 秒) |
_mark_request() |
记录请求时间与计数 |
_base_headers() |
构造浏览器伪装请求头 |
_handle_response(resp) |
统一解析响应、映射错误码、处理验证码 |
_merge_response_cookies(resp) |
将响应 Cookie 合并回 self.cookies |
_request_with_retry(method, url, **kwargs) |
带限速与重试的 HTTP 请求 |
_main_api_get(uri, params=None) |
主站 GET(自动签名) |
_main_api_post(uri, data, header_overrides=None) |
主站 POST(自动签名,支持头部覆盖) |
_creator_host(uri) |
根据 URI 前缀选择 CREATOR_HOST 或 EDITH_HOST |
_creator_get(uri, params=None) |
创作者 GET(x-s/x-t 签名) |
_creator_post(uri, data) |
创作者 POST(x-s/x-t 签名) |

浙公网安备 33010602011771号