我用 Playwright 做了个能读网页、总结内容的浏览器助手(附源码)
AI浏览器 实战案例分享|附源码
关键词:AI浏览器、智能体、网页自动化、Playwright、LLM、RAG、网页摘要、信息提取、自动化办公
随着大语言模型能力的不断增强,“AI浏览器”正在成为一个非常实用的落地场景。它并不是简单地把 ChatGPT 嵌入浏览器侧边栏,而是让 AI 具备“看网页、理解网页、操作网页、总结信息、完成任务”的能力。
本文将通过一个完整的实战案例,分享如何从零实现一个简易版 AI 浏览器助手:它可以打开指定网页,读取网页内容,提取关键信息,生成摘要,并根据用户指令进行简单的网页自动化操作。文末附上核心源码,方便你直接改造为自己的项目。
一、什么是 AI 浏览器?
传统浏览器主要解决“人访问网页”的问题,而 AI 浏览器试图解决的是:
让 AI 像人一样使用浏览器,并帮助人更高效地完成信息处理与网页操作。
一个典型的 AI 浏览器通常具备以下能力:
-
网页理解能力
能够读取网页中的标题、正文、链接、表格、按钮等信息。 -
内容总结能力
可以对长网页、新闻、博客、论文、产品文档进行摘要。 -
信息提取能力
例如从网页中提取价格、邮箱、公司名称、产品参数、招聘信息等。 -
网页操作能力
能自动点击按钮、填写表单、搜索关键词、翻页等。 -
任务规划能力
用户输入“帮我找出这个页面里最重要的三条信息”,AI 可以规划步骤并执行。 -
上下文记忆能力
可以记住当前网页、用户历史问题、已访问页面内容。
本文的目标不是实现一个完整商业级 AI 浏览器,而是实现一个具备核心能力的 MVP:
打开网页 → 获取内容 → 调用大模型分析 → 返回结果 → 支持简单操作。
二、实战案例说明
我们要实现的案例是:
输入一个网址和一个问题,AI 浏览器自动打开网页,读取页面内容,并回答用户问题。
例如用户输入:
网址:https://example.com/article
问题:请总结这篇文章的核心观点,并列出三个重点。
程序会自动完成以下步骤:
- 使用 Playwright 打开网页;
- 等待页面加载完成;
- 提取网页标题、正文文本、链接等信息;
- 将网页内容和用户问题发送给大语言模型;
- 生成结构化回答;
- 返回给用户。
进一步扩展后,它还可以支持类似指令:
请找到页面中所有下载链接。
请提取页面中的产品价格和规格参数。
请帮我判断这个网页是否包含招聘信息。
请总结这个页面,并生成一段适合发朋友圈的文案。
三、技术选型
本案例采用以下技术栈:
| 技术 | 作用 |
|---|---|
| Node.js | 后端运行环境 |
| Playwright | 控制浏览器,访问和解析网页 |
| OpenAI API / 兼容大模型 API | 负责内容理解与生成 |
| Express | 提供简单 HTTP API |
| dotenv | 管理环境变量 |
为什么选择 Playwright?
因为 Playwright 能够真正启动浏览器环境,适合处理现代网页。很多网页内容是通过 JavaScript 动态加载的,普通 HTTP 请求可能拿不到完整内容,而 Playwright 可以像真实用户一样等待页面渲染完成后再获取 DOM 内容。
四、项目目录结构
建议的项目结构如下:
ai-browser-demo
├── package.json
├── .env
├── src
│ ├── index.js
│ ├── browser.js
│ ├── llm.js
│ └── prompt.js
└── README.md
各文件职责如下:
| 文件 | 说明 |
|---|---|
index.js |
启动 Express 服务,提供接口 |
browser.js |
使用 Playwright 打开网页并提取内容 |
llm.js |
封装大模型调用 |
prompt.js |
管理提示词模板 |
.env |
存放 API Key 和模型配置 |
五、安装依赖
首先创建项目:
mkdir ai-browser-demo
cd ai-browser-demo
npm init -y
安装依赖:
npm install express playwright dotenv openai
安装浏览器:
npx playwright install
如果你部署在 Linux 服务器,可能还需要安装系统依赖:
npx playwright install-deps
六、环境变量配置
在项目根目录创建 .env 文件:
PORT=3000
OPENAI_API_KEY=你的_API_KEY
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini
如果你使用的是兼容 OpenAI 格式的国内或私有模型服务,只需要修改 OPENAI_BASE_URL 和 OPENAI_MODEL 即可。
例如:
OPENAI_BASE_URL=https://your-llm-provider.com/v1
OPENAI_MODEL=your-model-name
七、核心源码
下面是完整核心代码。
1. 浏览器模块:src/browser.js
这个模块负责打开网页并提取页面内容。
const { chromium } = require("playwright");
/**
* 清洗网页文本,去除多余空白
*/
function cleanText(text) {
return text
.replace(/\s+/g, " ")
.replace(/\n+/g, "\n")
.trim();
}
/**
* 截断文本,避免超过模型上下文长度
*/
function truncateText(text, maxLength = 12000) {
if (!text) return "";
if (text.length <= maxLength) return text;
return text.slice(0, maxLength) + "\n\n[内容过长,已截断]";
}
/**
* 提取网页基础信息
*/
async function extractPageInfo(url) {
const browser = await chromium.launch({
headless: true
});
const page = await browser.newPage({
viewport: {
width: 1280,
height: 900
}
});
try {
await page.goto(url, {
waitUntil: "networkidle",
timeout: 45000
});
const title = await page.title();
const description = await page
.locator('meta[name="description"]')
.getAttribute("content")
.catch(() => "");
const bodyText = await page.locator("body").innerText({
timeout: 10000
});
const links = await page.$$eval("a", (anchors) => {
return anchors
.map((a) => ({
text: a.innerText ? a.innerText.trim() : "",
href: a.href
}))
.filter((item) => item.href)
.slice(0, 80);
});
const headings = await page.$$eval("h1,h2,h3", (nodes) => {
return nodes
.map((node) => ({
tag: node.tagName.toLowerCase(),
text: node.innerText ? node.innerText.trim() : ""
}))
.filter((item) => item.text)
.slice(0, 50);
});
return {
url,
title,
description: description || "",
headings,
links,
text: truncateText(cleanText(bodyText))
};
} finally {
await browser.close();
}
}
module.exports = {
extractPageInfo
};
这里有几个关键点:
- 使用
chromium.launch启动无头浏览器; - 使用
networkidle等待页面网络请求基本结束; - 提取
title、description、正文文本、链接和标题结构; - 对正文进行清洗和截断,避免超过模型上下文长度;
- 最后一定要关闭浏览器,避免资源泄露。
2. 提示词模块:src/prompt.js
这个模块负责构造发送给大模型的 Prompt。
function buildWebQuestionPrompt(pageInfo, question) {
return `
你是一个专业的 AI 浏览器助手,擅长阅读网页、分析网页内容并回答用户问题。
请根据下面提供的网页信息回答用户问题。
要求:
1. 只基于网页内容回答,不要编造不存在的信息;
2. 如果网页内容不足以回答,请明确说明“页面中没有足够信息”;
3. 回答要结构清晰,尽量使用分点说明;
4. 如果用户要求总结,请输出核心观点、关键细节和结论;
5. 如果用户要求提取信息,请使用表格或列表形式。
网页信息如下:
【网址】
${pageInfo.url}
【网页标题】
${pageInfo.title}
【网页描述】
${pageInfo.description || "无"}
【页面标题结构】
${JSON.stringify(pageInfo.headings, null, 2)}
【页面链接】
${JSON.stringify(pageInfo.links, null, 2)}
【网页正文】
${pageInfo.text}
用户问题:
${question}
`;
}
module.exports = {
buildWebQuestionPrompt
};
Prompt 是 AI 浏览器效果的关键。
如果只把网页正文直接扔给模型,结果可能不稳定;加入标题、描述、链接和结构信息,可以让模型更准确地理解网页。
3. 大模型调用模块:src/llm.js
这里使用 OpenAI SDK,也兼容大多数 OpenAI API 格式的模型服务。
const OpenAI = require("openai");
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL
});
async function askLLM(prompt) {
const response = await client.chat.completions.create({
model: process.env.OPENAI_MODEL || "gpt-4o-mini",
messages: [
{
role: "system",
content:
"你是一个严谨、可靠的 AI 浏览器助手,擅长分析网页内容。"
},
{
role: "user",
content: prompt
}
],
temperature: 0.2
});
return response.choices[0].message.content;
}
module.exports = {
askLLM
};
这里建议将 temperature 设置低一些,例如 0.2,这样模型回答会更稳定,也更适合信息提取和网页分析类任务。
4. 服务入口:src/index.js
这个文件负责启动 HTTP 服务,并提供一个 /ask 接口。
require("dotenv").config();
const express = require("express");
const { extractPageInfo } = require("./browser");
const { buildWebQuestionPrompt } = require("./prompt");
const { askLLM } = require("./llm");
const app = express();
app.use(express.json({
limit: "2mb"
}));
app.get("/", (req, res) => {
res.send("AI Browser Demo is running.");
});
app.post("/ask", async (req, res) => {
const { url, question } = req.body;
if (!url || !question) {
return res.status(400).json({
error: "请提供 url 和 question"
});
}
try {
const pageInfo = await extractPageInfo(url);
const prompt = buildWebQuestionPrompt(pageInfo, question);
const answer = await askLLM(prompt);
res.json({
url,
question,
answer,
page: {
title: pageInfo.title,
description: pageInfo.description,
headings: pageInfo.headings
}
});
} catch (error) {
console.error(error);
res.status(500).json({
error: "处理失败",
message: error.message
});
}
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`AI Browser Demo started at http://localhost:${port}`);
});
启动服务:
node src/index.js
测试接口:
curl -X POST http://localhost:3000/ask \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"question": "请总结这个网页的主要内容"
}'
返回示例:
{
"url": "https://example.com",
"question": "请总结这个网页的主要内容",
"answer": "该网页主要用于示例展示,说明 Example Domain 可用于文档示例中引用...",
"page": {
"title": "Example Domain",
"description": "",
"headings": [
{
"tag": "h1",
"text": "Example Domain"
}
]
}
}
八、增加“网页操作”能力
上面的版本主要是“阅读网页”。如果要让 AI 浏览器更进一步,就需要让它可以执行动作,例如:
- 点击按钮;
- 输入关键词;
- 提交表单;
- 截图;
- 提取搜索结果;
- 多页面跳转。
下面增加一个简单案例:自动打开搜索页面,输入关键词并提取搜索结果标题。
创建 src/search.js:
const { chromium } = require("playwright");
async function searchBing(keyword) {
const browser = await chromium.launch({
headless: true
});
const page = await browser.newPage();
try {
await page.goto("https://www.bing.com", {
waitUntil: "domcontentloaded"
});
await page.fill("input[name='q']", keyword);
await page.keyboard.press("Enter");
await page.waitForLoadState("networkidle");
const results = await page.$$eval("li.b_algo", (items) => {
return items.slice(0, 5).map((item) => {
const titleEl = item.querySelector("h2");
const linkEl = item.querySelector("h2 a");
const descEl = item.querySelector(".b_caption p");
return {
title: titleEl ? titleEl.innerText.trim() : "",
url: linkEl ? linkEl.href : "",
description: descEl ? descEl.innerText.trim() : ""
};
});
});
return results.filter((item) => item.title && item.url);
} finally {
await browser.close();
}
}
module.exports = {
searchBing
};
然后在 index.js 中增加接口:
const { searchBing } = require("./search");
app.post("/search", async (req, res) => {
const { keyword } = req.body;
if (!keyword) {
return res.status(400).json({
error: "请提供 keyword"
});
}
try {
const results = await searchBing(keyword);
res.json({
keyword,
results
});
} catch (error) {
res.status(500).json({
error: "搜索失败",
message: error.message
});
}
});
测试:
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{
"keyword": "AI 浏览器 应用场景"
}'
这样,我们就从“AI 阅读网页”扩展到了“AI 操作网页”。
九、进一步升级:让 AI 自动决定操作步骤
真正的 AI 浏览器并不是写死每一个操作,而是让模型根据页面状态和用户目标决定下一步动作。常见做法是给模型提供一组可用工具,例如:
[
{
"name": "open_url",
"description": "打开指定网址"
},
{
"name": "click",
"description": "点击页面中的某个元素"
},
{
"name": "type",
"description": "向输入框输入文本"
},
{
"name": "extract_text",
"description": "提取当前页面文本"
},
{
"name": "screenshot",
"description": "获取当前页面截图"
}
]
模型收到用户目标后,输出类似:
{
"thought": "用户想搜索 AI 浏览器应用场景,需要先打开搜索引擎,然后输入关键词。",
"action": "open_url",
"params": {
"url": "https://www.bing.com"
}
}
程序执行后把结果再返回给模型,模型继续规划下一步。
这种模式就是常说的 Agent + Browser Tool。
不过在实际项目中,不建议一开始就做全自动 Agent。因为全自动网页操作存在很多不确定性,例如:
- 页面结构复杂;
- 按钮文字可能重复;
- 弹窗、验证码、登录态会影响流程;
- 模型可能产生错误操作;
- 自动操作成本和延迟较高。
更稳妥的方式是先实现一些高频、可控的工具,例如:
- 网页摘要;
- 链接提取;
- 表格提取;
- 搜索结果聚合;
- 页面截图;
- 表单自动填写;
- 指定网站的信息采集。
等这些能力稳定后,再逐步引入任务规划。
十、实战中的几个关键问题
1. 网页内容太长怎么办?
很多网页正文非常长,直接塞给模型会超出上下文限制,也会增加成本。常见解决方案包括:
- 截断正文;
- 按段落切分;
- 使用 Embedding 做检索;
- 先生成局部摘要,再合并摘要;
- 只提取主要内容区域。
本文示例使用的是简单截断:
truncateText(cleanText(bodyText), 12000)
如果要做得更专业,可以使用分块摘要:
网页正文 → 切分为多个 chunk → 分别摘要 → 合并摘要 → 回答问题
2. 如何避免模型胡说?
提示词中一定要明确要求:
只基于网页内容回答,不要编造。
如果页面信息不足,请明确说明页面中没有足够信息。
同时,可以要求模型引用网页中的关键原文或标题:
回答时请引用网页中的相关段落或标题作为依据。
3. 如何提升网页提取质量?
简单使用 body.innerText 虽然方便,但可能包含导航栏、页脚、广告等无关内容。可以进一步优化:
- 优先提取
article标签; - 移除
nav、footer、script、style; - 根据文本密度判断正文区域;
- 使用 Readability 算法;
- 针对特定网站写适配器。
例如可以优先提取文章区域:
const articleText = await page
.locator("article")
.innerText()
.catch(() => "");
如果 articleText 存在,就优先使用它。
4. 如何处理登录网站?
有些网站需要登录才能访问。可以使用 Playwright 保存登录态:
await context.storageState({
path: "auth.json"
});
下次启动时加载:
const context = await browser.newContext({
storageState: "auth.json"
});
这样就可以访问需要登录的页面,例如内部系统、管理后台、知识库等。
5. 如何保证安全?
AI 浏览器具备操作网页的能力,因此必须注意安全边界:
- 不要让 AI 自动执行支付、删除、提交审批等高风险操作;
- 对敏感动作增加人工确认;
- 限制可访问域名;
- 记录操作日志;
- 避免泄露 Cookie、Token、内部网页内容;
- 对用户输入 URL 做校验,防止 SSRF 风险。
十一、可落地的应用场景
AI 浏览器不是概念项目,它非常适合落地在以下场景:
1. 内容运营
运营人员每天需要阅读大量行业资讯、竞品文章、热点内容。AI 浏览器可以自动总结文章观点,提取标题、金句、传播角度,并生成二次创作文案。
2. 销售线索挖掘
输入公司官网,AI 浏览器可以提取公司介绍、业务范围、联系方式、产品信息,并生成客户画像。
3. 招聘信息整理
自动访问招聘网站或公司招聘页,提取岗位名称、薪资、地点、技能要求,并整理成表格。
4. 电商价格监控
定期访问商品页面,提取价格、库存、促销信息,并在价格变化时通知用户。
5. 企业内部知识库助手
对于 Confluence、飞书文档、Notion、语雀等知识库,AI 浏览器可以帮助员工快速阅读和总结页面内容。
6. 表单自动填写
对于重复性较强的后台录入工作,可以通过浏览器自动填写表单,减少人工操作。
十二、项目优化方向
如果你准备把这个 Demo 扩展成一个完整产品,可以从以下方向继续优化:
-
加入前端页面
做一个输入 URL 和问题的界面,展示 AI 回答和网页结构。 -
支持流式输出
使用 SSE 或 WebSocket,让答案逐字返回,体验更好。 -
增加网页截图能力
有些页面视觉信息比文本更重要,可以截图后交给多模态模型分析。 -
引入向量检索
对长网页或多个网页进行切片、向量化,支持跨页面问答。 -
实现工具调用机制
让模型可以调用open_url、click、extract等工具。 -
增加任务队列
对批量网页分析任务,可以使用 BullMQ、RabbitMQ 等队列系统。 -
增加权限控制
不同用户只能访问自己的任务和网页数据。 -
支持浏览器会话复用
避免每次请求都启动浏览器,提高性能。
十三、完整运行流程回顾
整个 AI 浏览器 Demo 的流程如下:
用户提交 URL 和问题
↓
Express 接口接收请求
↓
Playwright 打开网页
↓
提取标题、正文、链接、标题结构
↓
构造 Prompt
↓
调用大语言模型
↓
生成回答
↓
返回 JSON 结果
这个流程虽然简单,但已经具备 AI 浏览器的核心雏形。它不是单纯聊天,而是让 AI 获得了访问真实网页信息的能力。
十四、总结
AI 浏览器的核心价值在于:
把大语言模型的理解能力,与浏览器的网页访问和操作能力结合起来。
本文实现的 Demo 包含了几个关键模块:
- 使用 Playwright 控制浏览器;
- 提取网页标题、正文、链接和结构;
- 使用 Prompt 让大模型理解网页;
- 提供 HTTP API 供外部调用;
- 扩展搜索和网页操作能力。
对于个人开发者来说,这个项目可以作为学习 AI Agent、网页自动化和大模型应用开发的入门案例。对于企业团队来说,它可以进一步演化为内部知识助手、自动化运营工具、销售线索系统或数据采集平台。
如果你想继续深入,可以尝试做三个增强功能:
- 支持网页截图并调用多模态模型分析;
- 支持长网页分块摘要和向量检索;
- 支持模型自动规划网页操作步骤。
当 AI 不只是回答问题,而是能真正打开网页、读取网页、理解网页并执行任务时,它就从“聊天助手”变成了“浏览器智能体”。这也是 AI 应用从内容生成走向任务执行的重要一步。