# Skill: Satire News Scraper for Short Drama > **技能ID**: skill_satire_news_scraper > **版本**: v1.0.0 > **分类**: L1-Content / Satire Analysis > **置信度**: 0.89 > **创建时间**: 2026-03-25 > **创建者**: Evolver Agent --- ## 1. 技能描述 从讽刺新闻网站抓取内容,识别荒诞元素,评估短剧改编潜力,输出结构化数据供短剧创作使用。 ### 1.1 核心能力 - **多源抓取**: 支持 The Onion、ClickHole、Babylon Bee 等8个主流讽刺新闻源 - **讽刺识别**: 自动识别7种讽刺类型,计算荒诞指数(absurdity_score) - **短剧适配**: 评估内容的短剧改编潜力,提取场景/人物/对话元素 - **智能输出**: 生成标准化的JSON输出,包含完整的短剧改编建议 ### 1.2 支持的数据源 | 网站 | 类型 | 抓取方式 | 优先级 | |------|------|----------|--------| | The Onion | 死板式新闻 | 网页抓取 | P0 | | ClickHole | 病毒内容 | RSS | P0 | | Babylon Bee | 保守讽刺 | RSS | P1 | | Reductress | 女性媒体讽刺 | RSS | P1 | | The Hard Times | 朋克文化 | RSS | P1 | | McSweeney's | 文学幽默 | RSS | P2 | | The Beaverton | 加拿大讽刺 | RSS | P2 | | Waterford Whispers | 爱尔兰讽刺 | RSS | P2 | --- ## 2. 输入参数 ```json { "sources": { "type": "array", "description": "目标讽刺新闻源列表", "default": ["the_onion", "clickhole", "babylon_bee"], "items": { "type": "string", "enum": ["the_onion", "clickhole", "babylon_bee", "reductress", "the_hard_times", "mcsweeneys", "the_beaverton", "waterford_whispers"] } }, "fetch_mode": { "type": "string", "description": "抓取模式", "default": "rss", "enum": ["rss", "web", "auto"] }, "limit_per_source": { "type": "integer", "description": "每源抓取数量", "default": 20, "minimum": 1, "maximum": 100 }, "min_absurdity_score": { "type": "number", "description": "最小荒诞分数阈值", "default": 6.0, "minimum": 0, "maximum": 10 }, "adaptation_filter": { "type": "string", "description": "短剧适配等级筛选", "default": "B", "enum": ["A", "B", "C", "D", "all"] }, "include_political": { "type": "boolean", "description": "是否包含政治讽刺内容(需本地化)", "default": false }, "extract_drama_elements": { "type": "boolean", "description": "是否提取短剧元素(场景/人物/对话)", "default": true } } ``` --- ## 3. 输出Schema ```json { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": ["metadata", "articles"], "properties": { "metadata": { "type": "object", "required": ["fetch_time", "total_articles", "sources"], "properties": { "fetch_time": { "type": "string", "format": "date-time" }, "total_articles": { "type": "integer" }, "sources": { "type": "array", "items": { "type": "string" } }, "filter_stats": { "type": "object", "properties": { "fetched": { "type": "integer" }, "filtered_by_absurdity": { "type": "integer" }, "filtered_by_adaptation": { "type": "integer" }, "included": { "type": "integer" } } } } }, "articles": { "type": "array", "items": { "$ref": "#/definitions/SatireArticle" } } }, "definitions": { "SatireArticle": { "type": "object", "required": ["id", "title", "content", "source", "satire_analysis"], "properties": { "id": { "type": "string", "description": "文章唯一ID" }, "title": { "type": "string", "description": "文章标题" }, "content": { "type": "string", "description": "文章正文内容" }, "source": { "type": "string", "description": "来源网站" }, "category": { "type": "string", "description": "文章分类" }, "url": { "type": "string", "description": "原文链接" }, "published_at": { "type": "string", "format": "date-time" }, "satire_analysis": { "type": "object", "required": ["absurdity_score", "satire_type", "markers"], "properties": { "absurdity_score": { "type": "number", "minimum": 0, "maximum": 10, "description": "荒诞指数 0-10" }, "satire_type": { "type": "string", "enum": ["deadpan_news", "viral_content", "conservative_satire", "feminist_media_satire", "subculture_satire", "literary_humor", "national_satire", "mild_satire", "none"] }, "markers": { "type": "array", "items": { "type": "string" }, "description": "命中的讽刺标记词" }, "premise": { "type": "string", "description": "荒诞前提摘要" }, "twist": { "type": "string", "description": "反转/笑点说明" } } }, "drama_adaptation": { "type": "object", "required": ["adaptability_rating", "recommended_duration"], "properties": { "adaptability_rating": { "type": "string", "enum": ["A", "B", "C", "D"], "description": "A=直接改编, B=需调整, C=大幅改写, D=不适合" }, "recommended_duration": { "type": "string", "enum": ["15s", "60s", "3-5min"], "description": "建议短剧时长" }, "adaptation_notes": { "type": "string", "description": "改编建议" }, "localization_required": { "type": "boolean" } } }, "drama_elements": { "type": "object", "properties": { "setting": { "type": "object", "properties": { "location": { "type": "string" }, "mood": { "type": "string" } } }, "characters": { "type": "array", "items": { "type": "object", "properties": { "role": { "type": "string", "enum": ["protagonist", "antagonist", "supporting"] }, "type": { "type": "string" }, "trait": { "type": "string" } } } }, "dialogue": { "type": "array", "items": { "type": "string" }, "description": "可直接使用的对话" }, "visual_gags": { "type": "array", "items": { "type": "string" }, "description": "视觉笑点建议" } } } } } } } ``` --- ## 4. 执行逻辑 ### 4.1 主流程 ``` ┌─────────────────┐ │ 1. 参数校验 │ └────────┬────────┘ ▼ ┌─────────────────┐ │ 2. 数据源抓取 │ ← RSS/网页抓取 └────────┬────────┘ ▼ ┌─────────────────┐ │ 3. 数据清洗 │ ← 去重、格式化 └────────┬────────┘ ▼ ┌─────────────────┐ │ 4. 讽刺分析 │ ← 计算absurdity_score └────────┬────────┘ ▼ ┌─────────────────┐ │ 5. 短剧适配评估 │ ← 评级A/B/C/D └────────┬────────┘ ▼ ┌─────────────────┐ │ 6. 元素提取 │ ← 场景/人物/对话 └────────┬────────┘ ▼ ┌─────────────────┐ │ 7. 输出结果 │ ← 生成JSON └─────────────────┘ ``` ### 4.2 数据源抓取策略 **RSS 模式(推荐)** ```python RSS_FEEDS = { "clickhole": "https://clickhole.com/feed/", "babylon_bee": "https://babylonbee.com/feed/", "reductress": "https://reductress.com/feed/", "the_hard_times": "https://thehardtimes.net/feed/", "mcsweeneys": "http://feeds.feedburner.com/mcsweeneys", "the_beaverton": "https://www.thebeaverton.com/feed/", "waterford_whispers": "https://waterfordwhispersnews.com/feed/" } ``` **网页抓取模式(The Onion)** ```python WEB_SELECTORS = { "container": "article.post", "title": "h1.headline", "content": ".entry-content", "category": ".category-tag" } ``` ### 4.3 Absurdity Score 计算算法 ```python def calculate_absurdity_score(article): """ 计算荒诞指数 (0-10) 基于四大维度加权计算 """ # 1. 标题讽刺标记 (0-3分) headline_markers = { "local_man": ["Local Man", "Area Man", "Area Woman"], "study_finds": ["Study Finds", "Report:", "Experts Say"], "absurd_escalation": ["somehow", "unexpectedly", "tragically"], "literal_interpretation": ["literally", "actually", "definitely"], "emotional_manipulation": ["Heartbreaking", "Devastating", "Inspiring"] } headline_score = 0 title_lower = article.title.lower() for marker_type, markers in headline_markers.items(): if any(m.lower() in title_lower for m in markers): headline_score += 0.6 headline_score = min(3, headline_score) # 2. 内容荒诞密度 (0-2.5分) absurdity_keywords = [ "aliens", "time travel", "talking", "supernatural", "billion", "million", "37", "entire family", "turns out", "little did they know", "shocked to discover" ] content_lower = article.content.lower() keyword_count = sum(1 for kw in absurdity_keywords if kw in content_lower) density_score = min(2.5, keyword_count * 0.3) # 3. 叙事反转强度 (0-2.5分) twist_indicators = [ ("somehow", 1.0), ("turns out", 0.8), ("unexpectedly", 0.8), ("but actually", 0.7), ("discovered that", 0.6) ] twist_score = sum(score for indicator, score in twist_indicators if indicator in content_lower) twist_score = min(2.5, twist_score) # 4. 短剧适配度 (0-2分) adaptability_indicators = { "scene_clear": ["office", "home", "restaurant", "party"], "dialogue_rich": ['"', "said", "told"], "character_distinct": ["local man", "dad", "boss", "teacher"] } adapt_score = 0 for indicator_type, keywords in adaptability_indicators.items(): if any(kw in content_lower for kw in keywords): adapt_score += 0.7 adapt_score = min(2, adapt_score) total = headline_score + density_score + twist_score + adapt_score return round(total, 1) ``` ### 4.4 讽刺类型识别规则 | 类型 | 识别规则 | 代表源 | |------|----------|--------| | `deadpan_news` | AP新闻格式 + 荒谬内容 | The Onion | | `viral_content` | 列表/测验/点击诱饵格式 | ClickHole | | `conservative_satire` | 传统价值观视角反转 | Babylon Bee | | `feminist_media_satire` | 女性媒体风格 + relatability | Reductress | | `subculture_satire` | 亚文化黑话 + 内部梗 | The Hard Times | | `literary_humor` | 高品位文学风格 | McSweeney's | | `national_satire` | 国家/地域特色讽刺 | Beaverton/Waterford | **识别函数** ```python def detect_satire_type(article): source_mapping = { "the_onion": "deadpan_news", "clickhole": "viral_content", "babylon_bee": "conservative_satire", "reductress": "feminist_media_satire", "the_hard_times": "subculture_satire", "mcsweeneys": "literary_humor", "the_beaverton": "national_satire", "waterford_whispers": "national_satire" } # 基于来源初步判断 base_type = source_mapping.get(article.source, "unknown") # 基于内容微调 title_lower = article.title.lower() if "quiz" in title_lower or "which" in title_lower: return "viral_content" elif any(x in title_lower for x in ["study finds", "report:", "local man"]): return "deadpan_news" elif "open letter" in title_lower: return "literary_humor" return base_type ``` ### 4.5 短剧适配评级算法 ```python def assess_drama_adaptation(article, absurdity_score): """ 评估短剧改编潜力,返回 A/B/C/D 评级 """ scores = { "scene_visualization": 0, # 场景可视化 (0-2.5) "dialogue_richness": 0, # 对话丰富度 (0-2.5) "conflict_clarity": 0, # 冲突明确性 (0-2.0) "twist_impact": 0, # 反转冲击力 (0-2.0) "performance_feasibility": 0 # 表演可行性 (0-1.0) } content = article.content.lower() title = article.title.lower() # 场景可视化 locations = ["office", "home", "restaurant", "party", "wedding", "hospital"] if any(loc in content for loc in locations): scores["scene_visualization"] = 2.0 elif "local" in title: scores["scene_visualization"] = 1.5 else: scores["scene_visualization"] = 1.0 # 对话丰富度 quote_count = content.count('"') if quote_count >= 4: scores["dialogue_richness"] = 2.5 elif quote_count >= 2: scores["dialogue_richness"] = 1.8 elif "said" in content or "told" in content: scores["dialogue_richness"] = 1.2 else: scores["dialogue_richness"] = 0.8 # 冲突明确性 conflict_keywords = ["refuses", "demanding", "accused", "argument", "fight"] if any(kw in content for kw in conflict_keywords): scores["conflict_clarity"] = 2.0 elif absurdity_score > 7: scores["conflict_clarity"] = 1.5 else: scores["conflict_clarity"] = 1.0 # 反转冲击力 if absurdity_score >= 8: scores["twist_impact"] = 2.0 elif absurdity_score >= 6.5: scores["twist_impact"] = 1.5 else: scores["twist_impact"] = 0.8 # 表演可行性 if any(x in content for x in ["dragon", "alien", "explosion"]): scores["performance_feasibility"] = 0.3 # 需要特效 else: scores["performance_feasibility"] = 1.0 total = sum(scores.values()) # 评级标准 if total >= 8 and absurdity_score >= 7: return "A" # 直接改编 elif total >= 6.5: return "B" # 需少量调整 elif total >= 4.5: return "C" # 需大幅改写 else: return "D" # 不适合 ``` --- ## 5. 错误处理 ### 5.1 错误码定义 | 错误码 | 描述 | 处理建议 | |--------|------|----------| | `RSS_FETCH_ERROR` | RSS feed获取失败 | 切换至网页抓取模式 | | `WEB_SCRAPE_ERROR` | 网页抓取失败 | 检查选择器,降低请求频率 | | `RATE_LIMITED` | 请求频率超限 | 降低频率至20req/min,使用代理 | | `PARSE_ERROR` | 内容解析失败 | 检查编码(强制UTF-8) | | `NO_RESULTS` | 无匹配结果 | 放宽筛选条件 | | `TIMEOUT_ERROR` | 请求超时 | 增加超时时间,分批请求 | ### 5.2 重试策略 ```python retry_policy = { "max_attempts": 3, "backoff_factor": 2, "initial_delay": 1, "retryable_errors": ["TIMEOUT_ERROR", "RATE_LIMITED", "RSS_FETCH_ERROR"] } ``` --- ## 6. 示例代码 ### 6.1 Python 调用示例 ```python import requests import json # 调用 Skill API response = requests.post( "https://api.lemonai.com/v1/skills/satire-scraper/execute", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "sources": ["the_onion", "clickhole", "babylon_bee"], "fetch_mode": "auto", "limit_per_source": 10, "min_absurdity_score": 7.0, "adaptation_filter": "B", "extract_drama_elements": True } ) result = response.json() # 处理高适配度内容 for article in result["articles"]: if article["drama_adaptation"]["adaptability_rating"] == "A": print(f"[{article['satire_analysis']['absurdity_score']}] {article['title']}") print(f"讽刺类型: {article['satire_analysis']['satire_type']}") print(f"荒诞前提: {article['satire_analysis']['premise']}") print(f"建议时长: {article['drama_adaptation']['recommended_duration']}") print("---") ``` ### 6.2 直接抓取示例 ```python import feedparser # RSS 抓取示例 def fetch_from_rss(source_url): feed = feedparser.parse(source_url) articles = [] for entry in feed.entries[:20]: article = { "title": entry.title, "content": entry.get("summary", entry.get("description", "")), "url": entry.link, "published_at": entry.get("published", "") } # 应用 absurdity_score 算法 article["absurdity_score"] = calculate_absurdity_score(article) if article["absurdity_score"] >= 6.0: articles.append(article) return articles # 抓取 ClickHole clickhole_articles = fetch_from_rss("https://clickhole.com/feed/") ``` --- ## 7. 性能指标 | 指标 | 基准值 | 说明 | |------|--------|------| | 单次请求耗时 | < 45s | 抓取3个源,每源20条 | | 日调用上限 | 500次 | 受数据源限制 | | 成功率 | > 95% | 包括重试后成功 | | absurdity_score MAE | < 1.5 | 与人工评分平均绝对误差 | | 类型分类准确率 | > 75% | 讽刺类型识别准确率 | --- ## 8. 版本历史 | 版本 | 日期 | 变更内容 | |------|------|----------| | v1.0.0 | 2026-03-25 | 初始版本,支持8个讽刺新闻源 | --- ## 9. 参考资料 - [The Onion](https://www.theonion.com/) - [ClickHole](https://clickhole.com/) - [Babylon Bee](https://babylonbee.com/) - [Reductress](https://reductress.com/) - [The Hard Times](https://thehardtimes.net/) --- *技能定义版本: v1.0.0* *最后更新: 2026-03-25* *维护者: Evolver Agent*