Axios抓取网页数据后,如何正确将多条目写入JSON文件?
解决JSON写入仅保留最后一条&内容损坏的问题
嘿,我刚看完你的代码,马上就发现了两个核心问题导致你遇到的麻烦,咱们一步步来解决:
问题根源拆解
- 每次循环都覆盖写入文件:你在
$('li.article').each()循环里调用fs.writeFile,这个方法默认是覆盖模式——每迭代一次就把之前的文件内容完全替换掉,最后自然只剩最后一条数据。 - 异步操作的竞态冲突:
fs.writeFile是异步函数,循环里会快速触发多次写入请求,文件系统可能还没完成上一次写入,下一次就开始了,这会导致文件内容混乱损坏。 - 未正确收集所有数据:你定义了
prodArray但没真正用来存储所有爬取到的对象,而是每次循环都生成单个data直接写入,完全浪费了这个变量的作用。
修改后的完整代码(带详细注释)
const axios = require("axios"); const cheerio = require("cheerio"); const fs = require('fs').promises; // 改用Promise版的fs API,更适配异步流程 const colors = require("colors"); // 用数组存储所有爬取到的数据,数组是存储多个同类型对象的最佳选择 let prodArray = []; axios.get("https://arstechnica.com/gadgets/") .then(urlResponse => { const $ = cheerio.load(urlResponse.data); $('li.article').each((i, elem) => { const link = $(elem).find("a.overlay").attr("href"); // 用trim()去除文本前后的空白字符,避免数据里有多余的换行/空格 const header = $(elem).find("h2").text().trim(); const author = $(elem).find("p.byline").find("a").text().trim(); console.log("---------------\n".red); // 规范JSON键名,去掉多余的空格和冒号,符合行业常规写法 const data = { link: link, title: header, author: author }; // 把当前数据对象添加到数组中,先攒着不着急写入 prodArray.push(data); }); // 等所有数据都爬取收集完成后,再一次性写入文件 // JSON.stringify的第三个参数2,用来格式化输出(缩进2空格),让JSON文件更易读 return fs.writeFile("verified.json", JSON.stringify(prodArray, null, 2)); }) .then(() => { console.log("所有数据已成功写入verified.json文件!".green); }) .catch(err => { // 统一捕获所有错误:包括axios请求失败、文件写入失败等 console.error("操作出错:", err.message.red); });
关键修改点解释
改用
fs.promises.writeFile:
基于Promise的API和axios的Promise链式调用完美契合,避免了回调嵌套,也能更优雅地处理异步流程,不会出现竞态冲突。先收集所有数据再写入:
循环里只把每个爬取到的data对象加入数组,等所有迭代完成后再一次性写入文件——彻底解决了覆盖写入的问题,也避免了多次写入的冲突。优化数据格式:
- 用
trim()清理文本的空白字符,让数据更干净; - 把键名从
"Link: "改成"link",去掉多余符号,让JSON结构更标准,后续读取数据也更方便。
- 用
统一错误处理:
用.catch()捕获整个链式调用中的所有错误,不管是请求失败还是写入失败,都能得到清晰的错误提示,方便你排查问题。
新手友好小提示
- 尽量避免用同步的
fs.writeFileSync:它会阻塞Node.js的事件循环,数据量大的时候会拖慢程序性能,异步的Promise版本是更好的选择。 JSON.stringify的第三个参数非常实用:设置成数字(比如2)会格式化输出JSON,要是不设置,文件会是一行压缩的内容,可读性极差。
内容的提问来源于stack exchange,提问作者AlexF
相关产品推荐
相关产品推荐

