Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Search, analyze, and interact with Xiaohongshu (RedNote/小红书) content via a local MCP server and shell scripts.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
tools/xhs-downloader/export_to_workspace.py
1#!/usr/bin/env python2"""3将小红书笔记按单独文件导出到 OpenClaw workspace45用法:6python export_to_workspace.py [db_path] [output_dir]78默认:9db_path: Volume/Download/ExploreData.db10output_dir: ~/.openclaw/workspace/xhs-memory1112导出格式类似 gpt-history,每条笔记一个文件,文件名格式: YYYY-MM-标题.md13"""14import sqlite315import re16import sys17from pathlib import Path181920def sanitize_filename(name: str, max_len: int = 50) -> str:21"""清理文件名,移除非法字符"""22name = re.sub(r'[<>:"/\\|?*\n\r\t]', '', name)23name = re.sub(r'\s+', '-', name.strip())24name = re.sub(r'-+', '-', name)25name = name.strip('-')26if len(name) > max_len:27name = name[:max_len].rstrip('-')28return name or "无标题"293031def export_to_workspace(db_path: Path = None, output_dir: Path = None):32db_path = db_path or Path("Volume/Download/ExploreData.db")33output_dir = output_dir or Path.home() / ".openclaw/workspace/xhs-memory"34output_dir.mkdir(parents=True, exist_ok=True)3536if not db_path.exists():37print(f"错误: 数据库不存在: {db_path}")38return False3940conn = sqlite3.connect(db_path)41cursor = conn.cursor()4243cursor.execute("""44SELECT 作品标题, 发布时间, 作品链接, 作品描述, 作者昵称, 作品标签45FROM explore_data46ORDER BY 发布时间 DESC47""")4849rows = cursor.fetchall()50conn.close()5152if not rows:53print("数据库为空")54return False5556count = 057for title, time, link, desc, author, tags in rows:58# 解析时间: 2026-01-25_18:17:43 -> 2026-0159if time:60date_prefix = time[:7] # YYYY-MM61full_date = time.replace('_', ' ')62else:63date_prefix = "unknown"64full_date = "未知"6566# 生成文件名67safe_title = sanitize_filename(title or "无标题")68filename = f"{date_prefix}-{safe_title}.md"69filepath = output_dir / filename7071# 避免重复文件名72counter = 173while filepath.exists():74filename = f"{date_prefix}-{safe_title}-{counter}.md"75filepath = output_dir / filename76counter += 17778# 生成内容79content = f"# {title or '无标题'}\n\n"80content += f"**来源**: 小红书收藏/点赞\n\n"81content += f"**日期**: {full_date}\n\n"82content += f"**作者**: {author or '未知'}\n\n"83content += f"**链接**: {link or '无'}\n\n"84if tags:85content += f"**标签**: {tags}\n\n"86content += "---\n\n"87content += "## 内容\n\n"88content += f"{desc or '无内容'}\n"8990filepath.write_text(content, encoding="utf-8")91count += 19293print(f"导出完成: {output_dir}")94print(f"共生成 {count} 个文件")95return True969798if __name__ == "__main__":99db_path = Path(sys.argv[1]) if len(sys.argv) > 1 else None100output_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else None101export_to_workspace(db_path, output_dir)102