JavaScript爬虫实战:抓取公众号历史文章与阅读量评论

发布时间:2026/10/17 21:16:23

JavaScript爬虫实战:抓取公众号历史文章与阅读量评论 简介这是一份基于 Node.js 的微信爬虫项目源码面向需要批量采集公众号数据的开发者与数据分析人员通过 AnyProxy 中间人代理机制解析微信 HTTPS 请求可获取文章正文、阅读量、点赞量、在看数及评论等核心指标并支持抓取公众号全部历史文章链接。项目兼容 AnyProxy 4可运行于个人电脑或服务器并提供 Docker 部署方案使用前需准备 Node 8.8.1 以上、MongoDB 3.4.6 以上及 Redis 环境。压缩包共 80 个文件约 1.13MB以 40 个 js 与 9 个 jsx 业务脚本为主体辅以 json 配置、html 模板、png/svg 图标、woff2/ttf 等字体资源以及 Dockerfile、docker-compose.yml、证书与说明文档前后端与代理规则模块划分清晰。目前已有 2627 人学习下载适合想研究代理抓包、数据落库与爬虫工程化的读者参考复用。1. 公众号历史文章抓取从阅读量到评论一套 JavaScript 爬虫能拿到什么做公众号内容分析的人多半遇到过这种局面想复盘某个号三年的选题曲线后台只能一页页翻想统计阅读量中位数手动复制到表格里眼睛都花了。这个wechat_spider项目就是冲着这个痛点来的——用 JavaScript 写的一套抓取工具目标是把公众号的历史文章链接、正文内容、阅读量、点赞量、评论一并拉下来。它适合做内容运营分析、竞品号监测、选题库搭建的从业者也适合想拿真实数据练手的数据方向同学。技术栈是 Node.js 加 JavaScript核心思路是模拟客户端请求、解析返回结构、分页遍历最后落成结构化数据。下面按「它怎么跑起来、参数怎么调、哪里会翻车」的顺序拆一遍。2. 环境搭建与请求链路Node.js 侧要准备什么2.1 运行环境与依赖清单这套工具跑在 Node.js 上建议用 LTS 版本太老的版本对async/await和fetch支持不完整容易在并发请求那一步报奇怪的错。依赖方面常见做法是axios负责 HTTP 请求、cheerio负责 HTML 解析、fs-extra负责文件写入、p-limit控制并发数。这几个包在 npm 上都能直接装不需要额外编译工具链。先把项目目录初始化好装依赖# 初始化项目生成 package.json npm init -y # 安装核心依赖 npm install axios cheerio fs-extra p-limit # 如果要做定时任务再加一个 npm install node-cron装完之后检查node_modules里有没有axios和cheerio这两个目录缺了说明网络或镜像源有问题换源重装即可。这一步看着简单但后面所有请求和解析都依赖它们装不干净后面全是玄学报错。2.2 请求链路从公众号入口到文章列表抓取的核心链路分三段第一段是拿到目标公众号的文章列表入口第二段是翻页遍历历史文章拿到每篇的链接第三段是逐篇请求详情页解析阅读量、点赞和评论。第一段通常需要一个能定位到该公众号的标识参数这个参数在公众号主页的请求里能找到具体字段名各版本可能不同常见做法是从主页 URL 或返回的 JSON 里提取。下面是一个请求封装的骨架把公共头、超时、重试都收在一起const axios require(axios); // 创建带默认配置的请求实例 const client axios.create({ timeout: 10000, // 单次请求超时 10 秒太短容易误判失败 headers: { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: application/json, text/plain, */* } }); // 带重试的请求函数失败后隔 1 秒重试最多 3 次 async function requestWithRetry(url, params {}, retries 3) { for (let i 0; i retries; i) { try { const res await client.get(url, { params }); return res.data; } catch (err) { if (i retries - 1) throw err; await new Promise(r setTimeout(r, 1000)); } } } module.exports { requestWithRetry };这段代码里timeout设 10 秒是个经验值公众号接口偶尔会慢设 3 秒会大量误报失败。重试间隔 1 秒是为了避开瞬时抖动重试次数 3 次足够再多说明目标接口本身有问题硬重试没意义。User-Agent必须带不带的话部分接口直接返回空数据这是最常见的翻车点之一。2.3 分页遍历与去重历史文章列表是分页返回的每页条数有限需要循环请求直到没有下一页。这里要处理两个问题一是翻页游标或偏移量怎么传二是重复文章怎么去重。常见做法是用一个Set存已抓到的文章 ID每次解析后先查重再入库。const seen new Set(); // 已抓取的文章 ID 集合 async function fetchArticleList(accountId, maxPages 50) { const all []; let offset 0; const pageSize 20; for (let page 0; page maxPages; page) { const data await requestWithRetry(/api/article/list, { account_id: accountId, offset: offset, count: pageSize }); const list data?.articles || []; if (list.length 0) break; // 没有数据了提前结束 for (const item of list) { if (seen.has(item.id)) continue; // 去重 seen.add(item.id); all.push(item); } offset pageSize; await new Promise(r setTimeout(r, 500)); // 每页之间歇一下 } return all; }maxPages是保护性上限防止接口异常时无限循环。每页之间停 500 毫秒是降低请求频率太快容易触发限流。offset的递增方式取决于接口设计有的用游标cursor有的用页码page拿到实际返回后按字段调整。去重这步不能省翻页过程中接口偶尔会返回重复项不去重最后统计阅读量会偏高。3. 正文、阅读量、点赞与评论的解析字段怎么取、结构怎么落3.1 详情页解析正文与互动数据拿到文章链接后逐篇请求详情页。正文一般在 HTML 的某个容器里用cheerio按选择器提取阅读量和点赞量有的在页面内嵌的 JS 变量里有的在单独的接口返回中。常见做法是先用cheerio抓正文再用正则从页面源码里匹配阅读量字段。const cheerio require(cheerio); function parseArticleDetail(html) { const $ cheerio.load(html); // 正文取内容容器去掉脚本和样式 const content $(#js_content).text().trim(); // 阅读量、点赞量常藏在页面脚本里用正则匹配 const readMatch html.match(/read_num[]?\s*[:]\s*[]?(\d)/); const likeMatch html.match(/like_num[]?\s*[:]\s*[]?(\d)/); return { content: content, readNum: readMatch ? parseInt(readMatch[1], 10) : 0, likeNum: likeMatch ? parseInt(likeMatch[1], 10) : 0 }; }#js_content是正文容器的常见 ID不同模板可能不同抓不到时先打印 HTML 看结构再改选择器。正则里的read_num和like_num是字段名实际字段名以页面源码为准匹配不到就返回 0避免整条记录失败。正文用.text()会丢掉图片和链接如果需要保留富文本改用.html()再单独清洗。3.2 评论抓取分页与嵌套结构评论通常是单独接口按文章 ID 分页拉取每条评论可能带子评论。抓的时候要处理两层分页主评论翻页和子评论展开。常见做法是先拉主评论再对每条有子评论的项发二次请求。async function fetchComments(articleId, maxPages 20) { const comments []; let page 1; while (page maxPages) { const data await requestWithRetry(/api/comment/list, { article_id: articleId, page: page, limit: 20 }); const list data?.comments || []; if (list.length 0) break; for (const c of list) { comments.push({ user: c.nickname, text: c.content, like: c.like_num || 0, time: c.create_time }); } page; await new Promise(r setTimeout(r, 300)); } return comments; }maxPages同样做上限保护。评论接口对频率更敏感间隔设 300 毫秒以上比较稳。子评论如果也要就在主评论循环里判断c.reply_count 0再发一次请求但要注意嵌套层级别无限展开设个深度上限。3.3 数据落盘JSON 与 CSV 两种格式抓完的数据建议同时落 JSON 和 CSV。JSON 保留完整结构方便后续程序处理CSV 方便直接丢进表格软件看。用fs-extra写入const fs require(fs-extra); async function saveData(articles, outputDir ./output) { await fs.ensureDir(outputDir); // 目录不存在就创建 // 完整数据存 JSON await fs.writeJson(${outputDir}/articles.json, articles, { spaces: 2 }); // 摘要字段存 CSV const header title,readNum,likeNum,url\n; const rows articles.map(a ${a.title},${a.readNum},${a.likeNum},${a.url} ).join(\n); await fs.writeFile(${outputDir}/summary.csv, header rows, utf8); }ensureDir保证目录存在避免首次运行报错。CSV 里标题和 URL 用双引号包住防止内容里的逗号破坏列结构。JSON 用spaces: 2格式化方便人工查看。数据量大时 CSV 拼接可能占内存可以改成流式写入但一般几千篇以内直接拼没问题。4. 避坑与排查抓取过程中最容易翻车的五件事4.1 请求返回空数据或 403现象请求发出后返回空数组或 403 状态码。原因缺少必要的请求头或请求频率过高被临时限制。解决补全User-Agent、Referer等头降低并发数每页之间加延时。如果持续 403说明该入口暂时不可用换时间段再试。4.2 阅读量字段匹配不到现象正文能抓到但阅读量和点赞量全是 0。原因字段名和正则不匹配或数据在异步加载的接口里而非页面源码。解决先打印完整 HTML 搜索read关键字定位真实字段名如果页面源码里没有改用抓包工具找到对应接口单独请求。4.3 翻页死循环或漏抓现象程序一直翻页不结束或中间漏掉几页。原因翻页游标逻辑写错或接口在某一页返回异常导致break提前触发。解决打印每页返回的条数和游标值确认递增逻辑对异常返回做判断区分「真的没有下一页」和「请求失败」。4.4 评论抓取被限流现象评论抓到前几页后开始返回空或报错。原因评论接口频率限制比文章列表更严。解决把评论请求的间隔调到 500 毫秒以上并发降到 1必要时分批分时段抓。4.5 数据重复或统计偏差现象同一篇文章出现多次阅读量统计偏高。原因翻页过程中接口返回重复项去重没做或做得不彻底。解决用文章 ID 做Set去重入库前再查一次统计前先按 ID 去重再算中位数和均值。5. 进阶技巧把抓取结果变成可复用的选题库抓到数据只是第一步真正省时间的是把结果整理成能直接用的选题库。我一般会做三件事按阅读量分档、按时间看趋势、按关键词聚类。分档用简单的分位数就行不用上复杂模型function analyzeArticles(articles) { // 按阅读量排序后取分位数 const sorted [...articles].sort((a, b) a.readNum - b.readNum); const q (p) sorted[Math.floor(sorted.length * p)]?.readNum || 0; const low q(0.25); // 25 分位 const mid q(0.5); // 中位数 const high q(0.75); // 75 分位 // 分档统计 const buckets { low: 0, mid: 0, high: 0 }; for (const a of articles) { if (a.readNum low) buckets.low; else if (a.readNum high) buckets.mid; else buckets.high; } return { low, mid, high, buckets }; }这段代码把文章按阅读量分成三档q(0.25)是 25 分位值低于它的算低阅读高于 75 分位的算高阅读。分档之后就能看出这个号的内容基本盘在哪哪些选题属于爆款区间。趋势分析按月份分组算均值关键词聚类用简单的词频统计就够不用上 NLP 模型。验证抓取结果是否可靠我习惯抽三篇手动核对阅读量和评论数对不上就回头查解析逻辑。这个习惯是从一次翻车来的——当时正则匹配到了页面里另一个同名字段导致所有阅读量都偏大抽检才发现。从那以后我每次抓完都强制走一遍抽检三篇对不上就全量重跑。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/17 21:16:23

2026降AI工具实测:免费与付费效果差距全解析

“降AI工具”这个词,最近在内容创作圈子里出现的频率越来越高了。起因也简单:主流AI检测器越做越强,很多人发现直接拿大模型生成的文章,被检测出AI痕迹的概率相当高。于是,怎么让机器判断“这内容像是人写的”&#xf…

2026/10/17 21:16:23

Vue3集成Dify iframe的会话隔离与身份锚定方案

简介:本资源是一份面向Web前端开发者与Vue技术实践者的实战解决方案,聚焦于在Vue应用中通过Iframe安全嵌入Dify对话窗口并实现用户级会话隔离。针对Dify原生嵌入不支持传入用户ID、导致历史对话丢失的痛点,方案利用localStorage动态写入用户t…

2026/10/17 21:16:23

Flutter适配OpenHarmony:宿舍系统欢迎区域跨端实战解析

把 Flutter 跑在 OpenHarmony 上做应用,很多人一听就觉得“又要从零学一套新的 UI 框架了”。我接某高校宿舍管理系统的跨端需求时也是这个反应:移动端要跑 Android,楼栋自助终端要跑 OpenHarmony,最怕的就是同一套界面写两遍。结…

2026/10/17 22:36:37

从互联网+到人工智能+:数据、模型与工程落地全解析

简介:这份文档围绕从“互联网”迈向“人工智能”的技术演进展开,面向计算机专业学生、技术从业者及关注产业变革的读者,帮助梳理互联网到人工智能的转型脉络与产业竞争格局。资源包内含1个docx文件,大小约135KB,以文字…

2026/10/17 22:36:37

Theano老框架Win10下CUDA10.2环境搭建踩坑实录

很多年前的老项目要在新机器上重新跑起来,几乎是每个搞过深度学习的人都会遇到的噩梦。我这次遇到的,是一个依赖Theano框架的存量模型,机器是Win10,显卡驱动可以装到CUDA 10.2,但Theano官方早在2017年就停止维护&#…

2026/10/17 22:36:37

7天深度学习框架入门实战:从环境搭建到模型部署全流程经验分享

我一直觉得,入门一门新技术最怕的不是看不懂原理,而是打开官方文档却不知道第一行代码该写在哪。上个月我报名了某国产深度学习框架的7日入门培训课程,用一周时间把之前零散的深度学习知识重新串了起来,这篇文章就结合我的实际学习…

2026/10/17 22:36:37

OpenResearch:本地优先的研究工作流与CLI工具链实践

1. 项目概述:一个被误读的“OpenResearch”到底是什么?最近在技术社区和开发者群聊里,“OpenResearch”这个词出现频率陡增,但奇怪的是,几乎没人能说清它具体指什么。有人把它当成某个新发布的AI研究平台,有…

2026/10/17 22:31:36

Coinbase实操指南:账户安全、交易避坑与资产托管全解析

加密货币交易所在大众视野里一直带着神秘感,有人把它当赚钱工具,有人把它视为高风险雷区。作为圈内老兵,我可以明确告诉你:所谓“风险”和“机遇”之间,差的往往是基本功。我个人用过不少平台,也从踩坑里积…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/17 14:20:58

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑