Node.js 实现异步并发爬虫(第 1 部分)

这个部分主要实现了如何使用 Node.js 编写一个异步并发爬虫,利用 axios 发送请求,cheerio 解析页面,并使用 p-limit 控制并发数。

步骤:

1. 初始化项目

在你的项目文件夹中初始化一个 Node.js 项目:

mkdir node-crawler
cd node-crawler
npm init -y
npm install axios cheerio p-limit
  • axios 用于发起 HTTP 请求;
  • cheerio 用于解析 HTML 内容(类似于 jQuery);
  • p-limit 控制并发请求的数量,避免请求过多导致服务器负载过高。

2. 编写爬虫代码

创建 crawler.js 文件:

const axios = require('axios');
const cheerio = require('cheerio');
const pLimit = require('p-limit');

// 模拟要爬取的一组 URL
const urls = [
  'https://example.com',
  'https://example.org',
  'https://example.net',
  // 你可以继续添加更多 URL
];

// 设置最大并发数(例如:最多同时5个请求)
const limit = pLimit(5);

// 获取网页内容的函数
async function fetchPage(url) {
  try {
    // 使用 axios 发送 GET 请求
    const response = await axios.get(url, {
      timeout: 5000, // 设置请求超时
      headers: {
        'User-Agent': 'Mozilla/5.0 (Crawler Bot)' // 模拟常见的浏览器
      }
    });

    // 解析 HTML
    const html = response.data;
    const $ = cheerio.load(html);

    // 从页面中提取标题
    const title = $('title').text();

    console.log(`[Success] ${url} - ${title}`);
    return { url, title };
  } catch (error) {
    console.error(`[Error] ${url} - ${error.message}`);
    return { url, error: error.message };
  }
}

// 运行爬虫并控制并发
async function run() {
  // 使用 limit 函数限制并发请求
  const tasks = urls.map(url => limit(() => fetchPage(url)));
  
  // 等待所有任务完成
  const results = await Promise.all(tasks);

  // 输出所有任务结果
  console.log('\nAll tasks completed:');
  console.log(results);
}

// 启动爬虫
run();

3. 代码解析:

  • urls: 定义了一组要爬取的网页 URL,你可以根据需求添加更多的 URL;
  • p-limit: 使用 p-limit 限制并发请求的数量。此例中设置为 5,表示同时最多只能发起 5 个请求;
  • fetchPage: 该函数负责处理每个网页的请求、HTML 解析及数据提取;
    • 使用 axios 获取网页内容,设置了 5 秒的请求超时;
    • 使用 cheerio 解析 HTML 文档,并提取 <title> 标签中的内容;
    • 捕获任何请求错误,并输出错误信息;
  • run: 负责调度多个爬虫任务并使用 Promise.all() 等待所有任务完成。

4. 执行爬虫

在命令行中运行爬虫:

node crawler.js

如果一切顺利,输出类似如下:

[Success] https://example.com - Example Domain
[Success] https://example.org - Example Domain
[Success] https://example.net - Example Domain

All tasks completed:
[
  { url: 'https://example.com', title: 'Example Domain' },
  { url: 'https://example.org', title: 'Example Domain' },
  { url: 'https://example.net', title: 'Example Domain' }
]

5. 下一步的优化建议:

  • 错误重试机制: 如果请求失败,尝试重新请求。可以使用 axios-retry 实现。
  • 代理支持: 如果服务器限制访问,可以使用代理来绕过。
  • 数据存储: 将抓取的数据存储到数据库中,比如 MongoDB 或 MySQL。
  • URL 队列管理: 你可以增加一个 URL 队列管理器,实现深度爬取。

你现在有一个基本的爬虫框架。如果有其他需求(比如增加重试机制或存储数据),可以告诉我,我们可以进一步扩展这个爬虫。