ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python爬虫实战:手把手教你搭建小红书图文视频下载工具

2026/8/25 3:08:21 拓冰建站 浏览量
Python爬虫实战:手把手教你搭建小红书图文视频下载工具 最近在开发一个需要批量下载小红书图文素材的项目时发现市面上的工具要么功能不全要么操作复杂。经过一番折腾和整合我整理出了一套基于XHS-Downloader的完整实战方案。本文将手把手带你从零开始搭建一个稳定、高效的小红书图文/视频下载工具涵盖环境配置、核心功能使用、常见问题排查以及进阶优化思路。无论你是想用于个人学习、内容分析还是作为项目中的一个小模块都能从本文中找到可复用的代码和清晰的步骤。1. 背景与核心概念在开始动手之前我们有必要先了解一下XHS-Downloader是什么以及它能解决什么问题。1.1 什么是 XHS-DownloaderXHS-Downloader是一个开源的工具或脚本集合其主要功能是解析小红书Xiaohongshu的分享链接并下载对应的图文内容或视频。它通常通过模拟网络请求、解析页面数据如 JSON的方式绕过官方 App 的限制实现内容的批量获取。核心价值对于内容创作者、市场分析师或开发者而言手动保存每一篇笔记效率极低。XHS-Downloader自动化了这个过程可以快速收集特定主题、博主或关键词下的公开内容用于竞品分析、灵感收集或数据研究。1.2 它能做什么根据常见的开源实现XHS-Downloader通常具备以下能力解析单条笔记输入一条小红书笔记的分享链接即可获取该笔记的所有高清图片和视频。批量下载支持通过博主主页链接、收藏夹链接或关键词搜索结果批量下载多篇笔记。信息提取除了媒体文件还能提取笔记的标题、正文、发布时间、点赞数等元数据。自定义命名与存储按照指定规则如“博主名_笔记ID_序号”对下载的文件进行命名和分类存储。1.3 重要前提与合规性必须强调任何网络爬虫或下载工具的使用都必须遵守相关法律法规和平台的服务条款。仅用于个人学习与研究本文介绍的方案旨在技术交流请勿用于任何商业侵权、恶意抓取或干扰平台正常运行的行为。尊重版权与隐私下载的内容版权归原作者所有请勿在未授权的情况下进行传播、售卖或用于商业用途。控制访问频率在编写自动化脚本时务必添加合理的延时如time.sleep避免对目标服务器造成过大压力体现技术人的职业道德。2. 环境准备与版本说明我们将以一个典型的 Python 实现为例演示如何搭建XHS-Downloader的运行环境。不同版本的工具在依赖和用法上可能有差异本文以常见的实现思路为核心重点讲解配置方法和原理。2.1 基础运行环境操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu)。本文命令以 Windows 为例Linux/macOS 用户需相应调整如使用python3、pip3。Python 版本推荐使用 Python 3.8 及以上版本。这是目前大多数开源库兼容性较好的版本。包管理工具pip(Python 包安装器)。首先检查你的 Python 环境是否就绪。打开命令行终端CMD, PowerShell, 或 Terminal输入以下命令python --version # 或 python3 --version pip --version # 或 pip3 --version如果显示版本号如Python 3.9.13则说明环境已安装。如果未安装请前往 Python 官网 下载并安装记得勾选 “Add Python to PATH” 选项。2.2 创建项目目录与虚拟环境为了避免污染系统级的 Python 环境强烈建议为项目创建独立的虚拟环境。创建项目文件夹mkdir xhs-downloader-project cd xhs-downloader-project创建虚拟环境# Windows python -m venv venv # macOS/Linux python3 -m venv venv这会在当前目录下创建一个名为venv的文件夹里面包含独立的 Python 解释器和 pip。激活虚拟环境# Windows (CMD) venv\Scripts\activate.bat # Windows (PowerShell) venv\Scripts\Activate.ps1 # 如果执行策略限制可能需要先执行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser # macOS/Linux source venv/bin/activate激活后命令行提示符前通常会显示(venv)表示你已进入虚拟环境。2.3 安装核心依赖XHS-Downloader的核心功能依赖于几个关键的 Python 库。我们通过requirements.txt文件来管理依赖。在项目根目录下创建一个名为requirements.txt的文件内容如下requests2.28.0 beautifulsoup44.11.0 lxml4.9.0 pycryptodome3.15.0requests用于发送 HTTP 请求获取网页源码或 API 数据。beautifulsoup4lxml用于解析 HTML 页面提取有效信息。lxml是一个高效的解析器。pycryptodome某些版本的工具可能需要对返回的数据进行解密这个库提供了加解密功能。安装依赖pip install -r requirements.txt如果安装缓慢可以使用国内镜像源例如pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple3. 核心原理与代码拆解理解工具背后的工作原理能帮助我们在遇到问题时进行调试和定制。一个典型的XHS-Downloader工作流程如下3.1 工作流程剖析输入处理接收用户输入的小红书笔记分享链接。请求与解析使用requests库可能需要添加特定的headers如User-Agent模拟浏览器访问该链接。获取返回的 HTML 页面或 JSON 数据。小红书的数据通常通过接口动态加载隐藏在页面脚本或网络请求中。数据提取从获取到的数据中通过正则表达式、BeautifulSoup 解析或直接解析 JSON找到图片和视频的真实 URL。这些 URL 可能被编码或加密。同时提取笔记的元信息标题、描述等。媒体下载遍历提取到的媒体 URL再次使用requests发起请求将二进制内容图片的.jpg/.png视频的.mp4保存到本地文件。文件管理根据元信息创建文件夹、命名文件完成整理。3.2 关键代码模块示例下面我们分模块编写一个简化但功能完整的核心脚本xhs_downloader.py。3.2.1 请求头设置与工具函数为了模拟浏览器访问避免被反爬机制拦截我们需要设置合理的请求头。# xhs_downloader.py import requests import re import json import os from bs4 import BeautifulSoup import time from urllib.parse import urlparse class XHSDownloader: def __init__(self): # 定义请求头模拟浏览器 self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Connection: keep-alive, Upgrade-Insecure-Requests: 1, } self.session requests.Session() self.session.headers.update(self.headers) def get_html(self, url): 获取网页HTML内容 try: resp self.session.get(url, timeout10) resp.raise_for_status() # 检查请求是否成功 resp.encoding resp.apparent_encoding return resp.text except requests.exceptions.RequestException as e: print(f请求失败: {url}, 错误: {e}) return None3.2.2 链接解析与数据提取这是最核心的部分。小红书笔记的分享链接格式通常为https://www.xiaohongshu.com/explore/笔记ID或带有分享码的短链。我们需要从中提取出笔记的唯一标识如note_id。def parse_note_id(self, url): 从分享链接中解析出笔记ID # 处理短链或长链 parsed_url urlparse(url) # 示例从 /explore/63f9a123000000001f012345 中提取 63f9a123000000001f012345 path parsed_url.path note_id_match re.search(r/explore/([a-f0-9]), path) if note_id_match: return note_id_match.group(1) else: # 也可能是其他格式这里需要根据实际情况调整正则表达式 print(f无法从链接中解析出笔记ID: {url}) return None def extract_note_data(self, html): 从HTML中提取笔记数据图片/视频URL标题等 note_data { title: , desc: , image_urls: [], video_url: None } soup BeautifulSoup(html, lxml) # 尝试寻找包含笔记数据的JSON字符串小红书常见做法 script_tags soup.find_all(script) for script in script_tags: if script.string and window.__INITIAL_STATE__ in script.string: # 找到初始化状态数据 json_str script.string.split(window.__INITIAL_STATE__)[1].split(;)[0] try: data json.loads(json_str) # 这是一个复杂的嵌套结构需要根据实际页面结构来定位 # 这里是一个示例路径实际路径需要开发者工具分析 # note_detail data.get(note, {}).get(noteDetailMap, {}) # for key, detail in note_detail.items(): # image_list detail.get(note, {}).get(imageList, []) # for img in image_list: # url img.get(url, ).replace(//, https://) # if url: # note_data[image_urls].append(url) # note_data[title] detail.get(note, {}).get(title, ) # note_data[desc] detail.get(note, {}).get(desc, ) print(成功提取到笔记JSON数据) # 实际解析逻辑需要在此处根据 data 的结构编写 # 此处为演示我们用一个简单的正则来匹配图片URL break except (json.JSONDecodeError, IndexError, KeyError) as e: print(f解析JSON数据失败: {e}) # 备用方案使用正则表达式在HTML中直接匹配图片URL可能不完整或为缩略图 if not note_data[image_urls]: # 匹配类似 https://sns-img-xxxx.xxx 的图片URL image_pattern re.compile(rhttps://sns-img[^\]\.(?:jpg|png|jpeg|webp)) note_data[image_urls] image_pattern.findall(html) # 去重 note_data[image_urls] list(set(note_data[image_urls])) # 匹配视频URL video_pattern re.compile(rhttps://sns-video[^\]\.mp4) video_urls video_pattern.findall(html) if video_urls: note_data[video_url] video_urls[0] # 通常第一个是主视频 # 提取标题从title标签或特定class title_tag soup.find(title) if title_tag: note_data[title] title_tag.text.strip().replace( - 小红书, ) return note_data重要提示上述extract_note_data函数中的 JSON 解析部分是伪代码。小红书的前端数据结构可能频繁变动最可靠的方法是用浏览器打开一篇小红书笔记。按 F12 打开开发者工具进入Network(网络) 选项卡。刷新页面过滤XHR或JS请求。寻找包含noteDetail或类似关键词的请求响应里面通常有结构化的笔记数据。你需要根据实际响应结构来调整解析代码。3.2.3 文件下载与保存def download_media(self, url, filepath): 下载单个媒体文件图片/视频 try: resp self.session.get(url, streamTrue, timeout30) resp.raise_for_status() with open(filepath, wb) as f: for chunk in resp.iter_content(chunk_size8192): if chunk: f.write(chunk) print(f下载成功: {filepath}) return True except Exception as e: print(f下载失败 {url}: {e}) return False def save_note(self, note_data, note_id, base_dirdownloads): 保存笔记的所有媒体文件和元信息 # 创建以笔记ID命名的文件夹 save_dir os.path.join(base_dir, note_id) os.makedirs(save_dir, exist_okTrue) # 保存元信息为文本文件 meta_file os.path.join(save_dir, meta.txt) with open(meta_file, w, encodingutf-8) as f: f.write(f标题: {note_data[title]}\n) f.write(f描述: {note_data[desc]}\n) f.write(f图片数: {len(note_data[image_urls])}\n) f.write(f视频: {note_data[video_url] or 无}\n) # 下载图片 for idx, img_url in enumerate(note_data[image_urls]): # 构造文件名如 image_1.jpg ext os.path.splitext(img_url)[1] or .jpg filename fimage_{idx1}{ext} filepath os.path.join(save_dir, filename) self.download_media(img_url, filepath) time.sleep(0.5) # 礼貌性延时避免请求过快 # 下载视频 if note_data[video_url]: video_path os.path.join(save_dir, video.mp4) self.download_media(note_data[video_url], video_path) time.sleep(1) print(f笔记 {note_id} 保存完成目录: {save_dir})3.2.4 主流程整合def download_from_url(self, url): 主函数从URL下载笔记 print(f开始处理链接: {url}) note_id self.parse_note_id(url) if not note_id: print(无效的链接格式) return False html self.get_html(url) if not html: return False note_data self.extract_note_data(html) if not note_data[image_urls] and not note_data[video_url]: print(未从页面中提取到有效的媒体资源) return False self.save_note(note_data, note_id) return True # 使用示例 if __name__ __main__: downloader XHSDownloader() # 替换为你要下载的小红书笔记分享链接 test_url https://www.xiaohongshu.com/explore/xxxxxxxxxxxxxxxx downloader.download_from_url(test_url)4. 完整实战运行与测试现在让我们将上面的代码整合并运行起来。4.1 项目结构确保你的项目目录结构如下xhs-downloader-project/ ├── venv/ # 虚拟环境目录自动生成 ├── requirements.txt # 依赖列表 ├── xhs_downloader.py # 主程序文件 └── downloads/ # 下载文件保存目录运行后自动创建4.2 运行程序将xhs_downloader.py中的test_url替换为一个真实、公开的小红书笔记分享链接。在激活的虚拟环境中运行脚本python xhs_downloader.py观察控制台输出。如果一切顺利你将看到类似以下的日志开始处理链接: https://www.xiaohongshu.com/explore/63f9a123000000001f012345 成功提取到笔记JSON数据 下载成功: downloads\63f9a123000000001f012345\image_1.jpg 下载成功: downloads\63f9a123000000001f012345\image_2.jpg 笔记 63f9a123000000001f012345 保存完成目录: downloads\63f9a123000000001f012345检查downloads目录会发现以笔记ID命名的文件夹里面包含了下载的图片、视频和meta.txt文件。4.3 结果验证打开下载的文件夹确认图片文件能正常打开。视频文件能正常播放。meta.txt文件正确记录了笔记信息。5. 常见问题与排查思路在实际运行中你可能会遇到各种问题。下面是一个排查清单。问题现象可能原因解决思路请求失败返回 403/4041. 请求头User-Agent被识别为爬虫。2. 链接已失效或需要登录。3. IP 被暂时限制。1. 更新headers使用更常见的浏览器 UA。2. 在浏览器中手动打开链接确认是否有效且为公开内容。3. 添加Referer头如‘Referer’: ‘https://www.xiaohongshu.com/’。4. 增加请求间隔time.sleep或使用代理 IP。无法解析出笔记ID分享链接格式发生变化。使用正则表达式调试工具分析当前链接格式调整parse_note_id函数中的正则表达式。提取不到图片/视频URL1. 页面结构已更新JSON 数据路径变化。2. 媒体 URL 被动态加载或加密。1.核心步骤使用浏览器开发者工具F12 - Network - XHR重新分析数据接口。寻找包含note、detail、image、video等关键词的请求。2. 如果 URL 是加密的如以\u002F开头需要进行解码json.loads通常会自动处理。3. 尝试寻找src属性为blob:开头的视频这类需要不同的处理方式可能涉及M3U8流。下载的图片是模糊的缩略图提取到的 URL 是低分辨率预览图。在找到的 JSON 数据中寻找url、origin、original等字段通常高清原图 URL 会有特定标识。可能需要拼接参数如将...imageView2/2/w/300中的/w/300删除或改为/w/0如果接口支持。程序运行一段时间后报错停止1. 网络不稳定。2. 触发反爬机制被暂时封禁。1. 在download_media和网络请求处增加更完善的异常处理try-except和重试机制。2. 显著增加请求之间的延迟模拟真人操作。3. 考虑使用更复杂的策略如更换 User-Agent 池、使用代理 IP 池。BeautifulSoup解析警告未指定解析器或lxml未安装。确保已安装lxml(pip install lxml)并在BeautifulSoup(html, ‘lxml’)中明确指定使用它。6. 进阶优化与工程实践一个基础的下载脚本可以工作但要用于更稳定、更复杂的场景还需要进行工程化优化。6.1 配置化管理将可配置项如请求头、下载路径、延时时间等抽离到配置文件如config.yaml或config.ini中。# config.yaml request: user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 timeout: 10 delay_between_requests: 1.0 # 请求间延时秒 download: base_dir: ./downloads name_pattern: {note_id}_{index} # 文件名模式 overwrite: false # 是否覆盖已存在文件 log: level: INFO file: ./xhs_downloader.log在代码中使用yaml库加载配置。6.2 日志记录使用 Python 标准库的logging模块替代print便于记录运行状态和排查问题。import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(xhs_downloader.log), logging.StreamHandler() ] ) logger logging.getLogger(__name__) # 在代码中使用 logger logger.info(f开始处理链接: {url}) logger.error(f下载失败 {url}: {e})6.3 增加重试与代理机制网络请求不稳定是常态增加重试逻辑和代理支持能极大提高鲁棒性。from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry class XHSDownloader: def __init__(self, max_retries3, proxyNone): self.session requests.Session() # 配置重试策略 retries Retry(totalmax_retries, backoff_factor0.5, status_forcelist[500, 502, 503, 504]) self.session.mount(http://, HTTPAdapter(max_retriesretries)) self.session.mount(https://, HTTPAdapter(max_retriesretries)) # 配置代理 if proxy: self.session.proxies { http: proxy, https: proxy, } # ... 其他初始化代码6.4 实现批量下载与并发控制通过读取文件中的链接列表或解析博主主页来实现批量下载。同时使用线程池或异步IO如asyncioaiohttp可以提升下载效率但务必注意控制并发数避免对目标服务器造成过大压力。import concurrent.futures def batch_download(url_list, max_workers3): 使用线程池批量下载 downloader XHSDownloader() with concurrent.futures.ThreadPoolExecutor(max_workersmax_workers) as executor: future_to_url {executor.submit(downloader.download_from_url, url): url for url in url_list} for future in concurrent.futures.as_completed(future_to_url): url future_to_url[future] try: success future.result() logger.info(fURL {url} 处理结果: {success}) except Exception as exc: logger.error(fURL {url} 生成异常: {exc})6.5 错误恢复与状态持久化对于大批量任务记录成功和失败的链接支持断点续传。可以将任务状态保存到 JSON 文件或轻量级数据库中。import json def load_progress(filepathprogress.json): try: with open(filepath, r) as f: return json.load(f) except FileNotFoundError: return {success: [], failed: [], pending: []} def save_progress(progress, filepathprogress.json): with open(filepath, w) as f: json.dump(progress, f, indent2)7. 安全、合规与最佳实践总结在项目开发和运行过程中请始终将以下原则放在首位合规先行清晰了解你的数据用途。仅下载公开可用、且用于个人学习、研究或合法分析的内容。绝对不要尝试绕过登录获取非公开数据或进行数据贩卖。尊重robots.txt检查https://www.xiaohongshu.com/robots.txt了解平台允许和禁止爬取的路径。虽然技术上有能力但遵守它是良好的行业规范。设置友好速率在代码中务必加入time.sleep(random.uniform(1, 3))这样的随机延时模拟人类浏览间隔。高频率请求是导致 IP 被封的最常见原因。使用缓存对于重复的分析任务可以考虑将已解析的元数据缓存起来避免对同一页面反复请求。代码健壮性做好异常处理try-except、参数校验、类型检查。一个因网络波动就崩溃的脚本是不实用的。关注更新小红书的网页结构和接口可能随时变更。你的解析逻辑需要定期维护和更新。关注相关开源项目的Issues和Pull Requests是获取更新信息的有效途径。通过本文的梳理你应该已经掌握了搭建一个自定义XHS-Downloader的核心流程从环境搭建、原理剖析、代码实现到问题排查和进阶优化。真正的掌握来自于动手实践和不断调试。建议你从单条笔记下载开始逐步增加批量、并发、错误处理等功能最终构建一个适合自己需求的稳定工具。如果在实践中遇到新的问题不妨回头仔细分析网络请求那往往是找到答案的关键。