前阵子帮朋友整理一份产品文档,需要把一套几十屏长的网页完整保存下来,放到底稿里逐屏标注。我习惯性按下 Cmd+Shift+4,拉一个矩形区域,结果截到的只是当前屏幕这一块;往下滚一点,又得重新截一次,反复几次以后,拼接出来的效果惨不忍睹。macOS 自带的截图工具确实方便,但它没有"自动滚动截长图"这个功能。折腾了一圈之后我才意识到,问题不在 macOS,而在于我一直在用错工具——Chrome 本身就藏着整页截图的官方能力,而且远不止一种打开方式。这篇文章就围绕 MacOS 环境下用 Chrome 做整页截图这件事,把官方方案、命令行自动化、扩展插件、纯系统兜底全部理一遍,覆盖内容归档、竞品走查、设计评审这些常见场景,也会把我在实操中踩过的细节坑一并交代清楚。
1. 为什么Mac原生截图工具救不了长网页:先说清楚问题在哪
1.1 系统截图工具的四种方式和它们的共同短板
macOS 的截图能力其实不弱,日常用非常顺手:
- Cmd+Shift+3:截取整个屏幕
- Cmd+Shift+4:拖拽选择区域截图,按空格键可以切换成窗口截图
- Cmd+Shift+4 后按住 Shift+Option:可以调整选区大小,适合精确框选
- Cmd+Shift+5:打开带工具栏的截图/录屏面板,可以选择区域、窗口、全屏或者录屏
这套逻辑在国外社区里经常被夸"好用、干净、无打扰",但它有一个明显的边界:所有模式都只捕捉当前屏幕可见范围内的像素。你截不到屏幕之外的内容,哪怕那个网页已经渲染好了,滚动条下面的部分就是不在截图范围内。
这不是"哪个开关没开"的问题,而是设计如此。系统截图工具读取的是当前帧缓冲区(framebuffer)中的画面,屏幕上没显示的区域根本不会出现在缓冲区里。想要截到长网页,必须让浏览器或者某个工具自动滚动页面,逐段截下来再拼接,或者干脆让浏览器以完整页面的尺寸重新渲染一次再截——这两条路在 macOS 系统层面都不存在。
如果你需要把一篇二十屏长的技术文章完整存下来,最原始的做法就是滚动一次、截一次,然后回到相册里一张张拼。我在公司里见过同事用 PPT 拼长图的场景,那种接头错位、字体大小不一致的体验,做过一次就再也不想做了。
1.2 为什么浏览器反而能搞定整页截图
因为浏览器和系统截图工具的机制完全不同。Chrome 解析完 HTML 和 CSS 之后,整个页面在内存里是一棵完整的渲染树,视口只是这棵树上的一个观察窗口。也就是说,哪怕你的屏幕只有一屏高,浏览器手里也攥着整个文档的布局信息。
当 Chrome 执行整页截图时,它做的事情是:临时把视口高度扩展到等于页面总高度,重新排版一次,然后用 GPU 合成整张位图输出。这就是为什么 Chrome 能截到"屏幕之外"的内容,而系统截图工具不能。
理解这个原理很重要。后面所有方案,不管是 DevTools 的 full size screenshot、Playwright 的 fullPage 参数,还是扩展插件的滚动拼接,本质上都是在利用浏览器的渲染能力来获取页面完整布局。
另一个被很多人忽略的事实是:Safari 在 macOS Monterey 之后的系统截图工具里也提供了"整页"选项,效果是在侧栏生成一个长条缩略图,点击后可以导出为 PDF。但那个出口固定在"Safari + PDF"的组合上,灵活性远不如 Chrome。如果你主要用 Chrome 工作,直接掌握 Chrome 的整页截图手段就够了。
这一章的核心结论:别在系统截图工具里找滚动截图按钮了,它没有。正路是从浏览器内部下手。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发者工具的全尺寸截图:官方自带且不需要装任何东西
2.1 最简操作路径
Chrome 开发者工具里的全尺寸截图功能,是我在 MacOS 上最推荐的整页截图入口。不需要安装任何扩展,不需要命令行知识,只要是 Chrome 就能用。
完整操作步骤如下:
- 用 Chrome 打开目标网页,等待内容加载完成。
- 按下 Cmd+Option+I(⌥⌘I)打开开发者工具。
- 按下 Cmd+Shift+P(⇧⌘P)打开命令菜单(Run Command)。
- 在命令输入框里输入
screenshot。 - 在弹出的候选列表里选择
Capture full size screenshot(捕获完整尺寸截图),回车。
Chrome 会在几秒内完成渲染和捕获,然后自动把一张完整的 PNG 图片保存到下载目录,文件名通常是网页标题加上一串随机字符。
这个命令面板是 Chrome 非常实用的一个隐藏入口。除了整页截图,它还支持截取当前视口(Capture screenshot)和截取选中的页面节点(Capture node screenshot)。第 5 步的候选项里通常同时出现这三个命令,如果你只想截当前画面的某一屏,选第一项;如果你在开发者工具的 Elements 面板里选中了一个具体的 DOM 节点,选第三项可以直接把这个元素截成图,这在做前端改动前后对比时特别有用。
2.2 它在底层做了什么
这个功能对应的 Chrome DevTools Protocol(CDP)命令是 Page.captureScreenshot,调用时带上了 captureBeyondViewport 参数。这个参数的作用就是允许浏览器捕获超出当前视口范围的内容。
浏览器拿到指令后的处理方式是:把整个页面当成一块超大画布,按照页面实际宽度和文档总高度设置捕获区域,然后调用渲染管线输出位图。对于常规页面,这个过程在一两秒内就能完成,不需要真的去滚动屏幕。
这也是为什么你会看到截图结果里没有滚动条——Chrome 在捕获时已经隐藏了滚动条并把视口扩展到了整页宽度。
2.3 实操中容易忽略的三个细节
第一,如果你打开了开发者工具的设备模拟模式(Device Toolbar),整页截图会按照模拟设备的视口来截,而不是页面实际布局。很多人在写移动端页面调试时点了整页截图,发现宽度只有 375px,就是这个原因。如果你要的是桌面版整页截图,先按 Cmd+Shift+M 退出设备模拟,再执行截图命令。
第二,页面非常长时,Chrome 的 CDP 截图会有高度上限。根据我自己的测试,超过一万六千像素左右的页面,截图结果可能会出现底部空白或者整张失败。不是每个版本的 Chrome 都报同样的错,但长页面确实容易触发这类问题。遇到这种情况,我的做法是优先用打印为 PDF 的方案(见第 5 章),或者改用脚本分段截取。
第三,懒加载内容必须先触发加载。很多网站使用 IntersectionObserver 或者 loading="lazy" 来延迟加载图片。如果你刚打开页面就执行整页截图,页面下半部分的图片可能还没加载出来,截出来的图下半段就是灰的。正确做法是先快速滚到页面底部,等图片加载完成后滚回顶部,再执行截图。
这个官方方案的最大优点是无依赖、无隐私顾虑、永久可用。Chrome 更新不会砍掉这个功能,因为它同时服务于前端开发者的日常调试。我强烈建议所有 MacOS 用户先把这个操作记下来。
3. 命令行与脚本:把整页截图变成一条命令的事
3.1 基础命令行截图能做到什么程度
Chrome 从 59 版本开始支持 headless 模式,也就是无界面运行。你可以在终端里直接调用 Chrome 的二进制文件来截取网页,不需要打开浏览器窗口。
MacOS 上 Chrome 的路径是固定的:
bash复制/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
一个最简单的截图命令是这样:
bash复制/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--headless \
--disable-gpu \
--screenshot=/Users/你的用户名/Desktop/screenshot.png \
--window-size=1440,900 \
https://example.com
这个命令会启动一个隐藏的 Chrome 实例,以 1440x900 的视口访问 example.com,然后截取当前视口内容,输出到桌面上的 screenshot.png。
注意:这个基础版命令截的是"视口范围",不是整页。很多教程直接拿这个命令说"Chrome 命令行整页截图",其实是误传。headless 模式的 --screenshot 参数本身只截首屏,除非配合 --window-size 把窗口高度设置成大于页面总高度,才能勉强把整页装进去。
例如,把高度设成一个足够大的数:
bash复制--window-size=1440,20000
如果页面真实高度是 8000px,20000px 的视口高度通常能把整页都渲染出来。缺点也很明显:你没法预先知道页面总高度,设置过大又会在底部留出一大片空白。对于固定模板的页面或许能试出一个稳定值,但拿到其他网站上就不灵了。
3.2 用 Playwright 实现真正可控的整页截图
真正可控的方案是用 Playwright 或 Puppeteer 这类自动化库去调用浏览器的 CDP 接口,直接指定 full_page=True。这样浏览器会先算出页面真实总高度,再一次性输出完整截图。
推荐给有一定 Python 基础的读者,这是我目前在用的方案:
python复制import asyncio
from playwright.async_api import async_playwright
async def capture_full_page(url: str, output_path: str, width: int = 1440):
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(
viewport={"width": width, "height": 800},
device_scale_factor=2 # Retina 屏幕下输出更清晰
)
await page.goto(url, wait_until="networkidle")
# 触发懒加载:先滚到底,再回顶部
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await page.wait_for_timeout(1000)
await page.evaluate("window.scrollTo(0, 0)")
await page.wait_for_timeout(500)
# 整页截图
await page.screenshot(path=output_path, full_page=True)
await browser.close()
asyncio.run(capture_full_page("https://example.com", "fullpage.png"))
这段代码里有两个关键点:
device_scale_factor=2可以让输出图片保持 Retina 级别清晰度,如果你在 2x 屏幕上使用 Chrome 开发者工具截图,但发现图片发虚,用这个参数可以解决。- 滚动到底再回顶的步骤,是为了让懒加载图片先加载完。等
networkidle只能保证网络层空闲,不能保证懒加载触发,手动滚动是最稳妥的触发方式。
批量截取一组网页时,把 url 列表换成一个 for 循环就行。我之前用这个脚本给一个数据看板项目做过全页面巡检,一次跑了五十多个页面,输出结果非常稳定。
3.3 什么时候值得走脚本路线
命令行和脚本方案最大的价值在于可重复性和可扩展性。适合这几类场景:
- 批量为多个页面存档,比如每天定时截取某个网页作为快照
- 需要在 CI/CD 流程里做视觉效果回归检查
- 截图只是某个自动化流程中的一个环节,比如配合 OCR 识别页面内容
- 需要固定视口宽度、固定输出尺寸、统一文件命名规则
代价是要维护一套环境。对于非开发者读者,我的建议很直接:如果只是偶尔截几张图,用第 2 章的 DevTools 方案就够了,完全没必要为了截图去装 Python 环境。脚本方案是给"需要批量处理"的人准备的。
4. 扩展插件:日常用起来最顺手的图形化方案
4.1 三款常见扩展的实际体验对比
如果你接受不了 DevTools 的命令面板操作,也不想碰终端,又需要频繁截图,那扩展插件就是最顺手的图形化路线。
我实际用过的三款主流扩展如下:
| 扩展 | 核心特点 | 适合场景 | 注意事项 |
|---|---|---|---|
| FireShot | 整页截图、区域截图、编辑标注、导出 PDF/PNG | 需要标注和编辑截图 | 免费版够用,部分高级导出功能收费 |
| GoFullPage | 一键整页截图,自动处理固定元素 | 只想快速获得干净长图 | 编辑功能少,但胜在简单 |
| Awesome Screenshot | 截图 + 录屏 + 标注 + 模糊敏感信息 | 多功能需求 | 功能较重,权限请求较多 |
GoFullPage 在 MacOS 上表现非常稳定,点击扩展图标后它会自动滚动页面、逐段捕获、拼接成一张长图,然后在一个预览页面里展示。你可以在预览里直接下载 PNG,或者导出为 PDF。它的一个优势是会自动处理固定定位元素的重复问题,不会出现同一根导航栏在长图里重复出现好几次的情况。
FireShot 的标签能力更强。它的截图完成后会打开一个标注编辑器,支持画框、加箭头、涂马赛克。这对做竞品走查和交互评审非常实用,截图完直接在编辑器里标注交互问题,比先截图再导入其他工具省一步。
4.2 安装和手动加载 CRX 的注意事项
正常情况下,直接在 Chrome 网上应用店搜索扩展名,点击安装即可。但在国内网络环境下,访问 Chrome 网上应用店偶尔会失败,或者下载中断,很多人会拿去别的地方下载 CRX 文件手动安装。
手动安装的路径是:
- 打开
chrome://extensions/ - 打开右上角的"开发者模式"
- 把 CRX 文件直接拖进扩展管理页面
- 看到确认提示后点击"添加扩展程序"
这里我要提醒一件事:Chrome 对非官方渠道下载的文件有安全校验。如果 Chrome 提示"由于网站未使用安全连接,且文件可能已被篡改,因此 Chrome 阻止了此次下载",那说明文件的来源不可信。我遇到过同事从第三方下载站拿到的 CRX,安装后浏览器主页被改、不断弹出广告。所以手动加载 CRX 时务必确认文件来源,最好只在官方商店下载。
4.3 扩展的局限:它和 DevTools 方案的本质区别
扩展的整页截图大多采用"滚动拼接"策略:页面往下滚一段,截一段,再滚动,再截,最后把若干张截图拼成一张长图。这种机制和 DevTools 的"一次性扩展视口渲染"不同,影响有两个:
第一,动态内容容易出问题。页面上的悬浮框、Tooltip、下拉菜单这类交互元素,在分段截图时往往只出现在某一段里,甚至完全丢失。如果你的页面有 hover 才有内容的模块,用扩展截图大概率会缺失。
第二,固定元素的处理依赖插件算法。GoFullPage 会自动把固定定位的元素只在首屏保留,其他段删除,这个算法大多数时候靠谱,但遇到复杂的嵌套定位布局偶尔也会漏。DevTools 的 full size screenshot 因为是一次性渲染,固定元素只会出现在首屏位置,不存在重复问题。
所以我的选择建议是:只是要一张完整长图,优先用 DevTools;需要在图上做标注、框选,再考虑 FireShot;想要最省心的一键体验,GoFullPage 可以装一个备用。
5. 走系统打印通道:不装扩展也能拿到完整页面的兜底方案
5.1 用"存储为 PDF"绕过截图限制
Chrome 的"打印"对话框在 MacOS 上可以输出 PDF,这个功能对整页截图来说是一个很实用的兜底方案。它不需要安装任何扩展,不需要打开开发者工具,也不需要终端命令,操作方法就是日常的打印动作。
具体步骤:
- 在 Chrome 中打开目标网页。
- 按下 Cmd+P 打开打印对话框。
- 在打印机下拉菜单里选择"存储为 PDF"。
- 根据需求调整纸张大小和边距。默认 A4 横向时页面宽度受打印排版影响,如果你希望输出宽度接近网页真实布局,可以把纸张设为自定义尺寸,或者选择横向。
- 勾选"背景图形"选项。这一步非常关键,不勾选的话,网页的背景色、渐变、纹理都会丢失,截出来的 PDF 一片白底。该选项在打印对话框的"更多设置"里可以找到。
- 点击"存储",生成 PDF 文件。
这一步做完,你会得到一个完整的、包含整页内容的 PDF 文件。浏览器在打印时会自动把整个文档分页排版,不会出现"只打印当前屏幕内容"的问题,因为打印输出的参考就是整个文档。
5.2 把 PDF 转成 PNG 长图的后续操作
如果最终交付物必须是 PNG 图片,可以在预览(Preview)应用里打开刚才的 PDF,然后通过"文件"菜单的"导出"功能选择 PNG 格式。需要注意的是,预览应用导出 PDF 时默认按页导出,如果你的网页内容跨了三页 PDF,导出的 PNG 会是三张单页图,而不是一张长图。
想要把多页 PDF 拼成一张长图,macOS 自带的工具不太方便。我常用的办法是用 pdftoppm 命令,它属于 poppler 工具集,安装方法是:
bash复制brew install poppler
转换命令:
bash复制pdftoppm -png -r 144 网页存档.pdf page
这会按 144 DPI 的分辨率把 PDF 的每一页导出为一张 PNG,文件名类似 page-01.png、page-02.png。拿到这些单页图之后,再用 Preview 把多张图拖进同一个窗口,或者用第三方拼接工具合并。
如果你不想安装 poppler,也可以使用 macOS 自带的 Automator 或者快捷指令(Shortcuts)来做 PDF 到图片的转换,选择"提取页面"或者"将 PDF 页面转换为图像"的操作即可。但自动化方式配置起来反而比命令行麻烦,我一般直接用 pdftoppm。
5.3 这个方案的优缺点
打印为 PDF 的方案适合两种场景:一是需要把网页内容作为正式文档交付,PDF 本身就是可接受的格式;二是遇到超长页面,Chrome 整页截图直接失败,这时"打印为 PDF"往往能顺利输出几十页的内容,比硬拼一张超大 PNG 更实用。
缺点是它生成的不是一张连续的长图,而是分页的 PDF。如果你需要的只是"一条长截图",这个方案要多做一步拼接。另外,页面上所有依赖 JavaScript 动态生成的内容,如果没在打印样式里设置为可见,也可能输出不了。好在这类问题在 Chrome 中不太常见,大多数现代网页的打印输出结果都能接受。
6. 那些年我踩过的截图坑:从懒加载到超长页面
6.1 懒加载内容截出来的图是残缺的
这个问题在整页截图中出现频率最高。现在主流网站几乎都用懒加载,图片和组件只有在接近视口时才会请求加载。如果你在页面刚打开时就执行整页截图,下半部分没有触发加载的内容直接就空白了。
我一开始用 DevTools 的 full size screenshot 踩过这个坑,截出来的长图只有首屏附近的内容是完整的,往下全都是灰色占位区域。排查思路其实不复杂:先快速滚动到底部,让浏览器把该加载的资源都拉下来,等一两秒,再滚回顶部执行截图。扩展工具里通常会有一个"自动滚动以加载内容"的选项,GoFullPage 默认会这样做。
如果页面里的数据是异步请求来的,比如滚动到某个位置才发起接口请求加载下一个板块,那还得等接口返回后内容真正渲染出来,再执行截图。判断标准很简单:你手动滚动时看到的内容,和截图结果是否一致。
6.2 固定导航栏在长图里不断重复
使用滚动拼接型工具截长图时,position: fixed 或 position: sticky 定位的元素会在每一段画面里都出现。常见表现是长图里每隔一屏高度就出现一次同样的导航栏、回到顶部按钮或者客服浮窗。
最直接的解决办法是用临时脚本把固定元素隐藏掉。在 DevTools 的 Console 面板里执行:
js复制const elements = document.querySelectorAll('header, nav, .fixed-top, .sticky');
elements.forEach(el => {
el.style.display = 'none';
});
这段代码会把常见的固定定位元素隐藏起来,然后你再执行整页截图。注意这只是一次性修改,刷新页面后就会恢复。
GoFullPage 这类扩展内部会做固定元素检测,自动保留第一个出现的导航栏,后续段中将重复元素删除。在实际使用中,它对普通网站的导航栏处理效果不错,但遇到自定义嵌套表格这类复杂布局时还是有概率出错。所以如果截图里有重复的导航栏,不用怀疑是电脑问题,先试隐藏固定元素这条路。
6.3 Canvas 图表和 WebGL 内容截出来是空白的
页面里使用 ECharts、Chart.js 这类 Canvas 渲染的图表时,整页截图经常出现图表区域空白的情况。原因在于 Canvas 绘制的内容是画在一层独立的位图上的,滚动拼接或者扩展视口重绘时,这层位图可能没有被正确捕获,输出结果里图表区域就是透明或白色的。
WebGL 场景更麻烦一点,GPU 合成链路里有些内容默认就不走页面捕获路径。
做法和前面类似:先确认图表已经完成初始化和动画,再执行截图。如果问题依旧,可以尝试用 Chrome headless 模式加 --disable-gpu 参数,强制走软件渲染,部分 WebGL 内容在软件渲染下反而能正确输出。如果你只需要长图里的那一小块图表,直接区域截图补上会比折腾整页截图省时间。
6.4 超长页面截图失败或 PNG 体积过大
当一个页面高度超过浏览器能够处理的上限时,用 DevTools 的整页截图可能生成一个损坏的文件,或者干脆卡住不动。我遇到过的实际表现是:命令执行后没有任何报错,但保存下来的 PNG 用预览打开后下半部分全黑。
给这个场景的常规解法是先转换思路,不再追求一张超大 PNG:
- 优先用打印为 PDF 的方案,让 PDF 分页承受长度,浏览器对 PDF 分页的长度处理比单张位图宽容很多
- 如果必须用 PNG,把页面按滚动深度拆成几段,每段用视口截图,然后用自动化脚本把多张图竖向拼接
- 如果是长图内容里包含大量图片元素,实际生成的 PNG 可能达到几十上百 MB,用 ImageOptim 或 TinyPNG 压缩后再交付
在决定截长图之前,先问自己一句:这个网页内容真的需要一张 PNG 长图,还是 PDF 文档反而更好?我在技术文档归档时已经逐渐转向 PDF,遇到需要用截图沟通的场合才用 PNG。
6.5 Retina 屏幕下截图看起来发虚
MacBook Pro 外接显示器或者新款 Mac 默认开启高分屏(Retina),但 Chrome 的整页截图默认按照 CSS 像素输出,不会自动把 DPR(Device Pixel Ratio)乘进去。你截出来的图片在电脑上看着正常,放到大屏或者放大查看时边缘就会发虚。
解决方式和前面 Playwright 参数一致:使用 2 倍或 3 倍的 device scale factor 重新截取。DevTools 里的做法是打开设备模拟,在 Devices 设置里把 Device Pixel Ratio 改为 2,再执行整页截图;用 Playwright 则是直接设置 device_scale_factor=2。
需要注意,开启 2 倍 DPR 后,截图宽度会翻倍,比如 CSS 宽度为 1440px 的页面会输出 2880px 宽的图片,文件体积也跟着变大。如果你的目标只是发到工作群里快速查看,1 倍分辨率反而更合适。我在交付正式文档时用 2 倍,平时沟通时默认 1 倍,鱼和熊掌就不纠结了。
6.6 无限滚动页面的截图困境
像社交信息流、时间线这类无限滚动的页面,页面高度会随着滚动不断增大。DevTools 的整页截图执行时,浏览器只截取当前已渲染的 DOM 部分,后面的内容还没生成,自然截不到。
这类页面如果没有"滚动到底部自动加载全部"的机制,整页截图基本不可用。我的处理方式是写一个脚本,不断执行滚动、等待、再滚动,直到触发触底判断,然后再执行整页截图。Playwright 代码里可以这样:
python复制previous_height = 0
while True:
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await page.wait_for_timeout(1000)
current_height = await page.evaluate("document.body.scrollHeight")
if current_height == previous_height:
break
previous_height = current_height
循环结束后再执行 page.screenshot(full_page=True),就能拿到包含所有动态加载内容的完整截图。这个场景在自动化测试里会遇到,偶尔截图用这个逻辑完全够用。
7. 我的选型建议和一点个人偏好
如果你读完前面的内容觉得有点多,我帮你做一个快速决策:
偶尔截一次,直接用 DevTools 的 full size screenshot,零安装、零依赖、官方功能,最稳。每周都要截好几次,装一个 GoFullPage,点击图标出长图,省去命令面板的操作成本。截图之后需要标注和框选,用 FireShot。交付正式文档,优先打印为 PDF,PDF 比超大 PNG 更适合归档。要批量处理多个页面,用 Playwright 脚本,自动化是唯一可持续的路。
我个人的习惯是:平时用 GoFullPage 居多,因为它操作成本最低;遇到特别重要的长网页,我会用 DevTools 的官方方案交叉验证一遍,毕竟扩展偶尔会处理错固定元素。要是碰到超过一万像素的超长页面,直接放弃 PNG,走"存储为 PDF"的通道,反而省心。整页截图这件事看似简单,实操起来细节不少,但掌握这几条路之后,几乎不会再遇到"截不下来"的情况。
