#!/usr/bin/env python3 """ 内容格式转换器 将技术文章转换为小红书格式 """ import re import json import logging from typing import Dict, List, Optional, Tuple from dataclasses import dataclass # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) @dataclass class Article: """文章数据结构""" title: str content: str source: str url: str images: List[str] publish_date: Optional[str] = None author: Optional[str] = None category: Optional[str] = None class ContentConverter: """内容格式转换器""" # 表情符号映射 EMOJI_MAPPING = { 'ai': '🤖', '人工智能': '🧠', '技术': '💻', '编程': '⌨️', '开发': '🔧', '数据': '📊', '安全': '🔒', '网络': '🌐', '开源': '📦', '更新': '🔄', '发布': '🚀', '教程': '📚', '新闻': '📰', '突破': '⚡', '趋势': '📈', '分析': '🔍', '工具': '🛠️', '框架': '🏗️', '算法': '🧮', '测试': '🧪', '部署': '🚢', '云': '☁️', '移动': '📱', 'web': '🕸️', '设计': '🎨', '性能': '⚡', '优化': '✨', 'bug': '🐛', '修复': '🔧', '文档': '📄', '社区': '👥', '会议': '🎤', '学习': '🎓', '项目': '📁', '代码': '💾', 'API': '🔌', '数据库': '🗄️', '服务器': '🖥️', '前端': '🎯', '后端': '⚙️', '全栈': '🔗', 'DevOps': '🔀', '容器': '📦', '微服务': '🧩', '区块链': '⛓️', '物联网': '📡', '5G': '📶', 'AR/VR': '👓', '量子': '⚛️', '芯片': '🔋', '硬件': '💾', '软件': '💿', '企业': '🏢', '创业': '💡', '投资': '💰', '市场': '📈', '用户': '👤', '体验': '❤️', '隐私': '🕵️', '合规': '⚖️', '标准': '📏', '未来': '🔮', '创新': '💫', '变革': '🌪️', '挑战': '🏔️', '机遇': '🎯', '总结': '📝', '建议': '💡', '资源': '📚', '链接': '🔗', '关注': '👀', '分享': '📤', '评论': '💬', '点赞': '👍', '收藏': '⭐' } # 技术关键词标签映射 TAG_MAPPING = { 'ai': ['#AI', '#人工智能', '#机器学习'], 'python': ['#Python', '#编程'], 'javascript': ['#JavaScript', '#前端'], 'java': ['#Java', '#后端'], 'go': ['#Go语言', '#后端'], 'rust': ['#Rust', '#系统编程'], 'react': ['#React', '#前端框架'], 'vue': ['#Vue', '#前端框架'], 'node': ['#Nodejs', '#后端'], 'docker': ['#Docker', '#容器化'], 'kubernetes': ['#Kubernetes', '#容器编排'], 'aws': ['#AWS', '#云计算'], 'azure': ['#Azure', '#云计算'], 'gcp': ['#GCP', '#云计算'], 'linux': ['#Linux', '#操作系统'], 'windows': ['#Windows', '#操作系统'], 'macos': ['#macOS', '#操作系统'], 'ios': ['#iOS', '#移动开发'], 'android': ['#Android', '#移动开发'], 'flutter': ['#Flutter', '#跨平台'], 'react native': ['#ReactNative', '#跨平台'], '数据库': ['#数据库', '#SQL'], 'mysql': ['#MySQL', '#数据库'], 'postgresql': ['#PostgreSQL', '#数据库'], 'mongodb': ['#MongoDB', '#NoSQL'], 'redis': ['#Redis', '#缓存'], 'elasticsearch': ['#Elasticsearch', '#搜索'], 'kafka': ['#Kafka', '#消息队列'], 'rabbitmq': ['#RabbitMQ', '#消息队列'], 'nginx': ['#Nginx', '#Web服务器'], 'apache': ['#Apache', '#Web服务器'], 'git': ['#Git', '#版本控制'], 'github': ['#GitHub', '#开源'], 'gitlab': ['#GitLab', '#DevOps'], 'jenkins': ['#Jenkins', '#CI/CD'], 'terraform': ['#Terraform', '#基础设施即代码'], 'ansible': ['#Ansible', '#自动化'], 'prometheus': ['#Prometheus', '#监控'], 'grafana': ['#Grafana', '#可视化'], '区块链': ['#区块链', '#Web3'], '比特币': ['#比特币', '#加密货币'], '以太坊': ['#以太坊', '#智能合约'], 'nft': ['#NFT', '#数字资产'], '元宇宙': ['#元宇宙', '#虚拟现实'], 'ar': ['#AR', '#增强现实'], 'vr': ['#VR', '#虚拟现实'], 'iot': ['#物联网', '#智能设备'], '5g': ['#5G', '#通信技术'], '6g': ['#6G', '#未来通信'], '量子计算': ['#量子计算', '#未来科技'], '芯片': ['#芯片', '#半导体'], '自动驾驶': ['#自动驾驶', '#智能交通'], '机器人': ['#机器人', '#自动化'], '无人机': ['#无人机', '#航空科技'], '生物识别': ['#生物识别', '#安全技术'], '边缘计算': ['#边缘计算', '#分布式计算'], 'serverless': ['#Serverless', '#无服务器'], '微服务': ['#微服务', '#架构设计'], 'api': ['#API', '#接口设计'], 'rest': ['#REST', '#API设计'], 'graphql': ['#GraphQL', '#API设计'], 'websocket': ['#WebSocket', '#实时通信'], 'http': ['#HTTP', '#网络协议'], 'https': ['#HTTPS', '#安全协议'], 'ssl': ['#SSL', '#加密'], 'tls': ['#TLS', '#加密'], 'oauth': ['#OAuth', '#认证授权'], 'jwt': ['#JWT', '#令牌'], 'oauth2': ['#OAuth2', '#认证协议'], 'openid': ['#OpenID', '#身份认证'], '单点登录': ['#单点登录', '#SSO'], '多因素认证': ['#多因素认证', '#MFA'], '零信任': ['#零信任', '#安全架构'], '隐私计算': ['#隐私计算', '#数据安全'], '同态加密': ['#同态加密', '#加密技术'], '联邦学习': ['#联邦学习', '#分布式AI'], '迁移学习': ['#迁移学习', '#机器学习'], '强化学习': ['#强化学习', '#机器学习'], '深度学习': ['#深度学习', '#神经网络'], '自然语言处理': ['#自然语言处理', '#NLP'], '计算机视觉': ['#计算机视觉', '#CV'], '语音识别': ['#语音识别', '#ASR'], '语音合成': ['#语音合成', '#TTS'], '推荐系统': ['#推荐系统', '#个性化'], '搜索引擎': ['#搜索引擎', '#信息检索'], '知识图谱': ['#知识图谱', '#语义网络'], '大数据': ['#大数据', '#数据处理'], '数据挖掘': ['#数据挖掘', '#数据分析'], '数据可视化': ['#数据可视化', '#图表'], '数据仓库': ['#数据仓库', '#BI'], '数据湖': ['#数据湖', '#数据存储'], '数据治理': ['#数据治理', '#数据管理'], '数据安全': ['#数据安全', '#隐私保护'], '数据合规': ['#数据合规', '#GDPR'], '数据伦理': ['#数据伦理', '#AI伦理'] } def __init__(self, config: Optional[Dict] = None): """ 初始化转换器 Args: config: 配置字典 """ self.config = config or {} self.max_title_length = self.config.get('max_title_length', 30) self.max_content_length = self.config.get('max_content_length', 1000) self.auto_generate_tags = self.config.get('auto_generate_tags', True) self.add_source_info = self.config.get('add_source_info', True) self.add_emoji = self.config.get('add_emoji', True) logger.info("内容转换器初始化完成") def to_xiaohongshu(self, article: Article) -> str: """ 将文章转换为小红书格式(使用oh-my-writing-skill优化) Args: article: 文章对象 Returns: 小红书格式的内容 """ logger.info(f"开始转换文章: {article.title}") try: # 尝试使用oh-my-writing-skill转换器 from oh_my_writing_converter import OhMyWritingConverter converter = OhMyWritingConverter() # 构建完整内容 full_content = f"# {article.title}\n\n" if article.source: full_content += f"来源: {article.source}\n\n" full_content += article.content # 转换为小红书格式 result = converter.convert_to_xiaohongshu(full_content, article.title) # 添加来源信息(如果转换器没有包含) xhs_content = result.content if article.source and f"来源: {article.source}" not in xhs_content: xhs_content = xhs_content.replace("往下看 ⬇️", f"往下看 ⬇️\n\n📰 来源: {article.source}", 1) # 添加原文链接 if article.url: xhs_content += f"\n\n🔗 原文链接: {article.url}" logger.info(f"oh-my-writing-skill转换完成,标题: {result.title}") return xhs_content except Exception as e: logger.warning(f"oh-my-writing-skill转换失败,使用基础转换: {e}") # 回退到基础转换 return self._to_xiaohongshu_basic(article) def _to_xiaohongshu_basic(self, article: Article) -> str: """基础小红书格式转换(回退方案)""" # 处理标题 title = self._process_title(article.title) # 构建小红书内容 lines = [] # 添加标题(带emoji) if self.add_emoji: title_with_emoji = self._add_title_emoji(title) lines.append(f"🔥 {title_with_emoji}") else: lines.append(f"🔥 {title}") lines.append("") # 空行 # 添加来源信息 if self.add_source_info and article.source: lines.append(f"📰 来源: {article.source}") if article.publish_date: lines.append(f"📅 时间: {article.publish_date}") if article.author: lines.append(f"✍️ 作者: {article.author}") lines.append("") # 空行 # 处理正文内容 content_lines = self._process_content(article.content) lines.extend(content_lines) # 添加原文链接 if article.url: lines.append("") lines.append(f"🔗 原文链接: {article.url}") # 添加标签 if self.auto_generate_tags: tags = self._generate_tags(article) if tags: lines.append("") lines.append(tags) # 合并所有行 xhs_content = "\n".join(lines) # 限制内容长度 if len(xhs_content) > self.max_content_length: xhs_content = xhs_content[:self.max_content_length - 3] + "..." logger.warning(f"内容过长,已截断至 {self.max_content_length} 字符") logger.info(f"基础转换完成,内容长度: {len(xhs_content)} 字符") return xhs_content def _process_title(self, title: str) -> str: """处理标题""" # 移除多余空格 title = re.sub(r'\s+', ' ', title.strip()) # 限制长度 if len(title) > self.max_title_length: title = title[:self.max_title_length - 3] + "..." return title def _add_title_emoji(self, title: str) -> str: """为标题添加emoji""" title_lower = title.lower() # 查找匹配的emoji for keyword, emoji in self.EMOJI_MAPPING.items(): if keyword in title_lower: # 在标题开头添加emoji return f"{emoji} {title}" # 如果没有匹配,使用默认emoji return f"📝 {title}" def _process_content(self, content: str) -> List[str]: """处理正文内容""" lines = [] # 分割段落 paragraphs = content.split('\n') for para in paragraphs: para = para.strip() if not para: continue # 处理列表项 if re.match(r'^[0-9]+[\.\)]', para) or para.startswith('- ') or para.startswith('• '): lines.append(f"👉 {para}") # 处理标题 elif para.endswith(':') or len(para) < 50 and not para.endswith('。'): lines.append(f"📌 {para}") # 处理长段落 elif len(para) > 80: # 拆分长段落 sub_lines = self._split_long_paragraph(para) for sub_line in sub_lines: lines.append(sub_line) else: lines.append(para) # 段落间添加空行(除了列表项) if not (para.startswith('- ') or para.startswith('• ') or re.match(r'^[0-9]+[\.\)]', para)): lines.append("") return lines def _split_long_paragraph(self, paragraph: str, max_length: int = 80) -> List[str]: """拆分长段落""" words = paragraph.split() lines = [] current_line = [] current_length = 0 for word in words: word_length = len(word) # 如果当前行加上这个词会超过限制,且当前行不为空 if current_length + word_length + (1 if current_line else 0) > max_length and current_line: lines.append(' '.join(current_line)) current_line = [word] current_length = word_length else: current_line.append(word) current_length += word_length + (1 if current_line else 0) if current_line: lines.append(' '.join(current_line)) return lines def _generate_tags(self, article: Article) -> str: """生成话题标签""" # 基础标签 tags = ['#技术资讯', '#科技新闻'] # 添加分类标签 if article.category: tags.append(f'#{article.category}') # 分析内容生成标签 content_lower = (article.title + ' ' + article.content).lower() # 查找匹配的技术关键词 matched_tags = set() for keyword, tag_list in self.TAG_MAPPING.items(): if keyword in content_lower: matched_tags.update(tag_list) # 添加匹配的标签 tags.extend(list(matched_tags)) # 限制标签数量 max_tags = self.config.get('max_tags', 10) tags = tags[:max_tags] return ' '.join(tags) def analyze_article(self, article: Article) -> Dict: """分析文章特征""" content_lower = (article.title + ' ' + article.content).lower() # 检测技术关键词 detected_keywords = [] for keyword in self.TAG_MAPPING.keys(): if keyword in content_lower: detected_keywords.append(keyword) # 计算内容特征 features = { 'title_length': len(article.title), 'content_length': len(article.content), 'image_count': len(article.images), 'detected_keywords': detected_keywords, 'recommended_tags': self._generate_tags(article).split(), 'suitable_for_xhs': len(article.content) > 100 and len(article.content) < 2000 } return features # 使用示例 if __name__ == "__main__": # 测试文章 test_article = Article( title="OpenAI发布GPT-5:AI技术的新里程碑", content="""OpenAI近日宣布正在开发GPT-5,这是其语言模型系列的最新版本。 主要特点: 1. 更强的逻辑推理能力 2. 多模态理解(文本、图像、音频) 3. 更低的计算成本 4. 更好的中文支持 技术突破: - 参数规模达到10万亿 - 训练数据涵盖100种语言 - 推理速度提升50% 应用场景: • 智能助手 • 内容创作 • 代码生成 • 教育辅导 预计GPT-5将在2025年发布,这将推动AI技术进入新的发展阶段。""", source="TechCrunch", url="https://techcrunch.com/gpt5", images=["ai_image1.jpg", "ai_image2.jpg"], publish_date="2026-03-15", author="John Doe", category="AI" ) # 创建转换器 converter = ContentConverter() # 转换文章 xhs_content = converter.to_xiaohongshu(test_article) print("=" * 60) print("小红书格式内容:") print("=" * 60) print(xhs_content) print("=" * 60) # 分析文章 analysis = converter.analyze_article(test_article) print("\n文章分析:") print(json.dumps(analysis, indent=2, ensure_ascii=False))