浏览器书签工具一键导出AI对话为PDF/文本,支持ChatGPT/Claude/Gemini
1. 项目概述一个浏览器书签搞定所有主流AI对话导出作为一名长期和各类AI助手打交道的博主我深知一个痛点和ChatGPT、Claude、Gemini这些工具聊了半天产出了一堆有价值的代码、方案或者灵感最后想整理归档或者分享给同事时却异常麻烦。要么得一段段手动复制粘贴格式全乱要么想保存成PDF却发现网页自带的“打印”功能会把侧边栏、导航栏这些无关内容全打进去还得手动调整CSS非常不优雅。今天分享的这个叫“give-me/bookmarklets”的小工具完美解决了这个问题。它本质上是一个浏览器书签工具我习惯叫它“一键导出小助手”。你只需要把这个工具保存为浏览器书签之后在任何支持的AI对话页面目前完美支持ChatGPT、Claude、Gemini和Grok点一下这个书签就能把整段对话连同可能存在的代码块、文件预览等“附加内容”一键导出为干净的PDF或格式清晰的纯文本文件。整个过程完全在本地浏览器中运行不经过任何第三方服务器你的对话数据百分百不会泄露。它特别适合需要频繁整理AI对话内容的朋友比如程序员存档技术讨论、学生保存学习记录、内容创作者收集灵感素材。接下来我会详细拆解它的工作原理、手把手教你如何部署使用并分享一些我深度使用后总结的独家技巧和避坑指南。2. 核心原理与设计思路拆解这个工具虽然用起来简单但背后的设计思路非常巧妙充分考虑了不同场景下的用户体验和安全性。它不是一个大而全的浏览器插件而是一个轻量级的“书签小程序”这个选择本身就很有讲究。2.1 为何选择Bookmarklet而非浏览器插件首先我们得明白Bookmarklet是什么。它是一段以javascript:开头的代码保存在浏览器书签栏里。点击时这段代码会在当前页面的上下文中执行。相比于浏览器插件它有三大优势极致的轻量与便捷无需安装不占用浏览器后台资源没有复杂的权限申请。就是一个书签点一下就用。绝对的安全与隐私所有代码逻辑和数据处理都发生在你本地浏览器的内存中执行完毕即消失。它不会像插件一样常驻后台也无法访问你浏览器标签页之外的数据从根本上杜绝了数据被上传到开发者服务器的风险。无平台依赖与更新灵活不受Chrome应用商店或Firefox插件中心的审核限制。一旦AI对话页面的HTML结构发生变化导致工具失效开发者可以快速更新GitHub上的代码用户只需替换书签中的URL即可更新响应速度远快于插件审核上架。当然缺点也有比如功能复杂度受限于单次执行的代码量且无法进行复杂的后台交互。但对于“导出页面内容”这个单一、明确的需求Bookmarklet是近乎完美的解决方案。2.2 双引擎PDF导出策略的精妙之处这个工具最核心的智慧体现在PDF导出上它提供了“可搜索PDF”和“不可搜索PDF”两种模式其底层采用了两种完全不同的技术方案以适配不同AI平台的技术限制。方案一利用浏览器原生打印功能生成“可搜索PDF”这是默认且兼容性最好的方案。它的原理是工具通过CSS选择器精准定位到网页中的对话主区域和可能的附加内容区域如代码预览窗。在内存中创建一个临时的div容器并将找到的所有内容克隆一份放入其中。动态插入一段仅针对打印生效的CSS样式其核心规则是在打印时隐藏页面上所有其他元素只显示我们创建的那个临时容器。调用浏览器的window.print()方法。此时浏览器弹出的打印预览窗口里就只有我们想要的纯净对话内容了。用户选择“另存为PDF”即可得到一个文本可被选中、搜索的PDF文件。之后工具会自动清理掉临时创建的样式和容器。这个方案的优点是生成的PDF质量高、文字可搜索、完全依赖浏览器自身能力无需加载外部资源。但它的缺点是生成的PDF样式字体、布局受限于浏览器打印引擎和用户打印设置。方案二引入html2pdf.js库生成“不可搜索PDF”这个方案主要作为备选用于应对一些平台的安全策略。它的流程是当用户选择“生成不可搜索PDF”时工具会从Cloudflare的公共CDN动态加载一个叫html2pdf.js的第三方开源库。该库会在浏览器内将我们找到的HTML内容先渲染到Canvas画布上再将Canvas转换为PDF文件。由于本质上是将文字变成了图片所以生成的PDF内的文字无法被直接选中和搜索。这个方案看似是退而求其次实则解决了关键问题内容安全策略。像ChatGPT这样的网站可能会设置严格的CSP禁止页面执行eval或new Function等这有时会干扰方案一的打印流程。而方案二通过加载外部库绕开了这些限制保证了功能的可用性。开发者通过一个csp标志位来智能判断当前网站是否需要启用此方案非常贴心。2.3 内容定位机制CSS选择器的艺术工具能否准确抓取内容完全依赖于其对目标网站HTML结构的理解即那一串串的CSS选择器。例如对于Claude它用div[data-test-render-count]来找到对话容器用div[data-testiduser-message]来定位用户消息。这要求开发者必须持续跟进这些AI产品的界面更新。注意这是此类工具最脆弱的环节。一旦ChatGPT或Claude的前端工程师改了某个div的class名或>javascript:(function () { /* v. 0.12, github.com/give-me/bookmarklets */ let dialog, events [], extras [], csp false; switch (location.hostname) { case claude.ai: dialog document.querySelector(div[data-test-render-count]).parentElement; events dialog.querySelectorAll(div[data-testiduser-message], div[data-test-render-count]divdivdiv.font-claude-response); extras.push(document.querySelector(div.h-full.top-0 div.font-mono)); extras.push(document.querySelector(div.h-full.top-0 div#wiggle-file-content)); extras.push(document.querySelector(div.h-full.top-0 div#markdown-artifact)); break; case chatgpt.com: dialog document.querySelector(article).parentElement; events dialog.querySelectorAll(div[data-message-author-role]); extras.push(document.querySelector(section.popoversection)); csp true; break; case grok.com: dialog document.querySelector(div#last-reply-container).parentElement; events dialog.querySelectorAll(div.message-bubble); extras.push(document.querySelector(aside)); csp true; break; case gemini.google.com: dialog document.querySelector(#chat-history); events dialog.querySelectorAll(user-query-content, message-content); extras.push(document.querySelector(code-immersive-paneldiv.container)); extras.push(document.querySelector(deep-research-immersive-paneldiv.container)); extras.push(document.querySelector(extended-response-panel response-container)); csp true; break; default: return alert(location.hostname is not supported); } events [...events].filter(Boolean); extras [...extras].filter(Boolean); console.group(Found elements at ${location.hostname}:); console.debug(dialog, dialog); console.debug(events, events); console.debug(extras, extras); console.groupEnd(); let blocks [dialog, ...extras]; let ts new Date().toISOString().replace(/[-:T.]/g, ).slice(0, 14); if (confirm(Confirm if you prefer to export PDF instead of text)) { if (csp || confirm(Confirm if the PDF should be searchable)) { let temp document.createElement(div); temp.id id- Math.random().toString(36).slice(2, 9); blocks.forEach(el temp.appendChild(el.cloneNode(true))); let style document.createElement(style); style.textContent media print{body*{display:none!important}#${temp.id}{display:flex!important;flex-direction:column}}; document.head.appendChild(style); document.body.appendChild(temp); print(); setTimeout(() { document.head.removeChild(style); document.body.removeChild(temp); }, 1000); } else { let script document.createElement(script); script.src https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.12.1/html2pdf.bundle.min.js; script.onload function () { let pdf html2pdf().set({ margin: 5, filename: ${ts}.pdf, html2canvas: {scale: 2, logging: false} }).from(blocks.shift()); blocks.forEach(el pdf pdf.toPdf().get(pdf).then(pdfObj pdfObj.addPage()).from(el).toContainer().toCanvas().toPdf()); pdf.save(); }; document.body.appendChild(script); } } else { let txt events.map((e, i) # ${i % 2 ? AI : Me}:\n\n${e.innerText.trim()}\n\n).join(); txt extras.map((e, i) # Extra ${i 1}:\n\n${e.innerText.trim()}\n\n).join(); let href URL.createObjectURL(new Blob([\uFEFF, txt], {type: text/plain;charsetutf-8})); let link Object.assign(document.createElement(a), {href: href, download: ${ts}.txt}); link.click(); URL.revokeObjectURL(link.href); } })();保存点击“保存”按钮。此时你的书签栏里应该出现了一个以你刚才命名的新书签。实操心得建议将这个书签保存在书签栏的显眼位置或者将其添加到浏览器工具栏如果浏览器支持。因为代码较长在书签管理器里直接编辑容易出错如果未来需要更新更推荐的做法是删除旧书签然后重新创建。3.2 在不同AI平台上的使用流程书签创建好后使用就非常简单了基本是“哪里需要点哪里”。打开目标对话首先在浏览器中打开你想要导出的AI对话页面。确保页面已经完全加载完毕对话历史滚动到了你需要的位置。点击书签点击你刚刚创建的那个书签。跟随提示操作首先会弹出一个对话框询问“Confirm if you prefer to export PDF instead of text”。意思是“确认是否要导出PDF而非文本”。点击“确定”则进入PDF导出流程点击“取消”则直接导出为文本文件。如果选择导出文本工具会立即生成一个.txt文件并触发下载。文件内容会清晰地用# Me:和# AI:来区分对话角色附加内容也会单独标注格式非常整洁。如果选择导出PDF会弹出第二个对话框询问“Confirm if the PDF should be searchable”。意思是“确认PDF是否需要可搜索”。点击“确定”工具会尝试使用方案一浏览器打印生成可搜索的PDF。此时会立即调起浏览器的打印预览窗口。你需要在这个打印窗口的“目标打印机”处选择“另存为PDF”然后点击保存。这是最关键的一步很多新手会在这里愣住以为出错了。点击“取消”工具会使用方案二html2pdf.js生成不可搜索的PDF。这个过程可能需要几秒钟来加载库和生成文件完成后会自动下载。在不同平台上的细微差别Claude.ai: 体验最流畅通常直接使用可搜索PDF方案且能很好地捕获侧边栏的代码文件等内容。ChatGPT.com: 由于CSP限制通常会触发不可搜索PDF方案。如果你在点击书签后没有立即弹出打印窗口而是浏览器看起来“卡”了一下稍等片刻就会开始下载PDF这是正常现象。Gemini.google.com: 行为与ChatGPT类似。Grok.com: 根据其页面安全策略也可能触发不可搜索PDF方案。4. 高级技巧与深度定制解析如果你不满足于基本使用想更深入地掌控这个工具或者解决一些特定问题下面这些技巧会很有帮助。4.1 理解并验证内容抓取工具执行时会在浏览器的开发者控制台输出调试信息。你可以按F12打开开发者工具切换到Console标签页然后再点击书签。你会看到类似这样的日志Found elements at chatgpt.com: dialog: div.../div events: NodeList(20) [div, div, div, ...] extras: [section.popoversection]这非常有用events的数量代表了它找到了多少条对话消息。如果这里显示为0或数量远少于实际对话条数说明CSS选择器可能已经失效工具需要更新了。extras数组则显示了它找到了哪些额外的内容面板比如上传的文件预览区。4.2 自定义文件名与导出内容工具生成的PDF或文本文件默认以时间戳命名格式如20240415123045.pdf。如果你希望文件名能体现对话主题可以在浏览器打印预览窗口可搜索PDF方案中手动修改“文件名”字段再保存。对于文本导出工具会导出它所能抓取到的所有对话历史。如果你只想导出部分内容一个变通的方法是先手动将网页滚动到你想要开始导出的那条消息附近确保这些消息已经被加载到DOM中然后再点击书签。不过它无法智能地只导出“最后10条”或“选中部分”这是Bookmarklet这种轻量级形式的局限。4.3 处理导出PDF的样式问题使用“可搜索PDF”方案时最终的样式取决于你的浏览器打印设置。如果你对默认的PDF样式不满意比如字体太小、背景色被打印出来可以在打印预览窗口中进行调整布局通常选择“纵向”即可。纸张大小A4是通用选择。边距可以选择“无”以获得最大内容区域或者“最小值”。选项务必勾选“背景图形”。如果不勾选Claude、ChatGPT等深色背景下的文字可能会变成白底白字而无法显示。勾选后才能正确打印出文字颜色。缩放保持100%即可。这些设置会被浏览器记住下次调用打印时一般会沿用所以通常只需配置一次。4.4 如何手动更新失效的书签正如前面原理部分提到的当AI网站改版工具可能会失效。表现通常是点击书签后弹窗提示“xxx is not supported”或者导出的内容为空。这时你需要手动更新书签中的代码访问该工具的GitHub源码页面https://github.com/give-me/bookmarklets/blob/main/bookmarklets/export.js找到页面上最新的、完整的javascript:代码通常以javascript:(function(){...})();的形式包裹。完全复制这段新代码。回到浏览器书签管理器找到原来的书签编辑它用新代码完全替换掉“网址”字段里的旧代码。保存。这样就完成了更新。个人经验建议关注一下这个GitHub仓库甚至可以点个Star。这样当工具失效时你能第一时间想到可能是网站改版了并知道去哪里获取更新。这是使用这类开源小工具的必备素养。5. 常见问题排查与解决方案实录在实际使用中你可能会遇到一些问题。下面是我总结的一些常见情况及解决方法。5.1 点击书签没有任何反应这是最常见的问题通常原因和解决方法如下问题现象可能原因解决方案点击书签页面毫无反应无弹窗。1. 书签代码复制不完整首尾缺失或中间有换行。2. 浏览器安全策略禁止了书签栏执行大量JS代码。1.重新复制粘贴确保从javascript:开始到最后的})();结束完整且中间无换行。最好在纯文本编辑器里检查一遍。2.尝试在地址栏执行将书签代码完整复制粘贴到浏览器的地址栏中然后按回车。如果这时能运行说明书签本身有问题删除后重新创建。地址栏执行后报语法错误。代码在传输过程中可能被意外修改如邮件、聊天软件自动格式化。始终从项目的官方GitHub页面或可信的文章中直接复制代码源。仅在某些网站上无反应。该网站可能使用了严格的CSP阻止了某些内联脚本执行方式。尝试使用“不可搜索PDF”选项如果弹窗能出现的话。或者检查控制台是否有CSP报错。5.2 导出内容不完整或错乱问题现象可能原因解决方案导出的PDF/文本只包含最近几条消息。AI聊天界面是“无限滚动”加载的未滚动查看的历史消息并未被加载到DOM中。在点击书签前手动向上滚动页面直到所有你需要导出的历史对话都出现在屏幕上。确保它们被加载出来。导出的文本中“Me”和“AI”角色标记错乱。工具通过消息在列表中的奇偶索引位置来判断角色如果页面结构复杂可能有干扰元素。检查控制台输出的events数量是否正确。如果错乱严重可能是网站结构已大变需等待工具更新。临时方案是导出后手动校对。附加内容如代码文件没有被导出。该附加内容面板未被工具内置的CSS选择器捕获或者面板处于隐藏/折叠状态。在点击书签前确保相关的附加内容面板是展开可见的。例如在Claude中点击了上传的文件让预览窗显示出来。5.3 PDF相关的问题问题现象可能原因解决方案选择“可搜索PDF”后打印预览窗口内容空白。临时创建的容器样式可能未生效或被页面更高优先级的样式覆盖。1. 在打印预览窗口的“更多设置”中确认已勾选“背景图形”。2. 尝试使用“不可搜索PDF”方案。“不可搜索PDF”生成时间很长或浏览器卡死。对话历史非常长html2pdf.js在将大量HTML渲染到Canvas时消耗了大量资源。1. 耐心等待长对话可能需要数十秒。2. 考虑分批导出或先导出为文本。3. 检查控制台是否有JS错误。PDF文件很大。“不可搜索PDF”本质是图片分辨率高scale: 2会导致文件体积大。如果对文件大小敏感优先使用“可搜索PDF”方案。对于不可搜索方案可以尝试修改代码中的html2canvas: {scale: 2}将2改为1但会降低清晰度。5.4 浏览器兼容性与安全警告问题现象说明与解决方案在Safari上可能有限制。Safari对书签执行JS代码有时限制更严。确保在Safari的“偏好设置”-“高级”中勾选了“在菜单栏中显示开发菜单”然后在“开发”菜单中确保“允许JavaScript来自智能搜索字段”是启用的。浏览器提示“此网页正在尝试加载不安全的脚本”。当使用“不可搜索PDF”方案时会从Cloudflare CDN加载html2pdf.js库。浏览器可能会弹出警告。Cloudflare CDN是广泛使用的可信源可以放心点击“加载”或“允许”。这是实现本地转换功能的必要步骤代码本身不会外传你的数据。这个工具是我目前用过最优雅、最轻量的AI对话导出方案。它把复杂的功能封装成了一个简单的书签真正做到了“开箱即用用完即走”。它的存在提醒我们很多时候解决问题不需要重型的软件或插件一段精心设计的脚本就能极大提升效率。当然保持对工具更新机制的关注理解其工作原理以应对偶尔的失效是享受这种轻量化便利的同时需要承担的一点小责任。如果你也经常需要整理和保存与AI的对话强烈建议花五分钟设置一下它会成为你浏览器里一个低调但无比实用的效率神器。