"""平台规则引擎 - 各内容平台的规则和最佳实践""" import re PLATFORM_RULES: dict[str, dict] = { "wechat": { "name": "微信公众号", "icon": "wechat", "max_title_length": 22, "max_content_length": 20000, "min_content_length": 300, "supported_media": ["image", "video", "audio"], "max_images": 20, "best_publish_times": ["07:30-08:30", "11:30-12:30", "17:30-18:30", "20:30-22:00"], "best_publish_days": ["周二", "周四", "周六"], "format_features": { "supports_markdown": False, "supports_html": True, "max_heading_level": 3, "supports_code_block": True, }, "rules": [ "不得使用诱导分享/关注语句", "图片单张不超过10MB", "标题不含连续特殊符号(如!!!)", "正文不含外部链接(仅支持公众号链接和小程序)", "不得包含未经授权的商标/品牌名称", ], "seo_tips": [ "首段包含核心关键词", "使用小标题分段(适配搜一搜)", "文末设置话题标签", "摘要控制在120字内", ], }, "zhihu": { "name": "知乎", "icon": "zhihu", "max_title_length": 30, "max_content_length": 50000, "min_content_length": 500, "supported_media": ["image", "video"], "max_images": 50, "best_publish_times": ["09:00-10:00", "14:00-15:00", "20:00-22:00"], "best_publish_days": ["周一", "周三", "周五"], "format_features": { "supports_markdown": True, "supports_html": False, "max_heading_level": 4, "supports_code_block": True, }, "rules": [ "回答需针对问题本身", "不得过度营销或硬广", "引用需标注来源", "不得使用AI水文(内容需有信息增量)", ], "seo_tips": [ "回答开头直接给出结论", "使用数据和案例支撑", "合理设置二级标题", "文末引导关注专栏", ], }, "xiaohongshu": { "name": "小红书", "icon": "xiaohongshu", "max_title_length": 20, "max_content_length": 1000, "min_content_length": 100, "supported_media": ["image", "video"], "max_images": 18, "best_publish_times": ["07:00-09:00", "12:00-13:00", "18:00-20:00", "21:00-23:00"], "best_publish_days": ["周末", "周三"], "format_features": { "supports_markdown": False, "supports_html": False, "max_heading_level": 0, "supports_code_block": False, "supports_emoji": True, }, "rules": [ "首图质量决定点击率", "正文控制在300-800字", "话题标签3-8个", "不得出现其他平台引流信息", "图片不含水印", ], "seo_tips": [ "标题含数字更吸引点击", "正文用短句+emoji分段", "话题标签放文末", "首句即核心观点", ], }, "baijiahao": { "name": "百家号", "icon": "baidu", "max_title_length": 30, "max_content_length": 30000, "min_content_length": 800, "supported_media": ["image", "video"], "max_images": 30, "best_publish_times": ["08:00-09:00", "11:00-12:00", "17:00-18:00"], "best_publish_days": ["工作日"], "format_features": { "supports_markdown": False, "supports_html": True, "max_heading_level": 3, "supports_code_block": False, }, "rules": [ "原创内容优先推荐", "标题不含夸张/标题党词汇", "正文需含至少1张配图", "不得搬运/洗稿", ], "seo_tips": [ "标题包含百度搜索热词", "文章结构化(H2小标题)", "适当添加内链", "发布后及时答复评论", ], }, "douyin": { "name": "抖音", "icon": "douyin", "max_title_length": 55, "max_content_length": 5000, "min_content_length": 0, "supported_media": ["video", "image"], "max_images": 35, "best_publish_times": ["06:00-08:00", "12:00-13:00", "18:00-20:00", "22:00-23:00"], "best_publish_days": ["每天"], "format_features": { "supports_markdown": False, "supports_html": False, "max_heading_level": 0, "supports_code_block": False, "supports_emoji": True, }, "rules": [ "视频/图文需原创", "不得含其他平台水印", "话题标签2-5个", "文案简短有吸引力", ], "seo_tips": [ "前3秒决定完播率", "标题含热点关键词", "评论区互动提升权重", "合适的话题+POI定位", ], }, "toutiao": { "name": "今日头条", "icon": "toutiao", "max_title_length": 30, "max_content_length": 30000, "min_content_length": 500, "supported_media": ["image", "video"], "max_images": 30, "best_publish_times": ["07:00-08:00", "12:00-13:00", "18:00-19:00", "21:00-22:00"], "best_publish_days": ["每天"], "format_features": { "supports_markdown": False, "supports_html": True, "max_heading_level": 3, "supports_code_block": False, }, "rules": [ "标题不得标题党", "内容需有信息价值", "首发原创优先推荐", "配图清晰不模糊", ], "seo_tips": [ "标题含核心关键词", "文章1500字以上推荐更高", "合理使用头条话题", "发布频率稳定提升权重", ], }, } # 诱导分享/关注关键词 _INDUCING_PATTERNS = re.compile( r"(转发|分享|关注|点赞|收藏).{0,4}(领|获|得|拿|解锁|免费)", re.IGNORECASE, ) # 连续特殊符号 _CONSECUTIVE_SYMBOLS = re.compile(r"[!!??]{3,}") # 外部链接(排除公众号和小程序链接) _EXTERNAL_LINK = re.compile( r"https?://(?!mp\.weixin\.qq\.com|wx\.qq\.com|weixin://)[^\s<>))]+", re.IGNORECASE, ) # 标题党关键词 _CLICKBAIT_WORDS = {"震惊", "惊呆", "吓死", "笑死", "疯传", "刷屏", "出大事", "不敢相信"} # 水印检测(简化:检测常见平台水印文本) _WATERMARK_PATTERNS = re.compile( r"(抖音|快手|小红书|微博|B站|bilibili).*(水印|logo)", re.IGNORECASE, ) class PlatformRuleEngine: """平台规则引擎""" def get_platforms(self) -> list[dict]: """获取所有支持平台列表""" return [ { "id": key, "name": val["name"], "icon": val["icon"], "max_title_length": val["max_title_length"], "max_content_length": val["max_content_length"], "min_content_length": val["min_content_length"], "supported_media": val["supported_media"], "max_images": val["max_images"], } for key, val in PLATFORM_RULES.items() ] def get_platform_rules(self, platform: str) -> dict | None: """获取指定平台的完整规则""" return PLATFORM_RULES.get(platform) def validate_content(self, content: str, title: str, platform: str) -> dict: """ 校验内容是否符合平台规则 返回: { "is_valid": bool, "score": int (0-100), "issues": [{"severity": "high|medium|low", "message": "..."}], "passed": ["规则1", "规则2"] } """ rules = PLATFORM_RULES.get(platform) if rules is None: return { "is_valid": False, "score": 0, "issues": [{"severity": "high", "message": f"不支持的平台: {platform}"}], "passed": [], } issues: list[dict] = [] passed: list[str] = [] # --- 标题长度 --- title_len = len(title) max_title = rules["max_title_length"] if title_len > max_title: issues.append({ "severity": "high", "message": f"标题长度 {title_len} 超过限制 {max_title}", }) else: passed.append(f"标题长度合规({title_len}/{max_title})") # --- 内容长度 --- content_len = len(content) max_content = rules["max_content_length"] min_content = rules["min_content_length"] if content_len > max_content: issues.append({ "severity": "high", "message": f"内容长度 {content_len} 超过限制 {max_content}", }) elif min_content > 0 and content_len < min_content: issues.append({ "severity": "medium", "message": f"内容长度 {content_len} 低于建议最低 {min_content}", }) else: passed.append(f"内容长度合规({content_len}/{max_content})") # --- 平台特有规则 --- platform_specific = self._validate_platform_specific(content, title, platform) issues.extend(platform_specific["issues"]) passed.extend(platform_specific["passed"]) # --- 计算分数 --- total_checks = len(issues) + len(passed) if total_checks == 0: score = 100 else: # high=扣15分, medium=扣8分, low=扣3分 penalty = sum( 15 if i["severity"] == "high" else 8 if i["severity"] == "medium" else 3 for i in issues ) score = max(0, 100 - penalty) return { "is_valid": len([i for i in issues if i["severity"] == "high"]) == 0, "score": score, "issues": issues, "passed": passed, } def _validate_platform_specific( self, content: str, title: str, platform: str ) -> dict: """平台特定规则校验""" issues: list[dict] = [] passed: list[str] = [] if platform == "wechat": # 诱导分享/关注 if _INDUCING_PATTERNS.search(title) or _INDUCING_PATTERNS.search(content): issues.append({ "severity": "high", "message": "包含诱导分享/关注语句", }) else: passed.append("无诱导分享/关注语句") # 连续特殊符号 if _CONSECUTIVE_SYMBOLS.search(title): issues.append({ "severity": "medium", "message": "标题包含连续特殊符号", }) else: passed.append("标题无连续特殊符号") # 外部链接 if _EXTERNAL_LINK.search(content): issues.append({ "severity": "high", "message": "正文包含外部链接(仅支持公众号链接和小程序)", }) else: passed.append("无外部链接") elif platform == "zhihu": # 营销内容检测(简化) marketing_words = {"购买", "下单", "优惠价", "限时折扣", "点击购买"} found_marketing = marketing_words & set(content) if found_marketing: issues.append({ "severity": "medium", "message": f"疑似营销用语: {', '.join(found_marketing)}", }) else: passed.append("未检测到过度营销用语") elif platform == "xiaohongshu": # 字数建议 content_len = len(content) if content_len > 800: issues.append({ "severity": "medium", "message": f"正文建议300-800字,当前 {content_len} 字", }) elif content_len < 300: issues.append({ "severity": "low", "message": f"正文建议300-800字,当前仅 {content_len} 字", }) else: passed.append(f"正文字数适宜({content_len}字)") # 其他平台引流 cross_platform_keywords = ["微信", "公众号", "抖音号", "微博"] found_cross = [p for p in cross_platform_keywords if p in content] if found_cross: issues.append({ "severity": "high", "message": f"疑似其他平台引流: {', '.join(found_cross)}", }) else: passed.append("未检测到其他平台引流信息") elif platform in ("baijiahao", "toutiao"): # 标题党检测 found_clickbait = _CLICKBAIT_WORDS & set(title) if found_clickbait: issues.append({ "severity": "high", "message": f"标题含标题党词汇: {', '.join(found_clickbait)}", }) else: passed.append("标题无标题党词汇") elif platform == "douyin": # 水印检测 if _WATERMARK_PATTERNS.search(content): issues.append({ "severity": "high", "message": "内容包含其他平台水印信息", }) else: passed.append("未检测到其他平台水印") return {"issues": issues, "passed": passed} def get_optimization_tips(self, platform: str) -> list[str]: """获取平台优化建议""" rules = PLATFORM_RULES.get(platform) if rules is None: return [] return rules.get("seo_tips", [])