#!/usr/bin/env python3 """ 内容创作脚本 每天凌晨5:30运行,从选题库选出最适合当天发布的题目,创作内容,生成图片内联HTML """ import os import sys import yaml import json import datetime import logging import random from pathlib import Path from typing import Dict, List, Optional, Tuple import hashlib from dataclasses import dataclass, asdict import subprocess import re # 项目根目录 PROJECT_ROOT = Path(__file__).parent.parent sys.path.insert(0, str(PROJECT_ROOT)) # 导入图片生成器 from scripts.image_generator import ImageGenerator # 配置路径 CONFIG_DIR = PROJECT_ROOT / "config" DATA_DIR = PROJECT_ROOT / "automation" / "data" TEMPLATES_DIR = PROJECT_ROOT / "automation" / "templates" IMAGES_DIR = PROJECT_ROOT / "automation" / "images" LOGS_DIR = PROJECT_ROOT / "automation" / "logs" TODAY = datetime.datetime.now().strftime("%Y-%m-%d") # 日志配置 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(LOGS_DIR / f"creator_{TODAY}.log"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) @dataclass class ContentArticle: """内容文章""" id: str topic_id: str title: str platform: str # zhihu, wechat, xiaohongshu content: str # HTML内容 image_paths: List[str] metadata: Dict created_date: str output_dir: str class ContentCreator: """内容创作器""" def __init__(self): self.load_config() self.today_dir = DATA_DIR / "drafts" / TODAY self.today_dir.mkdir(parents=True, exist_ok=True) self.release_dir = DATA_DIR / "releases" / TODAY self.release_dir.mkdir(parents=True, exist_ok=True) # 结果存储 self.articles: List[ContentArticle] = [] def load_config(self): """加载配置文件""" with open(CONFIG_DIR / "sources.yaml", "r", encoding='utf-8') as f: self.config = yaml.safe_load(f) with open(CONFIG_DIR / "wecom_config.yaml", "r", encoding='utf-8') as f: self.wecom_config = yaml.safe_load(f) # 平台规则 self.platform_rules = self.wecom_config["content_rules"] logger.info("配置加载完成") def select_topic_for_today(self) -> Optional[Dict]: """选择最适合当天发布的选题""" topics_file = DATA_DIR / "sustainability_topics.json" if not topics_file.exists(): logger.error("选题库文件不存在") return None with open(topics_file, 'r', encoding='utf-8') as f: all_topics = json.load(f) if not all_topics: logger.warning("选题库为空") return None # 选择策略:优先选择高优先级、未发布、匹配当天趋势的选题 available_topics = [t for t in all_topics if t.get("status") != "已发布"] if not available_topics: logger.warning("没有未发布的选题") return None # 简单策略:选择优先级最高的 selected = max(available_topics, key=lambda t: t.get("priority_score", 0)) # 获取相关案例 case_ids = selected.get("cases", []) cases_file = DATA_DIR / "sustainability_cases.json" related_cases = [] if cases_file.exists(): with open(cases_file, 'r', encoding='utf-8') as f: all_cases = json.load(f) related_cases = [c for c in all_cases if c.get("id") in case_ids] logger.info(f"选择了选题: {selected.get('title')} (优先级: {selected.get('priority_score')})") return {"topic": selected, "cases": related_cases} def create_content(self, topic_data: Dict) -> str: """基于选题创作内容""" topic = topic_data["topic"] cases = topic_data["cases"] # 核心内容结构 sections = [ self._create_introduction(topic, cases), self._create_global_cases_section(cases), self._create_china_pain_analysis(topic), self._create_localization_solution(topic), self._create_mvp_actions(topic), self._create_conclusion(topic) ] content = "\n\n".join(sections) # 插入图片标记 image_markers = [ "[IMAGE: cover]", "[IMAGE: data_chart]", "[IMAGE: case_comparison]", "[IMAGE: action_checklist]" ] # 在合适位置插入图片标记 content_with_images = self._insert_image_markers(content, image_markers) return content_with_images def _create_introduction(self, topic: Dict, cases: List[Dict]) -> str: """创建引言部分""" title = topic.get("title", "") category = cases[0].get("category", "可持续生活") if cases else "可持续生活" # 添加时效性元素(基于当天日期) today_str = datetime.datetime.now().strftime("%Y年%m月%d日") introduction = f"""
今天是{today_str},全球可持续性领域又有新的进展。根据最新收集的数据和案例,我们发现{category}领域出现了一些值得关注的新趋势。
这些国际案例对中国读者有什么启示?它们能否在中国落地?本文将通过全球案例对比和中国痛点分析,给出具体的本土化建议和可执行行动清单。
核心观点:国际先进经验不能盲目照搬,必须结合中国现实进行调整。关键在于找到"最小可行行动",让可持续生活从理念变为日常实践。
""" return introduction.strip() def _create_global_cases_section(self, cases: List[Dict]) -> str: """创建全球案例部分""" if not cases: return "" case_sections = [] for i, case in enumerate(cases[:3], 1): # 限制3个案例 country = case.get("country", "全球") title = case.get("title", "") core_idea = case.get("core_idea", "") data_facts = case.get("data_facts", "") case_html = f"""核心方法: {core_idea[:150]}...
数据支撑: {data_facts}
全球优势: {case.get('global_advantage', '需进一步分析')}
""" case_sections.append(case_html.strip()) return "\n".join(case_sections) def _create_china_pain_analysis(self, topic: Dict) -> str: """创建中国痛点分析""" china_pains = topic.get("china_pain_points", "中国相关数据不足,需本土化验证") return f"""将上述国际案例在中国落地时,通常会遇到以下问题:
具体到本次选题,主要痛点是:{china_pains}
""" def _create_localization_solution(self, topic: Dict) -> str: """创建本土化方案""" solution = topic.get("localization_solution", "国际案例中国化适配方案") return f"""基于中国现实,建议采用以下适配策略:
具体方案:{solution}
""" def _create_mvp_actions(self, topic: Dict) -> str: """创建MVP行动清单""" mvp_actions = topic.get("mvp_actions", "读者可立即尝试的3个行动") return f"""以下是从今天开始可以执行的行动:
具体行动:{mvp_actions}
""" def _create_conclusion(self, topic: Dict) -> str: """创建结论部分""" title = topic.get("title", "") return f"""{title}的核心在于行动而非理论。国际案例提供参考,但最终的成功取决于在中国环境下的创造性适配。
建议读者:
宇之然将持续关注全球可持续性趋势,并提供更多中国落地的实践指南。
(本文由宇之然AI助手基于全球案例数据库生成,数据来源可靠,内容经合规审查)
""" def _insert_image_markers(self, content: str, markers: List[str]) -> str: """在内容中插入图片标记""" lines = content.split('\n') result_lines = [] image_index = 0 for line in lines: result_lines.append(line) # 在合适位置插入图片标记(如段落之后) if line.startswith('", tags_section + "\n") elif platform == "wechat": abstract = content[:100] + "..." html = html.replace("", f'
{abstract}
') elif platform == "xiaohongshu": field = "可持续生活" if topic_data and isinstance(topic_data, dict): topic_field = topic_data.get("topic", {}).get("field", "") if topic_field: field = topic_field hashtag_map = { "未来工作方式": ["#远程工作", "#数字游民", "#AI副业"], "可持续生活系统": ["#可持续生活", "#零浪费", "#环保"], "个人知识工厂": ["#第二大脑", "#PKM", "#个人成长"], "科技人文交叉": ["#科技", "#AI伦理", "#数字健康"], } hashtags = hashtag_map.get(field, ["#可持续生活", "#全球视野", "#宇之然"])[:5] hashtags_section = '
' html = html.replace("", hashtags_section) # 5. 替换日期和标题 title = "可持续性内容" if topic_data and isinstance(topic_data, dict): topic_title = topic_data.get("topic", {}).get("title") if topic_title: title = topic_title html = html.replace("{{DATE}}", TODAY) html = html.replace("{{TITLE}}", title) # Debug: check tags presence if platform == "zhihu": tags_pos = html.find('class="tags"') if tags_pos != -1: snippet = html[max(0, tags_pos-50):tags_pos+100] logger.info(f"[DEBUG] Tags found: ...{snippet}...") else: logger.info("[DEBUG] Tags section not found") return html def _get_base_template(self) -> str: """获取基础HTML模板""" return """