从零开始:用JavaScript编写一个简单的网页数据抓取脚本

作者:慢半拍时间:2026-10-30 19:03:02浏览:171

本文详细讲解如何从零开始使用JavaScript(Node.js、Axios与Cheerio)编写一个简单且实用的网页数据抓取脚本,涵盖环境准备、页面分析、代码编写、数据持久化及常见问题排查。

网页数据抓取(Web Scraping)是指通过程序自动向目标网站发起网络请求,获取网页内容并从中筛选、提取结构化信息的过程。本文面向刚接触该技术的开发者,通过 Node.js 环境搭配轻量工具 axios 与 cheerio,从零实现一个能够抓取文章标题与超链接并保存为本地 JSON 文件的完整爬虫脚本。

一、网页数据抓取的基本原理与准备工作

浏览器在访问网站时,首先向服务器发送 HTTP GET 请求,服务器返回 HTML 文本,随后浏览器解析该文本并渲染出图文并茂的页面。我们在编写轻量级爬虫时,本质上是在用程序模拟这一过程的前半段:先用网络库下载原始 HTML 字符串,再利用类似 jQuery 的 DOM 选择器解析语法,精准抽取出目标字段。

从零开始:用JavaScript编写一个简单的网页数据抓取脚本
图 1 说明:轻量级网页爬虫的数据流转全生命周期,重点在 HTML 字符串获取与内存 DOM 解析

开始前,请确保本地已安装 Node.js 环境(建议 LTS 版本)。在空目录中打开命令行终端,依次执行以下命令完成项目初始化与依赖安装:

# 1. 创建并进入项目目录
mkdir my-simple-scraper
cd my-simple-scraper

# 2. 初始化 package.json 配置文件
npm init -y

# 3. 安装网络请求库 axios 与网页解析库 cheerio
npm install axios cheerio

其中,axios 是一个基于 Promise 的 HTTP 客户端,用于发送 GET 请求获取网页源代码;cheerio 则是专门为服务端设计的快速、灵活的 HTML 解析器,API 完全兼容 jQuery,能够让我们用 $('div.title').text() 这样熟悉的语法在 Node.js 中提取页面内容。

二、核心操作步骤:实现第一个抓取脚本

编写爬虫脚本通常遵循清晰的四个阶段:准备工作、页面审查、逻辑编码和结果落地。每一步都有具体的判断标准与实现要点。

从零开始:用JavaScript编写一个简单的网页数据抓取脚本
图 2 说明:从环境搭建到最终数据文件输出的递进式操作步骤拆解

1. 审查目标网页的 DOM 结构

在编写任何解析代码前,必须先在浏览器中观察目标页面的 HTML 结构。打开目标网页(例如包含新闻或博客列表的网页),按下 F12 键调出开发者工具,切换到 Elements 面板。使用左上角的元素选择指针点击列表中的第一项,定位对应的 DOM 节点:

  • 寻找重复列表项的公共容器类名,例如 .post-item 或 article;
  • 在容器内部找到标题所在的标签及其选择器,例如 h2.title a;
  • 确认要抓取的数据对应标签的属性或文本:标题内容通常在 a 标签的文本节点中,超链接则存放在 href 属性中。

2. 编写完整的采集脚本

在项目根目录下新建 scraper.js 文件。以下是具备请求头伪装、异常处理与本地文件写入的完整实现:

// scraper.js
const axios = require('axios');
const cheerio = require('cheerio');
const fs = require('fs');
const path = require('path');

// 目标网址(以公开文章列表页为例)
const TARGET_URL = 'https://news.ycombinator.com/';

async function fetchWebsiteData() {
  try {
    console.log(`正在请求目标页面: ${TARGET_URL}`);

    // 发起 HTTP 请求,并配置基础请求头
    const response = await axios.get(TARGET_URL, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
      },
      timeout: 10000 // 10秒超时
    });

    const html = response.data;
    // 使用 cheerio 载入 HTML
    const $ = cheerio.load(html);
    const results = [];

    // 遍历每一个新闻条目节点(此处以 Hacker News 经典结构为例)
    $('.athing').each((index, element) => {
      // 在当前节点下寻找标题链接
      const titleLink = $(element).find('.titleline > a');
      const title = titleLink.text().trim();
      const link = titleLink.attr('href');

      if (title && link) {
        results.push({
          id: index + 1,
          title: title,
          url: link,
          scrapedAt: new Date().toISOString()
        });
      }
    });

    console.log(`抓取完成,共提取到 ${results.length} 条数据`);

    // 将结果写入本地 output.json 文件
    const outputPath = path.join(__dirname, 'output.json');
    fs.writeFileSync(outputPath, JSON.stringify(results, null, 2), 'utf-8');
    console.log(`数据已成功保存至: ${outputPath}`);

  } catch (error) {
    if (error.response) {
      console.error(`请求失败,状态码: ${error.response.status}`);
    } else if (error.code === 'ECONNABORTED') {
      console.error('网络请求超时,请检查网络或代理设置');
    } else {
      console.error(`执行过程中发生错误: ${error.message}`);
    }
  }
}

// 执行主函数
fetchWebsiteData();

保存文件后,在终端中执行以下命令运行该脚本:

node scraper.js

执行完成后,控制台将输出提取条数,并在当前目录下生成 output.json 文件。打开该文件即可查看到格式规整的 JSON 数组。

三、技术选型与适用边界

JavaScript 爬虫方案并不局限于 Axios + Cheerio。在决定技术方案前,必须明确目标页面的渲染机制。很多初学者误以为只要是网页就都能用上述脚本抓取,结果发现提取出来的数据全为空,这是因为没有区分“服务端渲染(SSR)”与“客户端单页应用(SPA)”。

从零开始:用JavaScript编写一个简单的网页数据抓取脚本
图 3 说明:三种主流 JavaScript 数据提取方式的技术特性与适用场景对照
如何快速验证目标页面能否直接使用 Axios 抓取?
在浏览器中打开目标网页,右键选择“查看网页源代码”(快捷键 Ctrl+U 或 Cmd+Option+U)。在打开的纯源码中按下 Ctrl+F 搜索你想要抓取的文字。如果搜得到,说明内容是服务端直接输出的静态 HTML,完全可以使用本文介绍的轻量方案;如果源码中搜不到,说明内容是由前端 JavaScript 动态加载的,此时应优先在 DevTools 的 Network 面板中寻找对应的后端 JSON 接口,或者改用 Puppeteer 无头浏览器。

四、常见问题排查与健壮性处理

实际运行抓取脚本时,往往会遇到网络拦截、节点丢失或格式异常等问题。针对这些最容易踩坑的环节,以下给出原因分析与解决对策:

1. 报错 403 Forbidden 或提示拦截

原因:大多数网站有基础的安全防御策略。当使用 axios.get() 时,默认发出的 User-Agent 会包含 axios/1.x 字样,服务器一旦识别出非人类浏览器行为,就会直接拒绝访问。
解决办法:在 Axios 请求配置中手动传入真实的浏览器 User-Agent,模拟主流操作系统与浏览器标头。如果遇到严格站点,还可以补全 Referer 和 Cookie 标头。

2. 抓取结果为空数组但控制台无报错

原因:一是选择器拼写错误或层级过于严苛(例如写成了 div > ul > li:nth-child(2) > a),一旦目标网站稍作改动就会失效;二是该部分数据为前端异步加载。
解决办法:在控制台打印 html.slice(0, 500) 检查返回的 HTML 源码,确认目标节点是否存在;选择器尽量采用稳定、有语义的类名或标签属性,避免依赖深度绝对层级。

3. 相对链接无法直接点击使用

原因:很多 HTML 标签的 href 属性使用的是相对路径(例如 /article/1024),直接保存后无法独立访问。
解决办法:使用 Node.js 内置的 new URL(relativeLink, BASE_URL).href 快速转换为标准绝对 URL:

const relativeLink = $(element).find('a').attr('href');
const absoluteUrl = new URL(relativeLink, 'https://example.com').href;

五、编写规范与总结

编写爬虫不仅要关注技术实现,还要兼顾代码健壮性与合规性。一个合格的抓取脚本应当具备优雅的容错与频控设计。

从零开始:用JavaScript编写一个简单的网页数据抓取脚本
图 4 说明:生产级爬虫编写的关键维度与合规安全准则

在进行多页批量抓取时,严禁使用 Promise.all 无节制地瞬间发起几十上百个并发请求,这不仅极易触发目标服务器的封禁机制,也会对其带宽与服务稳定性造成冲击。合理的做法是引入延时函数:

// 封装简易休眠函数
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function scrapeMultiplePages(urls) {
  for (const url of urls) {
    await fetchWebsiteData(url);
    // 每次请求后休眠 1.5 秒
    await delay(1500);
  }
}

通过本文的讲解与实践,您已经掌握了利用 JavaScript 编写轻量级爬虫的完整路径:从 Node.js 基础依赖安装、使用 DevTools 定位元素,到利用 Axios 获取数据、Cheerio 解析 DOM,并将结果持久化为 JSON。在此基础上,您可以结合具体业务需求,进一步探索登录会话维持、多页分页爬取以及关系型数据库存储等进阶主题。

作者最新文章
相关文章
更多