《NestJS智能体开发》(六):网站内容抓取为知识库数据

为了让智能体能够更精准地决策,我们需要为它构建一个专业知识库,本文将探讨 NestJS 中结合 PlaywrightTurndown 等工具,实现对网站内容的抓取,并将其转换为Markdown数据。

1. 关键技术介绍

  • Playwright:一个由微软开发的用于 Web 浏览器自动化的库,支持 Chromium、Firefox 和 WebKit 等多种浏览器,可以轻易的实现页面的各种自动化操作,比如模拟点击,模拟键盘输入,甚至可以通过 JavaScript 或 Python(通过 Playwright for Python)来实现爬虫功能。

  • Turndown:一个 HTML 到 Markdown 的转换器,可以帮助我们将从网页上抓取的 HTML 内容,转换为简洁的 Markdown 格式,便于存储和展示。

2. 安装依赖

为了实现网站内容抓取和转换,我们需要先安装 PlaywrightTurndown:

bash 复制代码
# 安装 Playwright
pnpm add playwright
pnpm exec install playwright

# 安装 Turndown
pnpm add turndown

3. 爬虫代码实现

以下是基于 PlaywrightTurndown 的网站内容抓取和转换代码:

typescript 复制代码
import { Injectable } from '@nestjs/common';
import { chromium } from 'playwright';
import TurndownService from 'turndown';

@Injectable()
export class CrawlerService {
  private turndown: TurndownService;

  constructor() {
    // 初始化 Turndown 转换器
    this.turndown = new TurndownService({
      bullet: '-', // 无序列表符号
      codeBlock: '```', // 代码块符号
      headingStyle: 'atx', // 标题样式
    });
  }

  async crawl(baseUrl: string): Promise<{ title: string; url: string; content: string }[]> {
    const docs: { title: string; url: string; content: string }[] = [];
    const urls = new Set<string>();
    urls.add(`${baseUrl}/`);

    try {
      await this.recursiveCrawl(baseUrl, docs, urls);
    } catch (err) {
      console.error('爬取失败:', err);
    }

    return docs;
  }

  private async recursiveCrawl(
    url: string,
    docs: { title: string; url: string; content: string }[],
    urls: Set<string>,
  ): Promise<void> {
    const browser = await chromium.launch();
    const context = await browser.newContext();
    const page = await context.newPage();

    try {
      await page.goto(url, { waitUntil: 'networkidle' });
      const title = await page.title();

      // 移除所有干扰元素
      await page.evaluate(() => {
        document.querySelectorAll('script, style, link[rel="stylesheet"], [style]').forEach(el => el.remove());
      });

      // 获取页面内容并转换为 Markdown
      const content = await page.content();
      const md = await new Promise(r => {
         r(this.turndown.turndown(content));
      });

      docs.push({ title, url, content: md });

      // 提取所有链接并递归抓取
      const links = await page.$$eval('a[href]', (elements) =>
        elements.map((el) => el.href?.replace(/#.*$/, '')).filter((link) => link.startsWith(baseUrl))
      );
      
      console.log(`正在抓取:${title} - ${url}`);

      for (const link of links) {
        if (!urls.has(link)) {
          urls.add(link);
          await page.waitForTimeout(50);
          await this.recursiveCrawl(link, docs, urls);
        }
      }
    } catch (err) {
      console.error(`抓取失败: ${url}`, err);
    } finally {
      await page.close();
      await context.close();
      await browser.close();
    }
  }
}

4. 代码说明

  1. 无头浏览器启动

    使用 chromium.launch() 启动无头浏览器,这是一种在后台运行的浏览器,可以模拟用户的行为,如浏览网站、点击链接等。

  2. 页面内容抓取与清理

    • 使用 page.goto() 访问目标网页,并等待页面加载完成。
    • 使用 page.evaluate() 执行 JavaScript 代码,移除网页中的 <script><style>[style] 等干扰元素,以便更准确地提取内容。
  3. HTML 转 Markdown

    使用 Turndown 将网页的 HTML 内容转换为 Markdown 格式。Turndown 支持自定义配置,可以根据需求调整 Markdown 的输出格式。

  4. 递归抓取链接

    通过 page.$$eval() 提取页面中的所有链接,并递归地对每个链接进行抓取,确保能够抓取到整个网站的内容。

5. 知识库数据存储

我们可以将抓取的 Markdown 数据转换为向量,并存储到 Milvus 中,从而构建一个基于向量的知识库。

6. 应用场景

  • 自动化知识库文档构建

    • 从多个网站抓取相关知识内容,并统一存储到知识库中,方便后续RAG系统使用。
  • 内容更新监测

    • 定期抓取网站内容,监测内容的变化,并及时更新知识库数据。

7. 注意事项

  • 遵守法律法规

    • 在爬取网站内容时,必须遵守相关法律法规,尊重网站的版权和隐私政策。
  • 避免过度请求

    • 合理设置爬虫的请求频率和并发数,避免对目标网站造成过大压力,导致服务器崩溃或被封禁。
  • 处理反爬机制

    • 部分网站可能会采取反爬措施,如验证码、IP 封禁等。可以通过设置合理的请求头、使用代理 IP 或模拟用户行为等方式,降低被检测的风险。

8. 示例运行

为了验证爬虫功能是否正常,可以运行以下代码:

typescript 复制代码
async function bootstrap() {
  const baseUrl = 'https://docs.nestjs.com';
  const crawler = new CrawlerService();
  const docs = await crawler.crawl(baseUrl);

  console.log('抓取完成,共抓取了', docs.length, '个页面');
  // 打印结果到控制台或保存到文件
}

bootstrap();

总结

通过 NestJS、Playwright 和 Turndown 的结合,我们可以轻松实现对网站内容的抓取和转换,并将其存储到知识库中。这种技术不仅可以用于知识文档的自动化抓取,还可以用于构建智能体的内容监测系统。

相关推荐
clownAdam3 分钟前
通俗易懂的分类算法之K近邻详解
人工智能·算法·分类·数据挖掘·knn·k邻近
灏瀚星空8 分钟前
用Python+Flask打造可视化武侠人物关系图生成器:从零到一的实战全记录
开发语言·人工智能·经验分享·笔记·python·flask
java1234_小锋9 分钟前
一周学会Flask3 Python Web开发-Flask3之表单处理WTForms安装与定义WTForms表单类
开发语言·前端·python
atbigapp.com32 分钟前
AI数据分析:deepseek生成SQL
人工智能·sql·ai·数据分析·提示词
我们的五年36 分钟前
ChatGPT与DeepSeek:开源与闭源的AI模型之争
人工智能·chatgpt
AskHarries39 分钟前
如何利用Twilio Verify 发送验证码短信?
后端
Hamm1 小时前
巧妙使用位运算来解决真实开发中的权限控制场景
java·后端·算法
xiaosenyouli1 小时前
政务信息化项目命名有什么门道?
大数据·人工智能
Asthenia04121 小时前
浅析连接池:没有会如何?SpringBoot+Mybatis下如何接入呢?DataSource显功劳!
后端
找方案1 小时前
黑龙江省地标-DB31/T 862-2021 “一网通办”政务服务中心建设和运行规范
大数据·人工智能·政务