本文作者:99ANYc3cd6

小红书视频解析源码

99ANYc3cd6 01-31 7
小红书视频解析源码摘要: 仅供学习研究仅用于技术学习和原理探究,旨在了解后端API交互和数据处理方式,尊重版权:请勿将此技术用于任何商业用途或侵犯他人版权的行为,小红书的视频内容受版权保护,请通过官方渠道观...

小红书视频解析源码
(图片来源网络,侵删)
  • 仅供学习研究仅用于技术学习和原理探究,旨在了解后端API交互和数据处理方式。
  • 尊重版权:请勿将此技术用于任何商业用途或侵犯他人版权的行为,小红书的视频内容受版权保护,请通过官方渠道观看和下载。
  • 反爬虫机制:小红书有非常严格的反爬虫和反盗链机制,直接解析或下载视频可能会触发风控,导致IP被封禁或账号受限,以下代码仅为示例,在实际环境中可能很快失效。

核心原理

小红书视频解析的核心原理是模拟客户端(如手机App或浏览器)向后端服务器发送请求,获取视频的真实播放地址,这个过程通常分为以下几个步骤:

  1. 获取视频ID:从小红书分享链接中提取出视频的唯一标识符。
  2. 构造请求:模拟App的请求头和参数,向小红书的API服务器发送一个获取视频信息的请求。
  3. 解析响应:服务器会返回一个JSON或XML格式的数据包,里面包含了视频的详细信息,其中最重要的就是视频的真实播放地址(URL)
  4. 下载视频:使用获取到的真实地址,通过HTTP请求下载视频文件。

源码解析(Python示例)

我们将使用Python语言,结合requests库来模拟网络请求,re库来解析URL,json库来处理响应数据。

分析小红书视频链接

小红书视频分享链接的格式通常如下: https://www.xiaohongshu.com/explore/xxxxxxxxxxxxxx

xxxxxxxxxxxxxx就是视频的唯一ID,我们的第一步就是从这个链接中提取出这个ID。

小红书视频解析源码
(图片来源网络,侵删)
import re
def extract_video_id(url):
    """
    从小红书分享链接中提取视频ID
    """
    # 使用正则表达式匹配
    # 匹配 /explore/ 后面跟着的一串字符
    pattern = r'/explore/([a-zA-Z0-9]+)'
    match = re.search(pattern, url)
    if match:
        return match.group(1)
    else:
        raise ValueError("无法从URL中提取视频ID,请检查URL格式。")
# 示例URL
example_url = "https://www.xiaohongshu.com/explore/6Z2GXxwY8nK8m5uE3fQ9bA"
video_id = extract_video_id(example_url)
print(f"提取到的视频ID: {video_id}")

模拟App请求获取视频信息

这是最关键的一步,我们需要找到小红书App在播放视频时调用的API接口,通过抓包工具(如Charles或Fiddler)可以分析出App的网络请求。

经过分析,小红书App在获取视频详情时,通常会调用一个类似https://www.xiaohongshu.com/discovery/item/v1/{video_id}的接口。

请求的关键点:

  1. 请求头:必须模拟App的请求头,否则服务器会拒绝请求,最重要的几个头是:

    • User-Agent:伪装成手机App。
    • X-Secsdk-Csrf-Token:一个动态的防CSRF令牌,通常在App启动时获取。
    • X-Secsdk-csrf-platform:平台标识,如androidios
    • 其他可能的头,如X-Requested-WithAccept-Language等。
  2. 请求参数:API通常需要一些参数,比如sourceextra等,这些也可能在抓包时被发现。

由于X-Secsdk-Csrf-Token等动态令牌的获取非常复杂且随时可能变化,我们的示例代码会先省略它,或者使用一个可能已经失效的静态值来展示逻辑。

import requests
import json
def get_video_info(video_id):
    """
    模拟App请求,获取视频信息
    """
    # API URL
    api_url = f"https://www.xiaohongshu.com/discovery/item/v1/{video_id}"
    # 请求头 - 这是关键!必须模拟App
    # 注意:下面的User-Agent和Token是示例,很可能已经失效
    headers = {
        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Mobile/15E148 Safari/604.1",
        "X-Requested-With": "XMLHttpRequest",
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        "X-Secsdk-Csrf-Token": "YOUR_CSRF_TOKEN_HERE", # 动态令牌,获取困难
        "X-Secsdk-csrf-platform": "android",
        "Referer": f"https://www.xiaohongshu.com/explore/{video_id}"
    }
    try:
        # 发送GET请求
        response = requests.get(api_url, headers=headers, timeout=10)
        # 检查请求是否成功
        response.raise_for_status()
        # 解析JSON响应
        data = response.json()
        if data.get('status') == 0:
            return data.get('data')
        else:
            print(f"API请求失败: {data.get('msg')}")
            return None
    except requests.exceptions.RequestException as e:
        print(f"网络请求出错: {e}")
        return None
    except json.JSONDecodeError:
        print("响应解析失败,返回的不是有效的JSON格式")
        return None
# 使用上一步提取的ID
video_data = get_video_info(video_id)
if video_data:
    print("成功获取视频信息!")
    # print(json.dumps(video_data, indent=2, ensure_ascii=False)) # 打印完整信息

从响应数据中解析视频URL

现在我们拿到了video_data,这是一个字典,我们需要在其中找到视频的播放地址,视频地址可能嵌套在note_card -> video -> play_addr -> url_list中。

def extract_video_url(video_data):
    """
    从视频信息数据中解析出视频的真实播放地址
    """
    if not video_data:
        return None
    # 根据实际的数据结构进行解析
    # 这是最常见的路径,但小红书可能会随时修改
    try:
        # 真实视频地址通常是一个列表,取第一个即可
        video_url_list = video_data['note_card']['video']['play_addr']['url_list']
        if video_url_list:
            # url_list 里的地址通常是带防盗链的,可以直接下载
            return video_url_list[0]
    except KeyError:
        print("数据结构已变更,无法解析视频URL。")
        # 尝试其他可能的路径
        # e.g., video_data['item_info']['item_struct']['video']['play_addr']['url_list']
    return None
if video_data:
    video_url = extract_video_url(video_data)
    if video_url:
        print(f"解析到的视频播放地址: {video_url}")
    else:
        print("未能解析到视频地址。")

下载视频文件

我们使用获取到的真实地址来下载视频,由于视频文件可能很大,我们需要使用流式下载,避免一次性占用过多内存。

def download_video(video_url, save_path="xiaohongshu_video.mp4"):
    """
    下载视频文件
    """
    print(f"开始下载视频到: {save_path}")
    try:
        # 使用 stream=True 进行流式下载
        with requests.get(video_url, stream=True, timeout=30) as r:
            r.raise_for_status()
            # 获取文件总大小(字节)
            total_size = int(r.headers.get('content-length', 0))
            # 打开文件,以二进制写入模式
            with open(save_path, 'wb') as f:
                # 已下载大小
                downloaded_size = 0
                # 设置块大小
                chunk_size = 8192 
                for chunk in r.iter_content(chunk_size=chunk_size):
                    if chunk:  # 过滤掉保持连接的新块
                        f.write(chunk)
                        downloaded_size += len(chunk)
                        # 打印下载进度
                        if total_size > 0:
                            progress = (downloaded_size / total_size) * 100
                            print(f"\r下载进度: {progress:.2f}%", end='')
            print("\n下载完成!")
            return True
    except requests.exceptions.RequestException as e:
        print(f"\n下载失败: {e}")
        return False
if video_url:
    download_video(video_url)

完整整合代码

将以上所有步骤整合到一个完整的脚本中:

import re
import requests
import json
import os
# --- 配置 ---
# 请替换为你要解析的小红书视频链接
TARGET_URL = "https://www.xiaohongshu.com/explore/6Z2GXxwY8nK8m5uE3fQ9bA" 
# 视频保存路径
SAVE_PATH = "xiaohongshu_video.mp4"
# --- 函数定义 ---
def extract_video_id(url):
    """从URL提取视频ID"""
    pattern = r'/explore/([a-zA-Z0-9]+)'
    match = re.search(pattern, url)
    if match:
        return match.group(1)
    raise ValueError("无效的URL格式,无法提取视频ID。")
def get_video_info(video_id):
    """模拟App请求,获取视频信息"""
    api_url = f"https://www.xiaohongshu.com/discovery/item/v1/{video_id}"
    headers = {
        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Mobile/15E148 Safari/604.1",
        "X-Requested-With": "XMLHttpRequest",
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        # 注意:这个Token是动态的,通常很难获取,这里的值很可能已失效。
        "X-Secsdk-Csrf-Token": "YOUR_CSRF_TOKEN_HERE", 
        "X-Secsdk-csrf-platform": "android",
        "Referer": f"https://www.xiaohongshu.com/explore/{video_id}"
    }
    try:
        response = requests.get(api_url, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()
        if data.get('status') == 0:
            return data.get('data')
        print(f"API返回错误: {data.get('msg')}")
        return None
    except Exception as e:
        print(f"请求或解析数据时出错: {e}")
        return None
def extract_video_url(video_data):
    """从数据中解析视频URL"""
    if not video_data:
        return None
    try:
        # 这是一个常见的路径,但可能变化
        return video_data['note_card']['video']['play_addr']['url_list'][0]
    except (KeyError, TypeError, IndexError):
        print("数据结构已变更,无法解析视频URL。")
        return None
def download_video(video_url, save_path):
    """下载视频"""
    if os.path.exists(save_path):
        print(f"文件 {save_path} 已存在,跳过下载。")
        return
    print(f"开始下载视频到: {save_path}")
    try:
        with requests.get(video_url, stream=True, timeout=60) as r:
            r.raise_for_status()
            total_size = int(r.headers.get('content-length', 0))
            with open(save_path, 'wb') as f:
                downloaded_size = 0
                for chunk in r.iter_content(chunk_size=32768):
                    if chunk:
                        f.write(chunk)
                        downloaded_size += len(chunk)
                        if total_size > 0:
                            progress = (downloaded_size / total_size) * 100
                            print(f"\r下载进度: {progress:.2f}%", end='', flush=True)
            print("\n下载完成!")
    except Exception as e:
        print(f"\n下载失败: {e}")
        if os.path.exists(save_path):
            os.remove(save_path) # 删除未下载完的文件
# --- 主程序 ---
if __name__ == "__main__":
    print("小红书视频解析器启动...")
    try:
        video_id = extract_video_id(TARGET_URL)
        print(f"视频ID: {video_id}")
        video_data = get_video_info(video_id)
        if not video_data:
            print("获取视频信息失败,程序退出。")
            exit()
        video_url = extract_video_url(video_data)
        if not video_url:
            print("解析视频URL失败,程序退出。")
            exit()
        print(f"获取到视频地址: {video_url}")
        download_video(video_url, SAVE_PATH)
    except Exception as e:
        print(f"程序运行出错: {e}")

总结与挑战

  1. 动态令牌:最大的挑战是获取X-Secsdk-Csrf-Token等动态请求头,这些令牌通常通过App启动时的复杂登录或握手流程获取,逆向工程难度极高。
  2. API变化:小红书会不定期更新其后端API和数据结构,导致解析代码失效,这需要持续地进行抓包分析和代码维护。
  3. 防盗链:视频URL本身可能带有防盗链参数,直接下载可能会被服务器拒绝,通常需要携带特定的Referer等请求头。
  4. 法律风险:绕过平台机制获取数据,可能违反用户协议和相关法律法规。

这个源码解析更多的是一种技术学习和探索,对于个人用户,最合规、最方便的方式仍然是使用小红书官方App或网页版进行观看。

文章版权及转载声明

作者:99ANYc3cd6本文地址:https://chumoping.net/post/22022.html发布于 01-31
文章转载或复制请以超链接形式并注明出处初梦运营网

阅读
分享