用 Node.js 编写一个简单网页爬虫

余胜军说java助理
2025-12-13 13:04
阅读 976

用 Node.js 编写一个简单网页爬虫:从零开始入门

引言/概述

随着互联网数据的爆炸式增长,网络爬虫(Web Crawler)成为获取公开数据的重要工具。无论是用于数据分析、内容聚合,还是市场调研,爬虫都能帮助我们高效地从网页中提取结构化信息。Node.js 凭借其非阻塞 I/O 模型和丰富的生态库,成为构建轻量级爬虫的理想选择。

本文将带领你从零开始,使用 Node.js 编写一个简单的网页爬虫。我们将涵盖环境准备、发送 HTTP 请求、解析 HTML、提取目标数据、处理异常以及遵守爬虫礼仪等关键环节。最终实现一个能够抓取某新闻网站标题列表的小型爬虫程序。即使你是初学者,也能通过本文掌握爬虫的基本原理与实现方法。

核心内容

准备工作:安装必要依赖

在开始编写代码之前,我们需要安装几个关键的 Node.js 包:

  • axios:用于发送 HTTP 请求,比原生 http 模块更简洁。
  • cheerio:类似 jQuery 的服务器端 HTML 解析库,用于提取 DOM 元素。
  • fs(内置模块):用于将抓取的数据保存到本地文件。

首先,创建一个新的项目目录并初始化 npm:

mkdir simple-crawler
cd simple-crawler
npm init -y

然后安装依赖:

npm install axios cheerio

注意:本文示例以抓取公开且允许爬取的网站为目标(如某些新闻聚合页)。请务必遵守目标网站的 robots.txt 协议,并避免对服务器造成过大压力。

发送 HTTP 请求获取网页内容

我们使用 axios 向目标 URL 发起 GET 请求,获取网页的 HTML 源码。以下是一个基础示例,抓取一个假设的新闻列表页面(例如:https://example-news.com):

// crawler.js
const axios = require('axios');

async function fetchPage(url) {
  try {
    const response = await axios.get(url, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (compatible; SimpleCrawler/1.0)'
      }
    });
    return response.data; // 返回 HTML 字符串
  } catch (error) {
    console.error('请求失败:', error.message);
    return null;
  }
}

// 示例调用
const url = 'https://example-news.com';
fetchPage(url).then(html => {
  if (html) {
    console.log('成功获取页面,长度:', html.length);
  }
});

这里我们设置了 User-Agent 头,模拟浏览器访问,避免被部分网站识别为机器人而拒绝请求。

使用 Cheerio 解析 HTML 并提取数据

获取 HTML 后,下一步是解析并提取我们需要的信息。假设目标页面的新闻标题都包裹在 <h2 class="title"> 标签中,我们可以使用 cheerio 进行选择和遍历:

const cheerio = require('cheerio');

function extractTitles(html) {
  const $ = cheerio.load(html);
  const titles = [];

  $('h2.title').each((index, element) => {
    const title = $(element).text().trim();
    if (title) {
      titles.push(title);
    }
  });

  return titles;
}

cheerio.load() 将 HTML 字符串加载为可操作的 DOM 对象,之后就可以像使用 jQuery 一样使用 CSS 选择器(如 $('h2.title'))来定位元素。

整合流程:构建完整的爬虫函数

现在我们将上述步骤整合成一个完整的爬虫函数,并添加数据保存功能:

const fs = require('fs').promises;

async function crawlNews(url, outputFile = 'news_titles.txt') {
  console.log(`正在抓取: ${url}`);
  
  const html = await fetchPage(url);
  if (!html) {
    console.log('无法获取页面内容,终止爬取');
    return;
  }

  const titles = extractTitles(html);
  console.log(`共提取到 ${titles.length} 条新闻标题`);

  // 将标题写入文件
  const content = titles.join('\n');
  await fs.writeFile(outputFile, content, 'utf8');
  console.log(`数据已保存至 ${outputFile}`);
}

// 启动爬虫
const targetUrl = 'https://example-news.com';
crawlNews(targetUrl);

运行此脚本后,你将在项目根目录下看到一个 news_titles.txt 文件,其中包含所有抓取到的新闻标题。

处理常见问题与增强健壮性

实际爬虫开发中会遇到各种问题,以下是几个关键点:

1. 错误重试机制

网络请求可能因超时或临时故障失败。我们可以添加简单的重试逻辑:

async function fetchPageWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await axios.get(url, { timeout: 5000 });
      return response.data;
    } catch (error) {
      console.warn(`第 ${i + 1} 次尝试失败:`, error.message);
      if (i === retries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒后重试
    }
  }
}

2. 遵守 robots.txt 与爬虫礼仪

负责任的爬虫应尊重网站的 robots.txt 规则。虽然本文不深入实现自动解析 robots.txt,但建议手动检查目标网站的该文件(如 https://example.com/robots.txt),确认是否允许爬取目标路径。

此外,应控制请求频率,避免对服务器造成压力。可在请求之间加入延迟:

await new Promise(resolve => setTimeout(resolve, 1000)); // 每次请求间隔1秒

3. 处理动态内容(进阶)

如果目标网站使用 JavaScript 动态加载内容(如 React/Vue 应用),axios 获取的 HTML 可能不包含完整数据。此时需使用 Puppeteer 等无头浏览器工具。但出于简单性,本文聚焦于静态页面爬取。

完整代码示例

以下是整合后的完整爬虫代码:

// simple-crawler.js
const axios = require('axios');
const cheerio = require('cheerio');
const fs = require('fs').promises;

async function fetchPage(url) {
  try {
    const response = await axios.get(url, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (compatible; SimpleCrawler/1.0)'
      },
      timeout: 5000
    });
    return response.data;
  } catch (error) {
    console.error('请求失败:', error.message);
    return null;
  }
}

function extractTitles(html) {
  const $ = cheerio.load(html);
  const titles = [];

  $('h2.title').each((index, element) => {
    const title = $(element).text().trim();
    if (title) {
      titles.push(title);
    }
  });

  return titles;
}

async function crawlNews(url, outputFile = 'news_titles.txt') {
  console.log(`正在抓取: ${url}`);
  
  const html = await fetchPage(url);
  if (!html) {
    console.log('无法获取页面内容,终止爬取');
    return;
  }

  const titles = extractTitles(html);
  console.log(`共提取到 ${titles.length} 条新闻标题`);

  const content = titles.join('\n');
  await fs.writeFile(outputFile, content, 'utf8');
  console.log(`数据已保存至 ${outputFile}`);
}

// 使用示例(请替换为真实可爬取的URL)
const targetUrl = 'https://example-news.com';
crawlNews(targetUrl);

⚠️ 注意:请将 targetUrl 替换为你有权爬取的真实网站 URL。切勿对禁止爬取或敏感网站发起请求。

总结

通过本文,我们从零开始构建了一个基于 Node.js 的简单网页爬虫。核心流程包括:使用 axios 发起 HTTP 请求获取 HTML、利用 cheerio 解析并提取目标数据、最后将结果保存到本地文件。同时,我们也探讨了错误处理、请求节流、遵守网络礼仪等实践要点。

虽然这个爬虫功能基础,但它为更复杂的爬虫系统奠定了基础——你可以在此之上扩展多页面爬取、数据去重、数据库存储、定时任务等功能。然而,请始终牢记:合法合规是爬虫开发的前提。在抓取任何网站前,务必确认其是否允许自动化访问,并控制请求频率,做一个负责任的数据使用者。

Node.js 凭借其异步特性和丰富生态,使得爬虫开发变得高效而简洁。希望本文能为你打开网络数据采集的大门,激发更多探索与实践!

评论 0

最热最新
暂无评论
余胜军说java助理Lv.1
0
影响力
0
文章
0
粉丝
聊天室