耗时一天写了个 Search GPT 浏览器插件,提高查找功率!
前语
很多人应该都运用过微软的 Chat Bing。当运用 Bing 进行查找的时分,不仅会展现检索的成果页,并且还会在浏览页面的侧边栏显示相似于 ChatGPT 的呼应。这个时分查找内容其实是作为 prompt 输入给 Chat Bing。如下图所示:
于是,我想着可不能够在百度或谷歌浏览器中也完成这样的作用。最直接的一个主意便是开发一个浏览器插件,赋予其 GPT 的才能。
除此之外,我还想添加一些额外的功用。每次查找时,咱们都会点击最匹配的成果页,并检查这些成果页是否符合预期。假如咱们把这一步交给 GPT 去做,让它帮咱们总结网页的内容,这样能够节约一些检索的时刻,提高查找功率。
好嘞,简单总结一下需求,有两点:
- 完成相似于 Chat Bing 的作用。
- 完成总结网页内容的功用。
需求明晰后,我用了大概一天时刻写了 Search GPT 浏览器插件。
接下来,我带我们看看我的完成进程!
Github 地址如下,其间包括详细的 README 文档
技能选型
为了快速开发浏览器插件,我挑选了 Plasmo 结构。首要原因是它支撑 React、Vue 等结构,且能够很便利的引入第三方组件库(如 Ant Design),调试插件的时分也很便利,支撑热布置。假如不运用结构的话,得写原生 html、js、css,很头疼哈哈哈哈~
挑选 React 作为前端开发结构,Ant Design 作为组件库。这儿就不多介绍啦,信任前端开发的同学一定非常熟悉。
为了完成这个总结页面内容这个功用,我选用了 Python + BeautifulSoup 去爬取网站内容,并由 GPT 进行总结。
Beautiful Soup 是一个能够从 HTML 或 XML 文件中提取数据的 Python 库。它能够经过你喜爱的转换器完成惯用的文档导航 / 查找 / 修改文档的办法。Beautiful Soup 会帮你节约数小时乃至数天的工作时刻。
如下为技能选型表:
技能栈 | 介绍 |
---|---|
Plasmo | 简化浏览器插件开发,支撑运用 React、Vue 等前端开发结构 |
React + Ant Design | 前端开发结构 + 前端组件库 |
Python + Flask + BeautifulSoup | 后端 Web 结构 + 爬虫 |
浏览器插件组件
为了便利接下来介绍 Search GPT 的开发进程,我先给我们介绍一下浏览器插件中根本的组件!
在 Search GPT 中,我运用到了 Content Script、Background 与 Popup
-
Content Script:其用于直接与浏览器进行交互,它运行在页面的上下文中,能够读取和修改网页的内容。
举个 🌰,假如咱们想要完成 Chat Bing 的作用,咱们需求在浏览器的右侧边栏画一个框,框中展现 ChatGPT 对查找内容的呼应。这个 UI 完成需求经过 Content Script 来完成!
-
BackGround Script:顾名思义,它是运行在插件的后台,独立于浏览器的其他网页,一般用于处理全局的状况,接收并处理 Content Script 发出的事情。
举个🌰,当 Content Script 读取到当时页面是一个查找页(www.google.com/search),它就能够读取查找内容,并以事情的形式发送给 Background。Background 便能够去恳求 GPT Api,获取 prompt 的呼应成果,并回来给 Content Script。
-
Popup:这个组件最简单了解,当咱们点击插件图标时,其会弹出一个框,可能是一个登陆框、设置框等。
代码结构
├── src
│ ├── background.ts
│ ├── backgroundFunctions
│ │ ├── handleCrawlerApi.ts
│ │ └── handleGptApi.ts
│ ├── contents
│ │ ├── BaiduSearchBox.tsx
│ │ ├── GoogleSearchBox.tsx
│ │ ├── PageCollapse.tsx
│ │ └── plasmo.tsx
│ └── popup.tsx
- background.ts:为 background 脚本文件
- backgroundFunctions:将 background.ts 中用到的函数抽取到该文件夹中
- contents:其间 plasmo 为 Content Script,剩下的为 React 组件
- popup.tsx:弹出框组件
完成计划
存储 API Key
在 Search GPT 中,Popup 用于设置 API Key 密钥,并用 Plasmo 供给的 storage 进行持久化存储。
如下图所示即为 popup 作用。
其间的中心代码便是处理 Save 与 Clear 事情。
-
保存 API Key,首先是验证 API Key 是否正确,然后进行存储,并更新提示状况。
详细怎么验证 API Key,请检查源码,原理便是调用 Open AI 供给的 api。
// src/popup.ts 第 43 行 const saveGptApiKey = async () => { if (await verifyGptApiKey(gptApiKey)) { try { await storage.set("gptApiKey", gptApiKey) setGptStatus("GPT API Key saved successfully.") } catch (error) { setGptStatus("Failed to save GPT API Key.") } } }
-
铲除 API Key,更新提示状况。
// src/popup.ts 第 54 行 const clearGptApiKey = async () => { await storage.remove("gptApiKey") setGptApiKey("") setGptStatus("Enter your GPT API Key") }
中心完成
需求 1
当用户用谷歌或百度查找时,插件会以查找内容作为 prompt,去恳求 GPT。如下为详细流程图。
-
判别页面是经过 Plasmo 结构供给的模板进行装备,只有当网站地址匹配到 google 或 baidu 才会进行之后的流程。
// src/contents/plasmo.tsx 第 8 行 export const config: PlasmoCSConfig = { matches: ["https://*.google.com/*", "https://*.baidu.com/*"] }
-
烘托 GPT 呼应框是经过
document
的办法获取到指定的方位,在该方位烘托对应的 React 组件。- 谷歌和百度对应的方位有所不同,所以需求分别处理
// src/contents/plasmo.tsx 第 30 行 const displayGptResponse = (responseText: string, hostname: string) => { let container = document.getElementById("gpt-response-container") if (container) { container.remove() } container = document.createElement("div") container.id = "gpt-response-container" if (hostname == "www.google.com") { let appbar = document.getElementById("appbar") appbar.parentNode.insertBefore(container, appbar) const root = createRoot(container) root.render(<GoogleSearchBox responseText={responseText} />) } else { const parentElement = document.getElementById("content_right") parentElement.insertBefore(container, parentElement.firstChild) const root = createRoot(container) root.render(<BaiduSearchBox responseText={responseText} />) } }
-
获取用户输入的内容是经过
window
的办法,获取地址栏中对应的恳求参数。- 关于百度查找,
wd
参数对应的便是查找内容。如:www.baidu.com/s?wd=你好 - 关于谷歌查找,
q
参数对应的便是查找内容,如:www.google.com/search?q=你好
// src/contents/plasmo.tsx 第 30 行 let urlSearchParams = new URLSearchParams(window.location.search) const queryParam = window.location.hostname == "www.google.com" ? urlSearchParams.get("q") : urlSearchParams.get("wd")
- 关于百度查找,
-
发送事情给 Background 是经过
chrome.runtime.sendMessage
完成的,发送的目标中有两个特点:-
type
:Background 可能会接收到多个事情,需求为事情命名以区分不同事情。 -
query
:查找内容,会作为 GPT 恳求的 prompt。
这儿需求留意的是,当 GPT 恳求成功或失败之后,需求再次烘托 GPT 呼应框,于是再次调用
displayGptResponse
办法。// src/contents/plasmo.tsx 第 58 行 chrome.runtime.sendMessage( { type: "fetchGptResponse", query: queryParam }, (response) => { if (response) { if (response.type === "gptResponse") { displayGptResponse(response.data, window.location.hostname) } else if (response.type === "gptError") { console.error("GPT Error:", response.error) displayGptResponse("Request GPT error...", window.location.hostname) } else if (response.type === "apiKeyError") { console.error("API Key Error: API Key not set or invalid.") displayGptResponse("API Key not set or invalid.", window.location.hostname) } } } )
-
-
Background 处理逻辑为判别事情类型。假如为 GPT 恳求事情,则恳求 GPT,并将恳求成果回来给 Content Script。
如下为恳求 GPT 代码,详细代码请检查
src/backgroundFunctions/handleGptApi.ts
// src/backgroundFunctions/handleGptApi.ts fetch( "https://api.openai.com/v1/engines/text-davinci-003/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: "Bearer " + apiKey }, body: JSON.stringify({ prompt: message.query, max_tokens: 1024 }) } )
需求 2
当用户点击每个查找成果页下方的 collapse 时,插件会去爬取对应网页的部分内容,然后将这部分内容作为 prompt 去恳求 GPT,总结网页内容。如下为详细流程图。
-
经过
document
获取每个查找成果页所对应的 HTML 元素,并在其下方烘托 collapse 组件。烘托办法与第一个需求相同。谷歌查找和百度查找的处理逻辑有细微差别,首要是因为 HTML 元素名不同。以 google 查找举例:
// src/contents/plasmo.tsx 第 79 行 let parentPageElement = document.getElementById("rso") let i = 0 for (let child of parentPageElement.children) { let targetElement = child.querySelector('[jsname="UWckNb"]') if (targetElement != null && targetElement.tagName === "A") { console.log("第" + i + "个page", targetElement.href) // add collapse let link = targetElement.href let container = document.createElement("div") container.id = "page-collapse-" + i const root = createRoot(container) child.insertAdjacentElement("afterend", container) root.render(<PageCollapse link={link} />) } i += 1 }
-
当用户点击 collapse 组件时,会展开对应的内容。此时,Content Script 会发送爬虫事情给 Background。
// src/contents/PageCollapse.tsx 第 18 行 const sendMessageToHandleCrawler = () => { return new Promise((resolve, reject) => { chrome.runtime.sendMessage( { type: "fetchCrawler", link: link }, (response) => { if (response) { if (response.type === "crawlerSuccess") { console.log("request crawler success", response.data) setLinkContent(response.data) resolve(response.data) // 解析 Promise } else if (response.type === "crawlerError") { console.error("request crawler error", response.error) reject(response.error) // 拒绝 Promise } } } ) }) }
-
Background 收到爬虫事情后,会去恳求 Flask 后端。
发送恳求的部分代码如下,详细代码请检查
src/backgroundFunctions/handleGptApi.ts
// src/backgroundFunctions/handleGptApi.ts fetch("http://localhost:5000/?link=" + encodeURIComponent(message.link)) .then((resp) => { if (resp.status === 200) { return resp .text() .then((text) => sendResponse({ type: "crawlerSuccess", data: text })) } else { sendResponse({ type: "crawlerError", error: "Cannot crawl the web page." }) } })
Python 爬虫代码如下,这儿需求留意两点:
- 网站内容过大,需求对爬取到的内容做截断,截取前 1000 个字符。
- 有些网站无法爬取到正常的内容,需求判别内容是否为空
@app.route('/') @use_kwargs({'link': fields.Str(required=True)}, location="query") def hello_world(link): response = requests.get(link) html = response.text soup = BeautifulSoup(html, "html.parser") text = soup.get_text() clean_text = text.strip().replace("\n", " ")[:1000] print(link, clean_text) if clean_text: return Response(clean_text, status=200) else: return Response('error', status=500)
-
Content Script 收到爬虫呼应内容后,会接着给 Background 发送 GPT 恳求事情,用 GPT 总结网页内容,并烘托到 collapse 框中。这儿的处理逻辑与需求 1 相似,就不再赘述啦~
作用展现
关于需求 1,其完成作用如下:
关于需求 2,其完成作用如下:
总结
在这篇文章中,我从技能选型、浏览器插件组件、代码结构、完成计划、作用展现等视点,详细介绍了 Search GPT 插件的完成进程。
插件的根本功用是能够正常运用的,需求优化的点在于 GPT 的呼应速度、查找成果页的摘要准确度等方面。
代码现已开源,我们感兴趣的话能够根据 README.md 文档去试用一下这个插件。现在插件还没有发布到 chrome 商铺。
假如运用进程中有 Bug 的话,费事提一下 Issue 呀~
Github 地址:github.com/ltyzzzxxx/s…
今日的内容就到这儿啦,我们觉得有用的话费事帮助点个赞、点个 Star 支撑一下呀,下期再见!