使用 ChatGPT、React 和 Node.js 创建网站聚合器 🚀
网站聚合器是一种从互联网上的其他网站收集数据并将信息放在访问者可以访问的地方的网站。
网站聚合器有很多版本;有些是搜索引擎,例如 Google 和 Duck Duck go,有些则具有更多的Product Hunt结构,您可以在其中看到图片和简短的文字。
您通常会抓取网站,获取其元标记和 h1-6 标记,扫描其 sitemap.xml,然后使用某些模式对信息进行排序。
今天我将使用不同的解决方案😈
我将获取整个网站内容,将其发送给 ChatGPT,并要求他们提供我需要的信息。
ChatGPT 解析网站内容有点疯狂
所以让我们开始吧🚀
在本文中,您将学习如何构建一个网站聚合器,该聚合器从网站抓取内容并使用 ChatGPT 确定网站的标题和描述。
什么是 ChatGPT?
ChatGPT是由OpenAI 训练的 AI 语言模型 ,用于生成文本并以类似人类的对话方式与用户交互。值得一提的是,ChatGPT 是免费且开放给公众使用的。
用户只需几秒钟即可提交请求并获得历史、科学、数学和时事等广泛主题的信息或问题的答案。
ChatGPT 还可以执行其他任务,例如校对、释义和翻译。它还可以帮助编写、调试和解释代码片段。ChatGPT 的广泛功能正是其流行的原因。
ChatGPT 尚未作为 API 提供 :( 为了使用,我们必须设法获取信息 😈
Novu——第一个开源通知基础设施
简单介绍一下我们。Novu 是第一个开源通知基础设施。我们主要负责管理所有产品通知。这些通知可以是应用内通知(类似 Facebook 的铃铛图标 - Websockets)、电子邮件、短信等等。
如果你能给我们一颗星,我会非常高兴!也请在评论区告诉我❤️
https://github.com/novuhq/novu
ChatGPT 的限制
如前所述,ChatGPT 无法通过公共 API 访问。我们可以使用网页抓取技术来访问它。这涉及到自动登录 OpenAI 网站、验证码(您可以使用2captcha来完成)以及发送包含 OpenAI Cookie 的 API 请求。幸运的是,有一个公共库可以帮我们处理这些任务。请注意,这不是正式的 API,因此如果您尝试发出大量请求,可能会遇到限制。此外,它不适用于实时请求。如果您想使用它,请考虑实现一个队列系统来进行后台处理。
项目设置
在这里,我将指导您创建 Web 应用程序的项目环境。我们将使用 React.js 作为前端,使用 Node.js 作为后端服务器。
通过运行以下代码为 Web 应用程序创建项目文件夹:
mkdir website-aggregator
cd website-aggregator
mkdir client server
设置 Node.js 服务器
导航到服务器文件夹并创建一个package.json
文件。
cd server & npm init -y
安装 Express、Nodemon 和 CORS 库。
npm install express cors nodemon
ExpressJS 是一个快速、简约的框架,它提供了在 Node.js 中构建 Web 应用程序的多种功能, CORS是一个允许不同域之间通信的 Node.js 包, Nodemon是一个在检测到文件更改后自动重启服务器的 Node.js 工具。
创建一个index.js
文件——Web 服务器的入口点。
touch index.js
使用 ExpressJS 设置 Node.js 服务器。当您http://localhost:4000/api
在浏览器中访问时,以下代码片段会返回一个 JSON 对象。
//👇🏻index.js
const express = require("express");
const cors = require("cors");
const app = express();
const PORT = 4000;
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cors());
app.get("/api", (req, res) => {
res.json({
message: "Hello world",
});
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
安装非官方 ChatGPT API 库和 Puppeteer。ChatGPT API 使用 Puppeteer 作为可选的对等依赖项,以自动绕过 Cloudflare 保护。
npm install chatgpt puppeteer
要在文件中使用 ChatGPT API server/index.js
,您需要配置文件以使用require
和import
关键字来导入库。
因此,更新server/package.json
文件以包含类型关键字。
{ "type": "module" }
在文件顶部添加下面的代码片段server/index.js
。
import { createRequire } from "module";
const require = createRequire(import.meta.url);
//...other code statements
完成最后两个步骤后,您现在就可以在index.js
文件中使用 ChatGPT。
通过将启动命令添加到package.json
文件中的脚本列表中来配置 Nodemon。下面的代码片段使用 Nodemon 启动服务器。
//In server/package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon index.js"
},
恭喜!您现在可以使用以下命令启动服务器。
npm start
设置 React 应用程序
通过终端导航到客户端文件夹并创建一个新的 React.js 项目。
cd client
npx create-react-app ./
从 React 应用程序中删除冗余文件,例如徽标和测试文件,并更新App.js
文件以显示“Hello World”,如下所示。
function App() {
return (
<div>
<p>Hello World!</p>
</div>
);
}
export default App;
导航到src/index.css
文件并复制以下代码。它包含设计此项目所需的所有 CSS。
@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap");
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "Space Grotesk", sans-serif;
}
input {
padding: 10px;
width: 70%;
margin: 10px 0;
outline: none;
}
button {
width: 200px;
cursor: pointer;
padding: 10px 20px;
outline: none;
border: none;
background-color: #6d9886;
}
.home,
.home__form,
.website__item,
.loading {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.home__form > h2,
.home__form {
margin-bottom: 30px;
text-align: center;
width: 100%;
}
.home {
min-height: 100vh;
padding: 20px;
width: 100%;
}
.website__container {
width: 100%;
min-height: 50vh;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
padding: 15px;
}
.website__item {
width: 80%;
margin: 10px;
background-color: #f7f7f7;
border-radius: 5px;
padding: 30px;
box-shadow: 0 2px 8px 0 rgba(99, 99, 99, 0.2);
}
.website__item > img {
width: 70%;
border-radius: 5px;
}
.website__item > h3 {
margin: 10px 0;
}
.website__item > p {
text-align: center;
opacity: 0.7;
}
.loading {
height: 100vh;
background-color: #f2e7d5;
}
更新App.js
文件以显示一个输入字段,允许您提供网站的 URL。
import React, { useState } from "react";
const App = () => {
const [url, setURL] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log({ url });
setURL("");
};
return (
<div className='home'>
<form className='home__form'>
<h2>Website Aggregator</h2>
<label htmlFor='url'>Provide the website URL</label>
<input
type='url'
name='url'
id='url'
value={url}
onChange={(e) => setURL(e.target.value)}
/>
<button onClick={handleSubmit}>ADD WEBSITE</button>
</form>
</div>
);
};
export default App;
恭喜!您已成功创建应用程序的用户界面。在接下来的部分中,我将指导您使用 Puppeteer 从网站抓取数据,并通过 ChatGPT 获取网站的描述和标题。
如何在 Node.js 中使用 Puppeteer 抓取数据
Puppeteer是一个 Node.js 库,可以自动执行多个浏览器操作,例如表单提交、抓取单页应用程序、UI 测试,特别是网页抓取和生成网页截图。
这里,我将指导你如何在 Node.js 中使用 Puppeteer 抓取网站内容。我们会将用户提供的网站 URL 发送到 Node.js 服务器,并通过 URL 抓取网站内容。
在服务器上创建一个端点,接受来自 React 应用程序的网站 URL。
app.post("/api/url", (req, res) => {
const { url } = req.body;
//👇🏻 The URL from the React app
console.log(url);
});
导入 Puppeteer 库并抓取网站内容,如下所示:
//👇🏻 Import Puppeteer at the top
const puppeteer = require("puppeteer");
app.post("/api/url", (req, res) => {
const { url } = req.body;
//👇🏻 Puppeteer webscraping function
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
//👇🏻 returns all the website content
const websiteContent = await page.evaluate(() => {
return document.documentElement.innerText.trim();
});
//👇🏻 returns the website meta image
const websiteOgImage = await page.evaluate(() => {
const metas = document.getElementsByTagName("meta");
for (let i = 0; i < metas.length; i++) {
if (metas[i].getAttribute("property") === "og:image") {
return metas[i].getAttribute("content");
}
}
});
console.log({ websiteContent, websiteOgImage })
await browser.close();
})();
});
在 React 应用程序中添加一个将 URL 发送到api/url/
端点的函数。
const handleSubmit = (e) => {
e.preventDefault();
setLoading(true);
setURL("");
//👇🏻 Calls the function.
sendURL();
};
async function sendURL() {
try {
const request = await fetch("http://localhost:4000/api/url", {
method: "POST",
body: JSON.stringify({
url,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
const data = await request.json();
//👇🏻 toggles the loading state if the request is successful
if (data.message) {
setLoading(false);
}
} catch (err) {
console.error(err);
}
}
从上面的代码片段中,我们添加了一个描述 API 请求状态的加载状态。
const [loading, setLoading] = useState(false);
创建一个Loading
在请求待处理时显示给用户的组件。
import React from "react";
const Loading = () => {
return (
<div className='loading'>
<h1>Loading, please wait...</h1>
</div>
);
};
export default Loading;
Loading
当内容尚未可用时,显示该组件。
import Loading from "./Loading";
//👇🏻 Add this code within the App.js component
if (loading) {
return <Loading />;
}
恭喜!您已经学会了如何使用 Puppeteer 从网站抓取内容。在接下来的部分中,您将学习如何通过生成网站描述和品牌名称,在 Node.js 中与 ChatGPT 进行通信。
如何在 Node.js 中与 ChatGPT 进行通信
ChatGPT 目前尚未开放公共 API。因此,为了使用它,我们必须自行摸索——这意味着我们将执行完整的浏览器自动化操作,登录 OpenAI 网站,解决验证码,并发送包含 OpenAI Cookie 的 API 请求。
幸运的是,有一个可以实现此功能的公共图书馆 ,并且已经作为项目要求的一部分进行了安装。
导入 ChatGPT API 库 并创建向 ChatGPT 发送请求的函数。
import { ChatGPTAPIBrowser } from "chatgpt";
async function chatgptFunction(content) {
// use puppeteer to bypass cloudflare (headful because of captchas)
const api = new ChatGPTAPIBrowser({
email: "<CHATGPT_EMAIL_ADDRESS>",
password: "<CHATGPT_PASSWORD>",
});
await api.initSession();
//👇🏻 Extracts the brand name from the website content
const getBrandName = await api.sendMessage(
`I have a raw text of a website, what is the brand name in a single word? ${content}`
);
//👇🏻 Extracts the brand description from the website content
const getBrandDescription = await api.sendMessage(
`I have a raw text of a website, can you extract the description of the website from the raw text. I need only the description and nothing else. ${content}`
);
//👇🏻 Returns the response from ChatGPT
return {
brandName: getBrandName.response,
brandDescription: getBrandDescription.response,
};
}
Chat GPT 超级智能,它能回答我们提出的任何问题。基本上,我们会让它根据完整的网站 HTML 代码,为我们编写品牌名称和描述。
品牌名称通常可以在“og:site_name”上找到,但为了展示它的酷炫之处,我们会让 ChatGPT 提取它。至于描述,它简直太神奇了。它会告诉我们网站的主题,并概括所有内容!
接下来,将路线
更新api/url
为如下所示:
//👇🏻 holds all the ChatGPT result
const database = [];
//👇🏻 generates a random string as ID
const generateID = () => Math.random().toString(36).substring(2, 10);
app.post("/api/url", (req, res) => {
const { url } = req.body;
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const websiteContent = await page.evaluate(() => {
return document.documentElement.innerText.trim();
});
const websiteOgImage = await page.evaluate(() => {
const metas = document.getElementsByTagName("meta");
for (let i = 0; i < metas.length; i++) {
if (metas[i].getAttribute("property") === "og:image") {
return metas[i].getAttribute("content");
}
}
});
//👇🏻 accepts the website content as a parameter
let result = await chatgptFunction(websiteContent);
//👇🏻 adds the brand image and ID to the result
result.brandImage = websiteOgImage;
result.id = generateID();
//👇🏻 adds the result to the array
database.push(result);
//👇🏻 returns the results
return res.json({
message: "Request successful!",
database,
});
await browser.close();
})();
});
要在 React 应用程序中显示响应,请创建一个保存服务器响应的状态。
const [websiteContent, setWebsiteContent] = useState([]);
async function sendURL() {
try {
const request = await fetch("http://localhost:4000/api/url", {
method: "POST",
body: JSON.stringify({
url,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
const data = await request.json();
if (data.message) {
setLoading(false);
//👇🏻 update the state with the server response
setWebsiteContent(data.database);
}
} catch (err) {
console.error(err);
}
}
最后,更新App.js
布局以向用户显示服务器的响应。
const App = () => {
//...other code statements
//👇🏻 remove the quotation marks around the description
const trimDescription = (content) =>
content.match(/(?:"[^"]*"|^[^"]*$)/)[0].replace(/"/g, "");
return (
<div className='home'>
<form className='home__form'>
<h2>Website Aggregator</h2>
<label htmlFor='url'>Provide the website URL</label>
<input
type='url'
name='url'
id='url'
value={url}
onChange={(e) => setURL(e.target.value)}
/>
<button onClick={handleSubmit}>ADD WEBSITE</button>
</form>
<main className='website__container '>
{websiteContent.map((item) => (
<div className='website__item' key={item.id}>
<img src={item?.brandImage} alt={item?.brandName} />
<h3>{item?.brandName}</h3>
<p>{trimDescription(item?.brandDescription)}</p>
</div>
))}
</main>
</div>
);
};
恭喜!🎉您已完成本教程的项目。
以下是从应用程序获得的结果示例:
结论
到目前为止,我们已经讨论过,
- ChatGPT 是什么
- 如何使用 Puppeteer 抓取网站内容,以及
- 如何在 Node.js 应用程序中与 ChatGPT 进行通信
本教程将引导您了解使用 Puppeteer 和 ChatGPT 构建的应用程序示例。ChatGPT 可被视为终极个人助理,在各个领域都非常有用,能够帮助我们更智能、更高效地工作。
本教程的源代码可以在这里找到:
https://github.com/novuhq/blog/tree/main/website-aggregator-with-chatgpt-react
感谢您的阅读!
帮帮我!
如果您觉得这篇文章帮助您更好地理解了 WebSocket!请给我们一个 Star,我会非常高兴!也请在评论区告诉我❤️
https://github.com/novuhq/novu