使用专用的有头Chrome浏览器配置文件(持久化登录状态),从网页中截取聚焦于特定区域的截图。根据用户上下文(URL、搜索查询、社交媒体帖子)导航到正确的页面,通过DOM选择器定位目标区域,并裁剪出干净、聚焦的截图,无需附加到用户的日常浏览器。
技能:浏览器截图
截取网页上特定区域的聚焦截图——Reddit帖子、推文、文章段落、图表等——而不仅仅是全页面截图。
前提条件:必须安装
agent-browser。此技能使用其自己的有头Chrome浏览器和专用持久化配置文件;用户的日常Chrome不需要远程调试。
专用浏览器配置文件
使用与 my-chrome-automation 相同的专用持久化配置文件约定:
PROFILE_DIR="${AGENT_BROWSER_PROFILE:-$HOME/.agent-browser-recorder-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\.[0-9]+:[0-9]+$/ {split($9, parts, ":"); print parts[2]; exit}')"
ab() {
agent-browser --namespace "$NAMESPACE" --session "$SESSION" --cdp "$CDP_PORT" "$@"
}
在附加之前,要求非空且明确的 PROFILE_PID 和 CDP_PORT 值。如果无法验证,请用户关闭专用自动化窗口并重试。不要关闭此任务仅附加到的浏览器。
概述
此技能处理完整的流程:
- 研究最适合截图的页面(网络搜索、获取)
- 导航到浏览器中的正确页面
- 定位页面上的目标元素/区域
- 捕获该区域的聚焦裁剪截图
硬性规则:禁止全屏截图
切勿将未裁剪的全视口或全页面截图作为最终结果输出。 全屏截图包含太多噪音(导航栏、侧边栏、广告、无关内容),不适合作为文章插图。每个截图必须裁剪到聚焦区域。
步骤0:研究——在打开浏览器之前查找并验证来源
浏览器用于捕获,而非浏览。 在Chrome中打开任何内容之前,使用基于文本的工具(WebSearch、WebFetch)查找候选页面,阅读其内容,并决定哪些页面值得截图。
研究优先的工作流程
- WebSearch 查找主题的候选页面
- WebFetch 每个候选页面以阅读其文本内容——检查它是否包含所需的信息/视觉元素
- 评估:这个页面值得截图吗?它是否有清晰、聚焦的区域可以作为插图?
- 然后打开浏览器捕获截图
这可以节省大量时间——大多数候选页面不值得截图,你可以在没有浏览器导航开销的情况下排除它们。
何时使用浏览器优先
在以下情况下跳过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仓库 | 直接URL:huggingface.co/<org>/<model> |
| API服务 | API文档页面 | WebSearch "<service name>" API docs |
注意:此表列出了常见的主题类型,但并非详尽无遗。对任何主题类型应用相同的研究优先策略——为手头主题找到最权威且视觉上最干净的源页面。
好的截图来源的特征
核心原则:少即是多。聚焦内容,而非界面。
好的截图来源包含聚焦、自包含的信息片段——一段文本、一个关键引用、一个数据表、一个图表。它不应该是充满按钮、导航、侧边栏和交互元素的繁忙页面。
- 首选:博客文章的一个段落,带有清晰的标题和1-2段文本。单个图表或图示。模型卡标题,包含名称和描述。引用或关键发现。
- 避免:带有CTA和导航的完整登陆页面。包含多个面板的仪表板视图。由UI控件(按钮、下拉菜单、表单)主导而非可读内容的页面。
- 官方博客文章是理想的:它们有英雄图像、突出的标题和为分享设计的简洁描述。
- 产品登陆页面可以工作,但仅当裁剪到英雄部分时——忽略其余部分。
- HuggingFace模型卡对于开源模型是可靠的:布局一致,模型名称+描述始终在顶部。
- API文档是可接受的备选:显示产品名称和关键规格。
经验法则:如果你计划捕获的区域包含的交互式UI元素(按钮、链接、导航项)多于可读文本内容,则这是一个糟糕的裁剪。寻找内容更丰富的区域,或选择完全不同的页面。
预检URL验证
在浏览器中打开之前,使用WebFetch(轻量级HEAD/GET)验证URL,避免浪费时间在404或重定向上:
WebFetch: <candidate-url>
→ 检查状态码、标题和内容片段
→ 如果404或重定向到无关页面,尝试下一个候选
区域选择策略
思考文章读者需要在此截图中看到什么:
| 文章上下文 | 要捕获的内容 | 目标区域 |
|---|---|---|
| 在系列中介绍模型 | 模型名称+关键标语/描述 | 博客英雄部分或HF模型卡标题 |
| 比较能力 | 功能亮点或规格表 | 显示规格/功能的博客部分 |
| 讨论特定功能 | 功能描述 | 相关部分标题+1-2段 |
| 展示产品/服务 | 品牌标识+价值主张 | 登陆页面英雄(标题+副标题+视觉元素) |
截图应让读者想到“啊,这就是这个模型/产品”——而不是“我在看什么?”
步骤1:导航到目标页面
始终从列出标签页开始
ab tab list
检查页面是否已在专用会话中打开。当它们具有正确的登录和页面状态时,重用其现有标签页。
按输入类型导航
| 用户提供 | 策略 |
|---|---|
| 直接URL | ab open <url> |
| 搜索查询 | ab open https://www.google.com/search?q=<encoded-query> → 查找并点击最佳结果 |
| 平台+主题 | 构建平台搜索URL(见下文)→ 定位目标内容 |
| 模糊描述 | Google搜索→评估结果→导航到最佳匹配 |
特定平台的搜索URL
| 平台 | 搜索URL模式 |
|---|---|
https://www.reddit.com/search/?q=<query> |
|
| X / Twitter | https://x.com/search?q=<query> |
https://www.linkedin.com/search/results/content/?keywords=<query> |
|
| Hacker News | https://hn.algolia.com/?q=<query> |
| GitHub | https://github.com/search?q=<query> |
| YouTube | https://www.youtube.com/results?search_query=<query> |
等待页面加载
导航后,等待内容稳定:
ab wait --load networkidle
注意:某些网站(Reddit、X、LinkedIn)永远不会达到
networkidle。如果open的输出已显示页面标题,则跳过等待。使用wait 2000作为安全替代。
步骤2:定位目标区域
这是关键步骤。目标是找到一个CSS选择器,精确包裹要捕获的内容。
主要方法:DOM选择器发现
-
拍摄带注释的截图以了解页面布局:
ab screenshot --annotate -
拍摄快照以查看页面的无障碍树:
ab snapshot -i -
识别目标容器元素。查找:
- 语义HTML容器:
<article>、<main>、<section> - 特定平台的组件(见平台选择器)
- 数据属性:
[data-testid="..."]、[data-id="..."]
- 语义HTML容器:
-
使用
get box验证以确认元素具有合理的边界框:ab get box "<selector>"返回
{ x, y, width, height }。合理性检查:- 宽度应 > 100px 且 < 视口宽度
- 高度应 > 50px
- 如果框是整个页面,则选择器太宽泛——细化它
-
如果难以找到选择器,使用
eval探索DOM:ab eval "document.querySelector('article')?.getBoundingClientRect()"
平台选择器
流行平台的常见容器选择器:
| 平台 | 目标 | 典型选择器 |
|---|---|---|
| 帖子 | shreddit-post、[data-testid="post-container"] |
|
| X / Twitter | 推文 | article[data-testid="tweet"] |
| 动态帖子 | .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验证。
多个匹配元素
如果选择器匹配多个元素(例如,时间线上的多条推文),缩小范围:
# 计数匹配
ab get count "article[data-testid='tweet']"
# 使用nth-child或:first-of-type,或更具体的选择器
# 或使用eval按文本内容找到正确的元素:
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:滚动+视口截图(适用于视口大小的目标)
当目标元素适合视口时最佳。
# 将目标滚动到视图中
ab scrollintoview "<selector>"
ab wait 500
# 拍摄视口截图
ab screenshot /tmp/browser-screenshot-raw.png
然后使用边界框裁剪(见裁剪)。
方法B:全页面截图+裁剪(适用于任何大小的目标)
当目标可能大于视口或需要精确裁剪时最佳。
# 拍摄全页面截图
ab screenshot --full /tmp/browser-screenshot-full.png
# 获取目标元素的边界框
ab get box "<selector>"
# 输出:{ x: 200, y: 450, width: 680, height: 520 }
然后裁剪(见裁剪)。
裁剪
使用ImageMagick(IMv7使用 magick,convert 已弃用)将截图裁剪到目标区域。添加内边距以获得视觉呼吸空间。
Retina显示处理
关键:在macOS Retina显示屏上,截图以2倍分辨率捕获。1728x940的视口产生3456x1880的图像。你必须考虑这一点:
-
检测缩放因子:比较视口大小与实际图像尺寸:
# 检查实际图像尺寸 magick identify /tmp/screenshot.png # → 3456x1880 表示在1728x940视口上为2倍缩放 -
在裁剪前将
get box坐标乘以缩放因子:# get box 返回视口坐标:{ x: 200, y: 450, width: 680, height: 520 } # 对于2倍Retina,实际图像坐标为: 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(视口像素)。如果目标具有明显的视觉边界(卡片、带边框的框),增加到约30px。如果用户想要紧密裁剪,使用0。
输出路径
- 如果用户指定了输出路径,使用该路径
- 否则,保存到当前目录中的描述性名称,例如
reddit-post-screenshot.png、tweet-screenshot.png
步骤4:验证结果
裁剪后,读取输出图像以验证它捕获了正确的内容:
# 使用Read工具视觉检查裁剪后的截图
如果裁剪错误(遗漏内容、空白过多、元素错误),调整选择器或边界框并重试。
备选方案:视觉高亮确认
当基于DOM的定位不确定时——选择器可能错误、存在多个候选或目标模糊——使用JS注入的高亮在裁剪前进行视觉确认。
工作原理
-
在候选元素上注入高亮边框:
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 -
拍摄截图并视觉检查:
ab screenshot /tmp/highlight-check.png读取截图以检查红色边框是否包围了正确的内容。
-
如果正确,移除高亮并继续裁剪:
ab eval "document.querySelector('<selector>').style.outline = ''; document.querySelector('<selector>').style.outlineOffset = '';" -
如果错误,尝试下一个候选或细化选择器,重新高亮并重新检查。
何时使用此备选方案
- 页面具有复杂/嵌套的组件,你不确定哪个容器是正确的
- 存在多个相似元素,你需要选择正确的那个
- 用户的描述模糊(“页面中间的那个图表”)
get box结果看起来可疑(太大、太小、零大小)
页面准备:捕获前清理
在拍摄最终截图之前,清理页面以获得更好的结果:
# 关闭Cookie横幅、弹窗、覆盖层
ab eval --stdin <<'EOF'
(function() {
// 常见的Cookie/弹窗选择器
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();
});
});
// 隐藏覆盖内容的固定/粘性元素(导航栏、横幅)
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)位于影子DOM或iframe中,无法通过JS click() 或 remove() 关闭。不要浪费多次JS尝试的时间。相反:
- 裁剪掉它——如果横幅在顶部或底部,只需调整裁剪区域以排除它。这是最快且最可靠的方法。
- 滚动过去——在捕获前将目标内容滚动离开横幅区域。
视口大小设置
为了获得一致的高质量截图,在捕获前设置视口:
# 标准桌面视口
ab set viewport 1280 800
# 更宽,适用于仪表板/数据密集型页面
ab set viewport 1440 900
# 更窄,适用于类似移动端的内容(社交媒体帖子)
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内容:
注意:仅适用于同源iframe。ab eval "document.querySelector('iframe').contentDocument.querySelector('<sel>').getBoundingClientRect()"
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






