从零开始:用JavaScript编写一个简单的网页数据抓取脚本
本文详细讲解如何从零开始使用JavaScript(Node.js、Axios与Cheerio)编写一个简单且实用的网页数据抓取脚本,涵盖环境准备、页面分析、代码编写、数据持久化及常见问题排查。
网页数据抓取(Web Scraping)是指通过程序自动向目标网站发起网络请求,获取网页内容并从中筛选、提取结构化信息的过程。本文面向刚接触该技术的开发者,通过 Node.js 环境搭配轻量工具 axios 与 cheerio,从零实现一个能够抓取文章标题与超链接并保存为本地 JSON 文件的完整爬虫脚本。
一、网页数据抓取的基本原理与准备工作
浏览器在访问网站时,首先向服务器发送 HTTP GET 请求,服务器返回 HTML 文本,随后浏览器解析该文本并渲染出图文并茂的页面。我们在编写轻量级爬虫时,本质上是在用程序模拟这一过程的前半段:先用网络库下载原始 HTML 字符串,再利用类似 jQuery 的 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 中提取页面内容。
二、核心操作步骤:实现第一个抓取脚本
编写爬虫脚本通常遵循清晰的四个阶段:准备工作、页面审查、逻辑编码和结果落地。每一步都有具体的判断标准与实现要点。
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)”。
在浏览器中打开目标网页,右键选择“查看网页源代码”(快捷键 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;
五、编写规范与总结
编写爬虫不仅要关注技术实现,还要兼顾代码健壮性与合规性。一个合格的抓取脚本应当具备优雅的容错与频控设计。
在进行多页批量抓取时,严禁使用 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。在此基础上,您可以结合具体业务需求,进一步探索登录会话维持、多页分页爬取以及关系型数据库存储等进阶主题。
Java和JavaScript名字像但没关系?详解两者及HTML的技术分工
2026-11-03 20:24:10
Node.js 中 CommonJS 与 ES Modules 模块规范的区别及使用
2026-11-03 15:35:13
先学JS还是先学Java?全栈开发视角的学习路径规划
2026-11-03 13:35:26
解决Node.js安装后提示“node不是内部或外部命令”
2026-11-03 08:39:43
生产环境升级 Node.js 的风险评估与 LTS 版本选择
2026-11-02 10:30:14








