小红书作为以 UGC 内容为核心的生活方式平台,其数据接口未正式对外开放。以下分析基于移动端 APP 的非官方 API(通过抓包解析),涵盖内容获取、搜索等核心功能,并提供 Python 实现方案。需注意:非官方接口存在稳定性风险,使用需遵守平台用户协议。
一、小红书接口核心特性分析
1. 接口体系与功能域
通过抓包分析,小红书核心接口可分为以下几类:
- 内容推荐:首页推荐笔记(
/api/sns/v1/feed)、关注页内容(/api/sns/v1/follow/feed); - 搜索功能:笔记搜索(
/api/sns/v1/search/notes)、用户搜索(/api/sns/v1/search/users); - 笔记详情:单篇笔记内容、评论、点赞数据(
/api/sns/v1/note/{note_id}/detail); - 用户信息:用户主页、发布的笔记(
/api/sns/v1/user/{user_id}/profile); - 互动操作:点赞、收藏、评论(需登录态,如
/api/sns/v1/note/like)。2. 认证与请求规范
- 匿名接口:部分推荐内容、公开笔记详情可匿名访问,但限制较多;
- 登录态接口:搜索、关注、互动等功能需携带登录凭证(关键参数为
x-s和x-t,由 APP 生成的签名和时间戳); - 请求头核心字段:
User-Agent:模拟小红书 APP(如Xiaomi Redmi K40/Android 12/XHS/7.63.0);x-s:请求签名(动态生成,涉及时间戳、路径、参数加密);x-t:毫秒级时间戳(与签名对应);Cookie:包含登录态信息(如web_session)。
- 参数加密:部分接口参数(如搜索关键词)经过简单加密,签名
x-s是反爬核心(算法未完全公开,需模拟或破解)。3. 典型接口示例(搜索笔记)
- 请求 URL:
- 方法:GET
- 核心参数:
keyword:搜索关键词(URL 编码);page:页码;size:每页条数(默认 20);sort:排序方式(general默认,hot热门)。
- 响应格式:JSON,包含
items(笔记列表)、has_more(是否有下一页)。二、Python 脚本实现:小红书接口调用框架
以下实现基于公开笔记搜索和详情获取,处理签名生成(简化版)和登录态,适用于学习研究。
import requests
import json
import time
import random
import logging
import hashlib
from typing import Dict, List, Optional
from requests.exceptions import RequestException
配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
class XiaohongshuAPI:
def init(self, cookie: str = ""):
"""
初始化小红书API客户端
:param cookie: 登录Cookie(从浏览器/APP抓包获取,含web_session等)
"""
self.base_url = "https://edith.xiaohongshu.com"
self.cookie = cookie
# 设备信息(模拟安卓设备)
self.device_id = f"android-{''.join(random.choices('0123456789abcdef', k=16))}"
self.user_agents = [
"com.xingin.xhs/7.63.0 (Linux; U; Android 12; zh-CN; Redmi K40; Build/SKQ1.211006.001; 64bit)",
"com.xingin.xhs/7.62.0 (Linux; U; Android 13; zh-CN; MI 13; Build/TKQ1.221114.001; 64bit)"
]
self.session = requests.Session()
self._update_headers()
def _update_headers(self):
"""更新请求头,包含动态参数"""
timestamp = str(int(time.time() * 1000))
headers = {
"User-Agent": random.choice(self.user_agents),
"Accept-Encoding": "gzip, deflate",
"Host": "edith.xiaohongshu.com",
"Connection": "keep-alive",
"x-t": timestamp,
"x-s": self._generate_sign(timestamp), # 简化版签名(实际需破解官方算法)
"x-device-id": self.device_id,
"Cookie": self.cookie,
"Content-Type": "application/json; charset=utf-8"
}
self.session.headers.update(headers)
def _generate_sign(self, timestamp: str) -> str:
"""
生成简化版x-s签名(仅用于示例,实际需根据官方算法逆向)
官方签名涉及路径、参数、设备信息、密钥等,此处为模拟
"""
sign_str = f"timestamp={timestamp}&device_id={self.device_id}&secret_key=xxx" # 假设密钥
return hashlib.md5(sign_str.encode()).hexdigest()
def _random_sleep(self):
"""随机休眠,降低反爬风险"""
time.sleep(random.uniform(2, 4))
def search_notes(self, keyword: str, page: int = 1, size: int = 10) -> Optional[List[Dict]]:
"""
搜索笔记
:param keyword: 搜索关键词
:param page: 页码
:param size: 每页条数
:return: 笔记列表(含标题、作者、点赞数等)
"""
url = f"{self.base_url}/api/sns/v1/search/notes"
params = {
"keyword": keyword,
"page": page,
"size": size,
"sort": "general",
"note_type": 0
}
try:
self._update_headers()
response = self.session.get(url, params=params, timeout=10)
response.raise_for_status()
result = response.json()
# 处理错误响应
if result.get("success") is False:
logging.error(f"搜索失败:{result.get('msg')},错误码:{result.get('code')}")
return None
# 解析笔记列表
notes = result.get("data", {}).get("items", [])
if not notes:
logging.info("未找到相关笔记")
return []
parsed_notes = []
for note in notes:
note_info = note.get("note_card", {})
parsed_notes.append({
"note_id": note_info.get("note_id"),
"title": note_info.get("title"),
"content": note_info.get("desc"),
"author": {
"user_id": note_info.get("user", {}).get("user_id"),
"name": note_info.get("user", {}).get("name")
},
"stats": {
"likes": note_info.get("like_count"),
"comments": note_info.get("comment_count"),
"collections": note_info.get("collect_count")
},
"image_urls": [img.get("url") for img in note_info.get("images", [])]
})
logging.info(f"搜索到{len(parsed_notes)}条笔记,关键词:{keyword},页码:{page}")
self._random_sleep()
return parsed_notes
except RequestException as e:
logging.error(f"搜索请求异常:{str(e)}")
self._random_sleep()
return None
def get_note_detail(self, note_id: str) -> Optional[Dict]:
"""
获取笔记详情
:param note_id: 笔记ID(从搜索接口获取)
:return: 笔记详情(含完整内容、评论等)
"""
url = f"{self.base_url}/api/sns/v1/note/{note_id}/detail"
try:
self._update_headers()
response = self.session.get(url, timeout=10)
response.raise_for_status()
result = response.json()
if result.get("success") is False:
logging.error(f"获取笔记详情失败:{result.get('msg')}")
return None
data = result.get("data", {})
note_info = data.get("note", {})
# 解析评论(前3条)
comments = []
for cmt in data.get("comments", [])[:3]:
comments.append({
"user_name": cmt.get("user", {}).get("name"),
"content": cmt.get("content"),
"time": cmt.get("create_time")
})
return {
"note_id": note_id,
"title": note_info.get("title"),
"content": note_info.get("desc"),
"author": {
"user_id": note_info.get("user", {}).get("user_id"),
"name": note_info.get("user", {}).get("name"),
"avatar": note_info.get("user", {}).get("avatar_url")
},
"stats": {
"likes": note_info.get("like_count"),
"comments": note_info.get("comment_count"),
"collections": note_info.get("collect_count"),
"shares": note_info.get("share_count")
},
"image_urls": [img.get("url") for img in note_info.get("images", [])],
"tags": [tag.get("name") for tag in note_info.get("tags", [])],
"comments": comments
}
except RequestException as e:
logging.error(f"笔记详情请求异常:{str(e)}")
self._random_sleep()
return None
示例调用
if name == "main":
# 1. 从抓包获取Cookie(需登录后获取,含web_session等字段)
COOKIE = "web_session=xxx; xhsTrackerId=xxx; ..." # 替换为实际Cookie
# 2. 初始化客户端
xhs = XiaohongshuAPI(cookie=COOKIE)
# 3. 搜索笔记(示例:关键词"旅行攻略")
notes = xhs.search_notes(keyword="旅行攻略", page=1, size=5)
if notes:
print("搜索到的笔记(前2条):")
for note in notes[:2]:
print(f"\n笔记ID:{note['note_id']}")
print(f"标题:{note['title']}")
print(f"内容:{note['content'][:50]}...")
print(f"点赞:{note['stats']['likes']} | 评论:{note['stats']['comments']}")
# 4. 获取第一条笔记的详情
if notes and len(notes) > 0:
first_note_id = notes[0]["note_id"]
detail = xhs.get_note_detail(first_note_id)
if detail:
print(f"\n\n笔记详情({detail['title']}):")
print(f"完整内容:{detail['content']}")
print(f"标签:{','.join(detail['tags'])}")
print("热门评论:")
for cmt in detail["comments"]:
print(f"- {cmt['user_name']}:{cmt['content']}")
三、关键技术点解析
1. 签名机制与反爬应对
x-s签名:小红书核心反爬措施,生成逻辑涉及时间戳(x-t)、设备 ID、请求路径、参数及私有密钥,需通过逆向 APP 获取算法(示例中为简化版)。实际使用可参考开源项目(如xhs-signature)的逆向实现。- 动态请求头:每次请求更新
User-Agent、x-t(时间戳)、x-s(签名),模拟真实 APP 行为。 - Cookie 管理:登录态 Cookie(如
web_session)是访问大部分接口的前提,需从抓包工具(如 Charles、Fiddler)中获取。2. 接口数据解析
- 搜索接口:响应中
data.items包含笔记卡片信息,提取note_id(用于详情查询)、标题、内容摘要、互动数据等。 - 详情接口:返回完整笔记内容、作者信息、标签、评论等,可进一步解析图片 URL(需处理防盗链)。
3. 扩展与限制
- 分页获取:通过
page参数循环调用搜索接口,直至has_more为false。 - 内容限制:未登录状态下可获取的内容有限,部分笔记需关注作者才能查看。
- 频率控制:建议单 IP 请求间隔≥2 秒,批量获取需使用代理池避免封禁。
四、风险与注意事项
- 合规性:非官方 API 调用可能违反小红书用户协议,商业用途需联系平台授权;
- 接口稳定性:
x-s签名算法可能频繁更新,导致脚本失效,需定期维护; - 法律风险:大规模爬取内容可能涉及侵犯知识产权或个人信息,需遵守《网络安全法》;
- 反爬升级:频繁请求可能导致账号封禁或 IP 拉黑,建议仅用于个人学习。
该实现可用于学习 API 逆向、反爬机制分析等场景,通过完善签名算法和代理池可提升稳定性。实际应用中需优先考虑平台官方合作渠道(如有)。