mcpField
B
browser-screenshot

browser-screenshot

github.com/zc277584121生产力04.1k 次安装9 周前更新
代码仓库

关于 browser-screenshot

browser-screenshot 是用于 Agent Browser 的技能,可截取网页指定区域,而非整页内容。它覆盖页面调研、区域定位和裁剪截图,并使用专用持久化 Chrome 配置文件。

主要功能

  1. 01调研并验证候选网页
  2. 02使用专用 Chrome 配置文件导航
  3. 03通过 DOM 选择器定位区域
  4. 04截取裁剪后的屏幕图像
  5. 05避免输出全屏截图

适用场景

  1. 01截取特定 Reddit 帖子或推文
  2. 02为文章配图,截取段落或图表
  3. 03选择并截取相关网页区域

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zc277584121/marketing-skills --skill browser-screenshot

安装后在 Claude Code 中输入/browser-screenshot确认已加载。

详细介绍

技能:浏览器截图

截取网页中特定区域的清晰截图,例如 Reddit 帖子、推文、文章章节、图表等,而不只是输出整页截图。

前提条件:必须已安装 agent-browser。此技能使用专用的有头 Chrome 和持久化配置文件;用户日常使用的 Chrome 无需启用远程调试。

专用浏览器配置文件

使用与 my-chrome-automation 相同的专用持久化配置文件约定:

PROFILE_DIR="${AGENT_BROWSER_PROFILE:-$HOME/.agent-browser-chrome}"
NAMESPACE="${NAMESPACE:-browser-screenshot}"
SESSION="${SESSION:-focused-capture}"

ab() {
  agent-browser --namespace "$NAMESPACE" --session "$SESSION" --headed --profile "$PROFILE_DIR" "$@"
}

所有浏览器命令都必须通过 ab 运行。不要使用 --auto-connect、通用 CDP 发现功能,也不要使用用户日常使用的 Chrome 配置文件。专用配置文件可在不同任务间保留 Cookie 和登录状态,同时让用户的常用 Chrome 保持开启且不受影响。

条件允许时,为任务选择一个有描述性的 SESSION。不要同时使用同一配置文件执行多个任务;应按顺序重复使用,以避免配置文件锁定冲突。

如果目标网站需要登录,请使用 ab 打开网站,暂停操作,让用户在专用有头窗口中完成登录或验证,然后使用相同的配置文件和会话继续操作。

如果专用配置文件已在运行

使用相同配置文件再次启动 Chrome,可能会在创建 DevTools 端点前退出。绝不要改用碰巧处于打开状态的 Chrome。可以重复使用已知的、使用此配置文件的 Agent Browser 会话,或者仅明确连接到命令行中包含确切 --user-data-dir=$PROFILE_DIR 值的 Chrome 进程。

在 macOS 上,可以这样识别该专用进程及其本地 CDP 端口:

PROFILE_PID="$(ps -axo pid=,command= | awk -v profile="--user-data-dir=$PROFILE_DIR" 'index($0, profile) && index($0, "--remote-debugging-port=") && $0 !~ /Helper/ {print $1; exit}')"
CDP_PORT="$(lsof -nP -a -p "$PROFILE_PID" -iTCP -sTCP:LISTEN | awk 'NR > 1 && $9 ~ /^127\.0\.0\.1:[0-9]+$/ {split($9, parts, ":"); print parts[2]; exit}')"

ab() {
  agent-browser --namespace "$NAMESPACE" --session "$SESSION" --cdp "$CDP_PORT" "$@"
}

连接前,必须确认 PROFILE_PID 和 CDP_PORT 均非空且无歧义。如果无法验证,请让用户关闭专用自动化窗口后重试。不要关闭仅供此任务连接使用的浏览器。


概述

此技能涵盖完整流程:

  1. 调研适合截图的网页(网页搜索、抓取)
  2. 导航至浏览器中的正确网页
  3. 定位页面上的目标元素或区域
  4. 截取仅包含该区域的清晰裁剪截图
硬性规则:禁止全屏截图

最终结果绝不能输出未经裁剪的全视口或整页截图。 全屏截图包含太多干扰信息(导航栏、侧边栏、广告、无关内容),不适合作为文章插图。每张截图都必须裁剪至重点区域。


步骤 0:调研——打开浏览器前查找并验证来源

浏览器用于截图,而非浏览网页。 在 Chrome 中打开任何内容之前,先使用基于文本的工具(WebSearch、WebFetch)查找候选网页、阅读其内容,并判断哪些网页确实值得截图。

先调研的工作流程
  1. 使用 WebSearch 查找与主题相关的候选网页
  2. 使用 WebFetch 阅读每个候选网页的文本内容——确认其中是否包含所需信息或视觉素材
  3. 评估:这个网页值得截图吗?它是否有清晰、集中的区域可用作插图?
  4. 只有完成以上步骤后,才打开浏览器截取截图

这样可以节省大量时间——大多数候选网页都不值得截图,因此可以避免浏览器导航的开销,直接将其排除。

何时应优先使用浏览器

遇到以下情况时,跳过 WebSearch/WebFetch 阶段,直接使用 Chrome 浏览:

  • 目标平台需要登录 — Reddit、LinkedIn、X/Twitter 等社交平台通常会将内容设为登录后才能查看。直接使用专用配置文件,以便复用其中保存的登录状态。
  • 用户指定的平台有明确的搜索需求 — 例如,“找一篇关于 X 的 Reddit 帖子”或“截取一条关于 Y 的推文”。直接在 Chrome 中搜索该平台。
  • WebFetch 返回的内容被屏蔽或不完整 — 有些网站会积极屏蔽非浏览器请求。如果遇到 403、CAPTCHA 页面或内容被剥离,请切换到 Chrome。

在这些情况下,使用 Chrome 浏览替代 WebSearch — 前往平台的搜索页面、浏览结果,并先通过视觉方式评估页面,再决定截取什么内容。

页面选择策略

合适的页面取决于文章的上下文,以及主题的新近程度和重要性:

主题类型最适合查找的页面查找方式
新模型/功能发布(< 6 个月)宣布该内容的官方博客文章WebSearch "<model name>" site:<vendor-domain> blog
成熟产品(> 6 个月)产品落地页或文档概览WebSearch "<model name>" official page
开源模型HuggingFace 模型卡或 GitHub 仓库直接访问:huggingface.co/<org>/<model>
API 服务API 文档页面WebSearch "<service name>" API docs

注意:此表列出了常见的主题类型,但并非详尽无遗。对任何主题类型都应采用同样的研究优先策略 — 为当前主题找到最权威、视觉效果最清晰的来源页面。

优质截图来源的特点

核心原则:少即是多。聚焦内容,而非页面框架。

优质截图来源应包含一段重点明确、内容完整的信息 — 一段文字、一句关键引述、一张数据表或一幅图表。页面不应充斥按钮、导航、侧边栏和交互元素。

  • 优先选择:标题明确且包含 1-2 段文字的博客文章章节。单张图表或示意图。包含名称和描述的模型卡页眉。引述或关键发现。
  • 避免选择:带有行动号召按钮(CTA)和导航栏的完整落地页。包含多个面板的仪表盘视图。页面主要由 UI 控件(按钮、下拉菜单、表单)构成,而非可读内容。
  • 官方博客文章是理想选择:它们通常有主视觉图片、醒目的标题和简洁的描述,适合分享。
  • 产品落地页也可以,但只能截取主视觉区域 — 忽略其余部分。
  • HuggingFace 模型卡是查找开源模型的可靠来源:布局统一,模型名称和描述始终位于顶部。
  • API 文档可作为备选:展示产品名称和关键规格。

经验法则:如果计划截取的区域中,交互式 UI 元素(按钮、链接、导航项)多于可读文本内容,那么这个裁剪区域就不理想。请寻找内容更丰富的区域,或改选其他页面。

打开前验证 URL

在浏览器中打开 URL 之前,使用 WebFetch(轻量级 HEAD/GET)进行验证,避免浪费时间处理 404 或重定向:

WebFetch: <candidate-url>
→ Check status code, title, and content snippet
→ If 404 or redirect to unrelated page, try next candidate
区域选择策略

思考一下文章读者需要在这张截图中看到什么:

文章上下文要截取的内容目标区域
介绍阵容中的某个模型模型名称 + 关键标语/描述博客主视觉区域或 HF 模型卡片标题
比较功能功能亮点或规格表展示规格/功能的博客部分
讨论某项特定功能功能说明相关章节标题 + 1–2 段文字
展示产品/服务品牌标识 + 价值主张落地页主视觉区域(标题 + 副标题 + 视觉元素)

截图应该让读者觉得“哦,原来这就是这个模型/产品”,而不是“我现在看到的是什么?”


步骤 1:导航到目标页面

始终先列出标签页
ab tab list

检查专用会话中是否已打开该页面。如果已有标签页的登录状态和页面状态正确,请复用这些标签页。

按输入类型选择导航方式
用户提供的内容策略
直接 URLab open <url>
搜索查询ab open https://www.google.com/search?q=<encoded-query> → 找到并点击最佳结果
平台 + 主题构造平台搜索 URL(见下文)→ 找到目标内容
模糊描述Google 搜索 → 评估结果 → 导航到最佳匹配项
平台专用搜索 URL
平台搜索 URL 格式
Reddithttps://www.reddit.com/search/?q=<query>
X / Twitterhttps://x.com/search?q=<query>
LinkedInhttps://www.linkedin.com/search/results/content/?keywords=<query>
Hacker Newshttps://hn.algolia.com/?q=<query>
GitHubhttps://github.com/search?q=<query>
YouTubehttps://www.youtube.com/results?search_query=<query>
等待页面加载

导航后,等待内容稳定下来:

ab wait --load networkidle

注意:某些网站(Reddit、X、LinkedIn)永远不会达到 networkidle 状态。如果 open 的输出中已显示页面标题,则跳过等待。也可以使用 wait 2000 作为稳妥的替代方案。


步骤 2:定位目标区域

这是关键步骤。目标是找到一个能够精确包裹要截取内容的 CSS 选择器。

首选方法:发现 DOM 选择器
  1. 截取带注释的截图,了解页面布局:

    ab screenshot --annotate
    
  2. 获取快照,查看页面的无障碍树:

    ab snapshot -i
    
  3. 识别目标容器元素。查找:

    • 语义化 HTML 容器:<article>、<main>、<section>
    • 平台专用组件(见平台选择器)
    • 数据属性:[data-testid="..."]、[data-id="..."]
  4. 使用 get box 验证,确认元素的边界框大小合理:

    ab get box "<selector>"
    

    这会返回 { x, y, width, height }。请进行合理性检查:

    • 宽度应 > 100px 且 < 视口宽度
    • 高度应 > 50px
    • 如果边界框覆盖整个页面,说明选择器范围过宽——请进一步精确定位
  5. 如果难以找到选择器,使用 eval 探查 DOM:

    ab eval "document.querySelector('article')?.getBoundingClientRect()"
    
平台选择器

常见平台的容器选择器:

平台目标常用选择器
Reddit帖子shreddit-post、[data-testid="post-container"]
X / Twitter推文article[data-testid="tweet"]
LinkedIn信息流帖子.feed-shared-update-v2
Hacker News故事 + 评论#hnmain .fatitem
GitHub仓库卡片[data-hpc]、.repository-content
YouTube视频播放器区域#player-container-outer
通用文章主要内容article、main、[role="main"]、.post-content、.article-body

这些选择器可能会随时间变化。使用前请务必通过 get box 验证。

多个匹配元素

如果选择器匹配多个元素(例如,时间线上有多条推文),请进一步缩小范围:

# Count matches
ab get count "article[data-testid='tweet']"

# Use nth-child or :first-of-type, or a more specific selector
# Or use eval to find the right one by text content:
ab eval --stdin <<'EOF'
const posts = document.querySelectorAll('article[data-testid="tweet"]');
for (let i = 0; i < posts.length; i++) {
  const text = posts[i].textContent.substring(0, 80);
  console.log(i, text);
}
EOF

然后使用 :nth-of-type(N) 或唯一的父级选择器定位特定元素。


第 3 步:截取目标区域的屏幕截图

方法 A:滚动 + 视口截图(适用于视口大小的目标)

适用于目标元素能完整显示在视口中的情况。

# Scroll the target into view
ab scrollintoview "<selector>"
ab wait 500

# Take viewport screenshot
ab screenshot /tmp/browser-screenshot-raw.png

然后使用边界框裁剪(参见裁剪)。

方法 B:整页截图 + 裁剪(适用于任意大小的目标)

适用于目标可能大于视口,或需要精确裁剪的情况。

# Take full-page screenshot
ab screenshot --full /tmp/browser-screenshot-full.png

# Get the target element's bounding box
ab get box "<selector>"
# Output: { x: 200, y: 450, width: 680, height: 520 }

然后进行裁剪(参见裁剪)。

裁剪

使用 ImageMagick(IMv7 中为 magick,convert 已弃用)将屏幕截图裁剪到目标区域。添加留白,让画面看起来更舒展。

Retina 显示屏处理

至关重要:在 macOS Retina 显示屏上,屏幕截图会以 2 倍分辨率捕获。1728x940 的视口会生成 3456x1880 的图像。你必须考虑这一点:

  1. 检测缩放系数:比较视口大小与实际图像尺寸:

    # Check actual image dimensions
    magick identify /tmp/screenshot.png
    # → 3456x1880 means 2x scale on a 1728x940 viewport
    
  2. 裁剪前,将 get box 坐标乘以缩放系数:

    # get box returns viewport coordinates: { x: 200, y: 450, width: 680, height: 520 }
    # For 2x Retina, actual image coordinates are:
    SCALE=2
    X=$((200 * SCALE))
    Y=$((450 * SCALE))
    W=$((680 * SCALE))
    H=$((520 * SCALE))
    PADDING=$((16 * SCALE))
    
裁剪命令
magick /tmp/browser-screenshot-full.png \
  -crop $((W + PADDING*2))x$((H + PADDING*2))+$((X - PADDING))+$((Y - PADDING)) \
  +repage \
  <output-path>.png

重要:get box 返回浮点值。传给 ImageMagick 前,先将其四舍五入为整数。

留白:使用 12–20px(视口 px)。如果目标有明显的视觉边界(卡片、有边框的框),可增加到约 30px。如果用户希望紧密裁剪,则使用 0。

输出路径
  • 如果用户指定了输出路径,就使用该路径
  • 否则,将文件保存到当前目录,并使用描述性名称,例如 reddit-post-screenshot.png、tweet-screenshot.png

第 4 步:验证结果

裁剪后,读取输出图像,确认截取到了正确的内容:

# Use the Read tool to visually inspect the cropped screenshot

如果裁剪有误(遗漏内容、留白过多、元素不正确),请调整选择器或边界框,然后重试。


后备方案:视觉高亮确认

当无法确定基于 DOM 的定位是否准确时——选择器可能有误、存在多个候选项,或目标不明确——请使用 JS 注入高亮,在裁剪前进行目视确认。

工作原理
  1. 为候选元素注入高亮边框:

    ab eval --stdin <<'EOF'
    (function() {
      const el = document.querySelector('<selector>');
      if (!el) { console.log('NOT_FOUND'); return; }
      el.style.outline = '4px solid red';
      el.style.outlineOffset = '2px';
      el.scrollIntoView({ block: 'center' });
    })();
    EOF
    
  2. 截取屏幕截图并目视检查:

    ab screenshot /tmp/highlight-check.png
    

    读取屏幕截图,确认红色边框是否框住了正确的内容。

  3. 如果正确,移除高亮并继续裁剪:

    ab eval "document.querySelector('<selector>').style.outline = ''; document.querySelector('<selector>').style.outlineOffset = '';"
    
  4. 如果不正确,尝试下一个候选项或优化选择器,重新高亮并再次检查。

何时使用此后备方案
  • 页面包含复杂或嵌套的组件,你不确定哪个容器才是正确的
  • 存在多个相似元素,需要选出正确的一个
  • 用户的描述比较模糊(“页面中间的那个图表”)
  • get box 的结果看起来可疑(过大、过小或尺寸为零)

页面准备:捕获前清理页面

拍摄最终屏幕截图前,先清理页面以获得更好的效果:

# Dismiss cookie banners, popups, overlays
ab eval --stdin <<'EOF'
(function() {
  // Common cookie/popup selectors
  const selectors = [
    '[class*="cookie"] button',
    '[class*="consent"] button',
    '[class*="banner"] [class*="close"]',
    '[class*="modal"] [class*="close"]',
    '[class*="popup"] [class*="close"]',
    '[aria-label="Close"]',
    '[data-testid="close"]'
  ];
  selectors.forEach(sel => {
    document.querySelectorAll(sel).forEach(el => {
      if (el.offsetParent !== null) el.click();
    });
  });

  // Hide fixed/sticky elements that overlay content (nav bars, banners)
  document.querySelectorAll('*').forEach(el => {
    const style = getComputedStyle(el);
    if ((style.position === 'fixed' || style.position === 'sticky') && el.tagName !== 'HTML' && el.tagName !== 'BODY') {
      el.style.display = 'none';
    }
  });
})();
EOF

谨慎使用:隐藏固定元素可能会移除重要上下文。仅在覆盖层明显遮挡目标区域时才运行此操作。

无法关闭的 Cookie 横幅

一些 Cookie 同意横幅(例如 Jina AI 的 Usercentrics)位于 shadow DOM 或 iframe 中,无法通过 JS click() 或 remove() 关闭。不要浪费时间反复尝试 JS。请改用以下方法:

  1. 将其裁剪掉——如果横幅位于顶部或底部,只需调整裁剪区域将其排除。这是最快、最可靠的方法。
  2. 滚动避开——在捕获前滚动页面,让目标内容离开横幅所在区域。

视口尺寸

为确保屏幕截图一致且质量良好,请在捕获前设置视口:

# Standard desktop viewport
ab set viewport 1280 800

# Wider for dashboard/data-heavy pages
ab set viewport 1440 900

# Narrower for mobile-like content (social media posts)
ab set viewport 800 600

选择合适的视口宽度,让目标内容清晰呈现——既不要太挤,也不要拉伸过度。


故障排除

get box 返回 null 或尺寸为零
  • 选择器未匹配到任何元素。使用 get count "<selector>" 进行验证。
  • 元素可能处于隐藏状态或尚未渲染。尝试执行 wait 2000,然后重试。
裁剪后的图像为空白或区域不正确
  • 整页截图的坐标可能与视口坐标不同。将 screenshot --full 与 get box 配合使用(它们使用相同的坐标系)。
  • 检查页面是否有水平滚动——get box 的 x 值可能会偏移。
目标元素位于 iframe 内
  • get box 和 snapshot -i 无法查看 iframe 内部。
  • 使用 eval 访问 iframe 内容:
    ab eval "document.querySelector('iframe').contentDocument.querySelector('<sel>').getBoundingClientRect()"
    
    注意:仅适用于同源 iframe。
open 执行成功,但页面内容不正确
  • 浏览器可能已切换到其他标签页(例如,弹出窗口或重定向打开了新标签页)。导航后请务必验证:
    ab eval "document.location.href"
    
  • 如果 URL 不正确,使用 tab list 查找正确的标签页,并使用 tab goto <N> 切换过去。
截图命令因字体加载而超时
  • 某些页面(例如 Google 开发者文档)会卡在 document.fonts.ready。先强制解除等待:
    ab eval "document.fonts.ready.then(() => 'ok')"
    
    然后重试截图。
页面包含延迟加载的内容
  • 截图前向下滚动以触发加载:
    ab scroll down 1000
    ab wait 1500
    ab scroll up 1000
    

评论

支持 Markdown