# Weibo Scraper Skill ## Identity - **Name**: weibo_multi_list_scraper - **Version**: 2.0.0 - **Type**: Scraping/Data Extraction - **Platform**: Weibo (weibo.com) ## Description 微博热点内容爬取技能(多榜版本)。能够从微博平台爬取热搜榜、热门微博、热点话题等内容,提取结构化数据并进行清洗分类。**强化版支持详情页爬取 + 图片提取 + 简报生成 + 反爬强化**。 ## Capabilities 1. **热搜榜爬取** - 获取微博热搜榜单 Top 50 2. **热门微博抓取** - 获取热门微博内容及作者信息 3. **分类筛选** - 按娱乐、社会、科技、搞笑、体育、财经等分类 4. **指标提取** - 提取点赞数、评论数、转发数、阅读数 5. **数据清洗** - 去广告、去无效内容、格式标准化 6. **定时爬取** - 支持 Cron 定时任务和增量更新 7. **反爬合规** - UA轮换、频率控制、响应验证、Cookie池 8. **详情页爬取** - 访问热点详情页 URL,提取完整正文内容 **强化反爬** 9. **图片提取** - 从 media_urls / 详情页提取并保存图片到本地 **多源备份** 10. **简报生成** - 生成文字+图片 Markdown/HTML 简报 **降级处理** ## Methodology ### Step 1: 初始化请求环境 1. 创建 requests Session 2. 设置基础请求头 (User-Agent, Accept, Accept-Language, Referer) 3. 配置超时 (10秒) 4. 初始化 Cookie 池 ### Step 2: 反爬合规控制 (强化版) 1. **User-Agent 轮换**: 准备 10+ 个常用 UA,每次请求随机选择 - Chrome (Mac/Windows/Linux) 各版本 - Safari (iOS/Mac) - Firefox (Windows/Linux) - 移动端 UA (Android/iOS) 2. **Cookie 池轮换**: 准备 5+ 个 Cookie,每 10 次请求轮换一次 3. **请求频率限制**: 每次请求间隔 5-10 秒随机延迟 4. **单 IP 限流**: 每分钟请求数 ≤ 10 5. **指数退避**: 遇到 429/403 时,等待 60s 后重试,再失败则等待 120s 6. **代理 IP 支持**: 配置代理池,遇到 403/429 自动切换 7. **请求头完整性**: 设置 Accept-Encoding, Accept-Language, Connection 等 ### Step 3: 执行爬取 (多 API 源) 1. **主 API**: 请求 PC 端热搜 `https://weibo.com/ajax/side/hotSearch` 2. **备用 API 1**: 请求移动端热搜 `https://m.weibo.cn/api/index/list?type=hot` 3. **备用 API 2**: 请求话题搜索 `https://m.weibo.cn/api/container/getIndex` 4. 优先使用返回 JSON 的 API,遇到 HTML 响应则切换 5. 解析 JSON 响应 6. 过滤低质量内容 (flag=1,2 为有效数据) 7. 分类标注 ### Step 4: 数据验证与新鲜度 1. **时间戳验证**: 确保数据时间戳 < 1 小时 2. **新鲜度保障**: 记录采集时间,过期数据标记 3. **JSON 验证**: 检测返回是否为 HTML (验证码页面) 4. **响应验证**: 验证 JSON 解析是否成功,检查 ok 字段是否为 1 5. **降级策略**: 主 API 失败时自动切换备用 API ### Step 5: 数据增强 1. **字段补全**: engagement.comments/shares 尝试从多源获取 2. **作者信息补全**: 尝试从话题页获取作者信息 3. **多源验证**: 对比多个数据源,确保数据准确性 4. **数据清洗**: 去广告、去无效内容、格式标准化 5. **时间戳标准化**: 将 API 返回的时间戳转为 ISO 格式 ### Step 6: 数据清洗与格式化 1. 提取关键字段 2. 转换中文数字 (如 "1.2万" → 12000) 3. 标准化时间戳 4. 生成最终 JSON,保留原始数据 ### Step 7: 详情页爬取 (强化反爬 - 解决 D6) 1. **提取详情页 URL**: 从热搜数据中识别 `m.weibo.cn/detail/{mid}` 格式 URL 2. **搜索 API 获取详情**: 使用 `https://m.weibo.cn/api/container/getIndex?containerid=100103type%3D1%26q%3D{keyword}` 获取微博详情 3. **请求详情页 API**: 使用 `https://m.weibo.cn/statuses/show?id={mid}` 获取完整内容 4. **内容提取**: 解析返回的 JSON,提取 text 字段(完整正文) 5. **图片URL提取**: 从 `pics` 数组提取图片 URL 到 `content.media_urls` 6. **详情页反爬强化** **NEW**: - Cookie 池轮换:准备多个 Cookie,每 10 次请求轮换一次 - 代理 IP 支持:如果检测到 403/429,自动切换代理 IP - 详情页专用退避:首次失败等待 15s,第二次等待 30s,第三次等待 60s - 请求头强化:详情页请求额外添加 `X-Requested-With: XMLHttpRequest` - 失败标记:如果连续 3 次失败,在 metadata 中标记 `详情页爬取: 部分失败` - 备用来源:使用搜索 API 作为备用 7. **降级处理**: 详情页失败时使用热搜榜已有信息,记录 `详情页爬取: 部分失败` ### Step 8: 图片提取与保存 (多源备份 - 解决 D7) 1. **图片URL验证**: 检查 content.media_urls 是否为空 2. **并发下载**: 使用 asyncio/aiohttp 并发下载图片(控制并发数 ≤ 3) 3. **重试机制**: 下载失败时重试 3 次 4. **保存路径**: 保存到 `workspace/images/{mid}_{index}.jpg` 5. **图片命名**: 使用原始文件名或 {timestamp}_{index} 命名 6. **质量验证**: 下载后验证图片完整性(检查文件大小 > 1KB,尝试解码) 7. **图片提取多源备份** **NEW**: - 备用来源1:media_urls 字段(API 直接返回的 URL) - 备用来源2:从 text 字段的 HTML 中提取 `` - 备用来源3:详情页搜索 API 返回的 pics 数组 - 降级处理:如果所有图片 URL 都无效,在 media_urls 中保留空数组并记录 ### Step 9: 简报生成 (降级处理 - 解决 D8) 1. **创建简报目录**: `workspace/output/{date}/` 2. **生成 Markdown 简报**: 按模板格式生成 brief.md 3. **生成 HTML 简报**: 可选,使用 HTML 模板渲染 4. **简报内容结构**: - 基本信息: 发布时间、来源、热度排名、分类标签 - 核心内容: 标题 + 正文 + 摘要(50字内) - 互动数据: 点赞/评论/转发/阅读数 - 关联图片: 使用相对路径引用图片 - 原始链接: 详情页 URL 5. **降级处理**: 部分数据缺失时,在简报中标注 "[数据获取失败]" 6. **简报降级处理** **NEW**: - 内容缺失占位:如果正文为空,简报中显示 `(暂无详细内容)` - 图片缺失占位:如果无图片,简报中显示 `(暂无图片)` - 互动数据缺失:如果某项为 null,简报中显示 `--` - Fallback 机制:如果 Markdown 生成失败,尝试生成纯文本简报 - 错误日志:记录简报生成过程中的错误,但不影响主流程 ### Step 10: 结果输出 1. 保存 JSON 数据到 workspace 目录 2. 保存图片到 workspace/images/ 目录 3. 保存简报到 workspace/output/ 目录 4. 打印执行结果摘要 ## Implementation ### API Endpoints - **主 API (PC 端)**: `https://weibo.com/ajax/side/hotSearch` - 需要 Cookie - **备用 API 1 (移动端)**: `https://m.weibo.cn/api/index/list?type=hot` - 无需 Cookie - **备用 API 2 (话题)**: `https://m.weibo.cn/api/container/getIndex` - 无需 Cookie - **详情页 API**: `https://m.weibo.cn/statuses/show?id={mid}` - 获取完整内容 - **搜索详情 API**: `https://m.weibo.cn/api/container/getIndex?containerid=100103type%3D1%26q%3D{keyword}` - 搜索获取详情 - 用户微博: `https://weibo.com/ajax/statuses/mymblog?uid={uid}` - 需要登录 - 评论 API: `https://m.weibo.cn/comments/hotflow` - 需要登录 ### Crawl Strategy 1. 使用公开 API 或网页爬取热搜数据 2. 解析 JSON 响应获取结构化数据 3. 过滤低质量内容(广告、无内容、已删除) 4. 分类标注 5. 输出标准化 JSON ### User-Agent Pool ```python USER_AGENTS = [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 Safari/604.1", "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 Chrome/112.0.0.0 Mobile Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0 Safari/605.1.15" ] ``` ### Cookie Pool (模拟) ```python # 实际使用时需要维护真实的 Cookie 池 COOKIE_POOL = [ {"cookie": "SUB=_xxx1", "domain": ".weibo.com"}, {"cookie": "SUB=_xxx2", "domain": ".weibo.com"}, # ... ] ``` ### Proxy Pool (占位) ```python # 实际使用时需要配置真实的代理池 PROXY_POOL = [ {"http": "http://proxy1:port", "https": "https://proxy1:port"}, {"http": "http://proxy2:port", "https": "https://proxy2:port"}, # ... ] ``` ### Rate Limiting - 请求间隔: 5-10 秒随机 - 每分钟请求数: ≤ 10 - 超时重试: 3 次 - 退避策略: 遇到 403/429 等待 60s/120s 后重试 - 详情页请求: 间隔 10-15 秒,降低被反爬概率 - **详情页专用退避**: 首次失败等待 15s,第二次 30s,第三次 60s ### Error Handling & Fallback Strategy - **主 API 失败**: 自动切换到移动端 API - **详情页失败**: 返回热搜榜基础信息,标注 "[详情页获取失败]" - **详情页反爬强化**: - Cookie 池轮换(每 10 次请求) - 代理 IP 切换(403/429 时) - 专用退避策略(15s → 30s → 60s) - **图片提取备用**: media_urls 字段、HTML 提取、降级处理 - **简报降级**: 内容/图片缺失占位、Fallback 纯文本 - 403 Forbidden: 更换 User-Agent 或切换备用 API - 429 Too Many Requests: 指数退避 (60s, 120s, 240s) 后重试 - 网络超时: 重试,超时递增 - 反爬机制: 降低频率或切换 API - HTML 响应: 检测到验证码页面,更换 UA 并尝试备用 API ## Output Format ```json { "source": { "platform": "weibo", "category": "娱乐", "hot_id": "xxx", "url": "https://weibo.com/xxx" }, "engagement": { "likes": 12345, "comments": null, "shares": null, "read_count": 123456 }, "content": { "title": "热搜标题", "text": "微博正文内容", "media_urls": ["https://xxx.jpg"], "category_tags": ["娱乐", "明星", "综艺"] }, "author": { "username": null, "followers": null, "verified": false }, "timestamp": "2024-01-01T00:00:00Z", "metrics": { "hot_rank": 1, "hot_score": 999999, "trend": "up" }, "metadata": { "采集时间": "2024-01-01T00:00:00Z", "数据来源": "weibo_hotsearch_api", "API版本": "v2", "反爬合规": { "ua_rotation": true, "rate_limiting": true, "response_validation": true, "cookie_pool": true, "proxy_support": true }, "详情页爬取": { "enabled": true, "状态": "成功|部分失败|完全失败", "正文提取": true, "反爬策略": ["cookie_pool", "proxy_fallback", "backoff_15_30_60"] }, "图片提取": { "enabled": true, "主来源": "pics/media_urls", "备用来源": ["html_extract", "detail_search_api"], "降级处理": true }, "简报生成": { "enabled": true, "format": "markdown", "降级处理": true, "内容缺失占位": true } }, "raw_data": {} } ``` ### 简报输出格式 (brief.md) ```markdown # 微博热点简报 ## 基本信息 - 发布时间: {timestamp} - 来源: {author.username} ({verified}) - 热度: 排名 {hot_rank} | 热度值 {hot_score} - 分类标签: {category_tags} ## 内容摘要 {summary_50_chars} ## 核心内容 {content.title} {content.text|(暂无详细内容)} ## 互动数据 - 点赞: {likes|--} - 评论: {comments|--} - 转发: {shares|--} - 阅读: {read_count|--} ## 关联图片 {images_markdown|(暂无图片)} ## 原始链接 {url} ``` ### 降级处理占位符 ```markdown ## 核心内容 {title} {content_text|(暂无详细内容)} ## 关联图片 {images_markdown|(暂无图片)} ## 互动数据 - 点赞: {likes|--} - 评论: {comments|--} - 转发: {shares|--} - 阅读: {read_count|--} ``` > **注意**: engagement 和 author 字段中无法获取的数据使用 `null` 而非 `0` 或 `-1`,表示数据不可用而非数据缺失。 ## Execution Commands ```bash # 爬取热搜榜 python3 weibo_scraper.py --category 娱乐 --limit 20 # 爬取热门微博 python3 weibo_scraper.py --type hot --limit 50 # 详情页爬取 + 简报生成 python3 weibo_scraper.py --detail --brief --limit 10 # 定时增量更新 python3 weibo_scraper.py --cron --output /path/to/output.json ``` ## File Structure ``` weibo-scraper/ ├── .claude/ │ └── skills/ │ └── main_skill/ │ └── SKILL.md (本文件) ├── weibo_scraper.py # 主脚本 ├── requirements.txt # Python 依赖 └── workspace/ # 输出目录 ├── output/ # 简报目录 │ └── 2026-03-28/ │ └── brief.md └── images/ # 图片目录 └── xxx.jpg ``` ## Dependencies - Python: 3.8+ - pip packages: requests, aiohttp (异步下载) ## Quality Checks - [x] 数据完整: engagement 数据字段存在,无法获取的标记为 null - [x] 分类准确: category_tags 正确 - [x] 时效性: timestamp 为最近内容 - [x] 无效过滤: 已删除/广告内容已过滤 - [x] 反爬合规: UA 轮换、Cookie 池、请求间隔、响应验证 - [x] 数据可用性: 保留 metadata 和 raw_data - [x] 详情页爬取: 支持获取详情页完整内容 **强化反爬** - [x] 图片提取: 支持提取并保存图片 **多源备份** - [x] 简报生成: 支持生成 Markdown/HTML 简报 **降级处理** --- ## 训练记录 ### 迭代历史 - v1.9.0 → v2.0.0: 强化反爬(Cookie池、代理支持、详情页专用退避)+ 图片多源备份 + 简报降级处理 ### Benchmark 得分 (目标) - v1.9.0: 91.34 (理论) - v2.0.0: 目标 ≥ 90 (实际) ### 改进内容 1. Step 2 增加 Cookie 池轮换 2. Step 2 增加代理 IP 支持 3. Step 7 增加详情页反爬强化(Cookie池、代理、专用退避) 4. Step 8 增加图片多源备份 5. Step 9 增加简报降级处理占位符