详细分析 ▾
运行时依赖
版本
**重大技能重构:XClaw 现成为本地 X (Twitter) 帖子提取器,专注于抓取与归档。** - 移除所有先前的基于 API 的情报、分析与端点文档。 - 清除与原情报功能及命令集相关的所有代码与引用。 - 技能现仅提供一项主要功能:提取并归档 X 时间线/用户帖子,含互动数据与本地媒体下载。 - 提供严格的抓取情报文件输出格式,包括帖子元数据、指标及文件/文件夹命名规范。 - 更新使用说明与工作流,仅用于本地抓取与文件管理。
安装命令
点击复制技能文档
# XCLAW 从 X(Twitter)页面提取帖子,包含完整元数据:文本、作者、时间戳、互动指标、媒体 URL,并下载本地副本。 ## 使用场景 - 列出某用户主页的最新帖子 - 提取主页时间线帖子 - 不进入单条帖子即可抓取帖子链接 - 收集互动指标(点赞、转推、回复、浏览量) - 下载图片与视频缩略图 - 分析多条帖子的内容模式 --- ## 文件结构 `` intel/x/ ├── {date}-{time}-X.md # 情报文件 └── media/ └── {YYYYMMDD}-{HHMMSS}/ # 媒体文件夹(与情报文件同名时间戳) ├── image_{postId}_{index}.jpg └── video_{postId}_poster.jpg ` --- ## 文件命名 | 项目 | 格式 | 示例 | |------|--------|---------| | 情报文件 | {date}-{time}-X.md | 2026-03-16-143052-X.md | | 媒体文件夹 | {YYYYMMDD}-{HHMMSS} | 20260316-143052 | | 图片 | image_{postId}_{index}.jpg | image_2032741269689213289_0.jpg | | 视频缩略图 | video_{postId}_poster.jpg | video_2032741269689213289_poster.jpg | --- ## 输出格式 —— 必须严格遵循的格式 以如下精确格式写入情报文件。每个组件都必须严格遵守。 ` # X Intelligence Report Scraped: {date} {time} (Asia/Shanghai) Source: X Home Timeline Posts Collected: {count} --- ## Post {n}: {Topic Title} Author: {Name} (@{handle}) Posted: {relative time} URL: {post_url} Content: {post_text_content} Metrics: ❤️ {likes} 🔁 {reposts} 💬 {replies} 👁 {views} Media: 🖼️ media/{folder}/image_{postId}_{index}.jpg Signal: {🔴 HIGH / 🟡 MEDIUM / 🟡 LOW / ⚪ SKIP} — {Brief signal assessment} --- ## Post {n+1}: ... (更多帖子...) --- ## Summary High Signal (Tier 1): 1. {Topic} — {一句话摘要} 2. {Topic} — {一句话摘要} Medium Signal (Tier 2): - {Topic} — {一句话摘要} - {Topic} — {一句话摘要} Skip: - {Topic} — {原因} ` ### 输出格式中的媒体部分(条件) 如果帖子包含媒体,在 Metrics 后插入 Media 行: ` Media: 🖼️ media/{folder}/image_{postId}_{index}.jpg ` 视频缩略图则写: ` Media: 🎬 media/{folder}/video_{postId}_poster.jpg ` 若帖子无媒体,则完全省略 Media 部分。 --- ## 工作流 ### 步骤 1:创建媒体文件夹 在提取任何帖子前,先创建媒体文件夹: `bash # 按当前时间戳创建文件夹 # 格式:YYYYMMDD-HHMMSS mkdir -p ../../intel/x/media/{YYYYMMDD}-{HHMMSS} # 示例 mkdir -p ../../intel/x/media/20260316-143052 ` ### 步骤 2:导航至目标页面 `javascript // 主页时间线 browser.navigate({ url: "https://x.com/home" }) // 指定用户主页 browser.navigate({ url: "https://x.com/username" }) ` ### 步骤 3:滚动加载更多帖子 `javascript browser.act({ fn: "() => { for(let i=0; i<3; i++) { window.scrollBy(0, window.innerHeight); } return 'scrolled'; }", kind: "evaluate" }) ` ### 步骤 4:提取帖子及媒体链接 `javascript browser.act({ fn: "() => { const articles = document.querySelectorAll('article[data-testid=\"tweet\"]'); const posts = []; articles.forEach(article => { const url = article.querySelector('a[href=\"/status/\"]')?.href; if (!url || url.includes('/analytics') || url.includes('/photo')) return; const text = article.querySelector('[data-testid=\"tweetText\"]')?.innerText || ''; const timeEl = article.querySelector('time'); const timestamp = timeEl?.innerText || ''; const datetime = timeEl?.dateTime || ''; // Get author info const nameEl = article.querySelector('[data-testid=\"User-Name\"]'); const name = nameEl?.querySelector('span')?.innerText || ''; const handle = nameEl?.querySelector('a[href=\"/\"]')?.innerText || ''; // Get engagement metrics const metricButtons = article.querySelectorAll('button[role=\"button\"]'); let replies = '0', reposts = '0', likes = '0', views = '0'; metricButtons.forEach(btn => { const txt = btn.innerText || ''; const label = btn.getAttribute('aria-label') || ''; if (label && label.includes('Reply')) { const match = txt.match(/(\d+)/); if (match) replies = match[1]; } if (label && label.includes('Repost')) { const match = txt.match(/(\d+)/); if (match) reposts = match[1]; } if (label && label.includes('Like')) { const match = txt.match(/(\d+)/); if (match) likes = match[1]; } }); // Views are in an link (not button), e.g. 71K const viewLink = article.querySelector('a[href=\"/analytics\"]'); if (viewLink) { const v = viewLink.innerText.trim(); views = v.replace(/[^0-9KMB.]/g, '') || '0'; } // Get media URLs - images and video thumbnails const mediaUrls = []; // Image posts: pbs.twimg.com/media/ const images = article.querySelectorAll('img[src=\"pbs.twimg.com/media/\"]'); images.forEach(img => { if (img.src && !mediaUrls.includes(img.src)) { mediaUrls.push(img.src); } }); // Also check for image links in tags const imageLinks = article.querySelectorAll('a[href=\"pbs.twimg.com/media/\"]'); imageLinks.forEach(link => { if (link.href && !mediaUrls.includes(link.href)) { mediaUrls.push(link.href); } }); // Video thumbnails: pbs.twimg.com/amplify_video_thumb/ const videoThumbs = article.querySelectorAll('img[src=\"pbs.twimg.com/amplify_video_thumb/\"]'); videoThumbs.forEach(img => { if (img.src && !mediaUrls.includes(img.src)) { mediaUrls.push(img.src); } }); const postId = url.match(/\/status\/(\d+)/)?.[1] || ''; posts.push({ name, handle, text: text.substring(0, 500), timestamp, datetime, url, postId, mediaUrls, metrics: { replies, reposts, likes, views } }); }); return posts.slice(0, 10); }", kind: "evaluate" }) ` ### 步骤 5:下载媒体文件 针对每条含媒体的帖子,下载文件: `bash # 下载图片 - 将 URL 替换为实际提取到的媒体地址 curl -L -o "../../intel/x/media/{folder}/image_{postId}_{index}.jpg" "https://pbs.twimg.com/media/xxx?format=jpg&name=large" # 下载视频缩略图 curl -L -o "../../intel/x/media/{folder}/video_{postId}_poster.jpg" "https://pbs.twimg.com/amplify_video_thumb/xxx" ` 重要: - 使用 -L 参数跟随重定向 - 图片 URL 追加 &name=large 获取高分辨率 - 使用步骤 4 提取的真实地址 ### 步骤 6:写入情报文件 将 Markdown 文件写入 ../../intel/x/{date}-{time}-X.md 严格遵循上方所示输出格式。包含: - 头部:Scraped、Source、Posts Collected - 每条帖子:Author、Posted、URL、Content、Signal - 如有媒体则引用 - 末尾 Summary 部分 ### 步骤 7:关闭 Chrome 标签 `javascript browser.close() ` --- ## 关键选择器 | 元素 | 选择器 | |---------|----------| | 文章容器 | article[data-testid="tweet"] | | 帖子正文 | [data-testid=\"tweetText\"] | | 时间戳 | time | | 用户名 | [data-testid=\"User-Name\"] | | 帖子链接 | a[href=\"/status/\"] | | 浏览量链接 | a[href=\"/analytics\"] | | 互动按钮 | button[role=\"button\"] | | 图片 | img[src=\"pbs.twimg.com/media/\"] | | 视频缩略图 | img[src=\"pbs.twimg.com/amplify_video_thumb/\"] | --- ## 技巧 1. 不要点击进入单条帖子 —— 在时间线直接提取链接 2. 过滤链接 —— 排除 /analytics 与 /photo` 地址 3. 先滚动再提取 —— X 动态加载帖子 4. 先建文件夹 —— 下载前媒体文件夹必须存在 5. curl 用 -L 参数 —— 跟随 pbs.twimg.com 的重定向 6. 追加 &name=large —— 获取原图而非缩略图 7. 务必关闭标签页 —— 任务结束时关闭标签