如何批量下载微信公众号文章并保留 100% 原版式wechat-article-exporter 实战指南【免费下载链接】wechat-article-exporter一款在线的 微信公众号文章批量下载 工具支持导出阅读量与评论数据无需搭建任何环境可通过 在线网站 使用支持 docker 私有化部署和 Cloudflare 部署。 支持下载各种文件格式其中 HTML 格式可100%还原文章排版与样式。项目地址: https://gitcode.com/gh_mirrors/we/wechat-article-exporter批量下载微信公众号文章、还原原版式排版、导出阅读量与评论数据还能用 Docker 或 Cloudflare 一键私有化部署——这套能力都装在wechat-article-exporter里。本文不堆代码按你想干一件什么事的顺序把它的抓取原理、数据流向、导出机制和三种部署方式讲清楚让你读完就能判断它值不值得托管、又该怎么跑起来。一次扫码为什么能一直抓别人的文章核心机制是一句话你扫自己的公众号登录一次它就借你这个合法身份去调公众号后台的搜索并引用其他公众号文章接口。微信公众平台在写文章流程里允许运营者搜索别的公众号历史文章并引用背后就是searchbiz按关键字搜公众号和appmsgpublish按公众号fakeid分页拉历史文章这两个接口。本项目把它们程序化了你用微信扫一个二维码登录自己的公众号登录流程见 server/api/web/login/服务端把微信返回的set-cookie存进自己的 CookieStore再给浏览器下发一个唯一的auth-key。之后所有对微信平台的请求都由 server/utils/proxy-request.ts 统一转发——它原样带上Referer、Origin和一个伪装成浏览器管理端的User-Agent并把 cookie 存回、回传。这里有个容易被忽略但很关键的细节登录态是有保质期的。cookie 在 KV 存储里只保留 4 天expirationTtl: 60 * 60 * 24 * 4见 server/kv/cookie.ts到期就得重新扫码。接口返回200003时程序会判定会话过期并清空当前登录账号见 apis/index.ts。所以扫一次码不是扫一次管一年而是扫一次、用四天。⚠️ 客观说一句这是借用你本人账号的会话去调接口是否合规取决于微信平台条款本项目本身不做背书请自行评估。从找到一个号到拿到一篇正文的数据路径登录只是拿到钥匙真正干活的是搜索 → 列表 → 正文这条流水线它分三步把数据搬进你的浏览器本地。第一步是搜索公众号。前端调searchbiz按关键字返回公众号列表你选中一个添加为管理对象程序随即自动同步它的第一页文章。注意公众号的翻页结束判断和普通文章不一样只有当第一页结果就少于 5 个、或后续页彻底为空时才算抓完见 apis/index.ts 的getAccountList。第二步是分页拉文章列表。appmsgpublish按fakeid一页 20 条地返回历史文章ARTICLE_LIST_PAGE_SIZE 20见 config/index.tsbegin从 0 开始递增当某次返回 0 条就表示加载完毕。每次拉到的列表都会写进本地 IndexedDB 缓存updateArticleCache这样下次不用重复请求接口。列表页基于 ag-grid 实现支持按作者、标题、发布时间、原创标识、所属合集等维度过滤。第三步是抓正文 HTML。对每篇mp.weixin.qq.com/s/xxx链接程序通过代理节点抓取正文并写入缓存。这里有个贴心的修复单篇链接本身推不出公众号fakeid下载器会先把 HTML 抓下来、从window.cgiDataNew里解析出真实的bizuin再回填fixSingleFakeidTask见 utils/download/Downloader.ts所以你在单篇页面直接粘链接也能下。抓到正文后程序还会做状态校验已下架/作者删除的文章、内容违规的页面都会有专门的处理与标记仓库samples/目录里就存了作者已删除内容违规等样例页避免把无效内容当成有效正文存进去。消息类型也不限于普通图文——ITEM_SHOW_TYPE映射覆盖了普通图文0、视频分享5、音乐分享6、图片分享8、文本分享10、文章分享11、短文17等见 config/index.ts意味着目标号发的是图片分享或文本分享时同样能正常导出。正文只是骨架阅读量、评论为什么必须抓包列表和正文能直接拿到但阅读量、点赞、分享、留言这些数据藏在需要额外凭证的接口里这就是项目里credentials 机制存在的原因。credentials 是一组微信接口鉴权参数包含biz公众号标识、uin用户标识、key、pass_ticket、wap_sid2、appmsg_token和完整cookie字段定义见 types/credential.d.ts。它们不是登录扫码能拿到的得靠抓包页面提供抓取 Credentials面板components/global/CredentialsDialog.vue支持两种抓包方式——配套监听程序或直接用仓库自带的 mitmproxy 插件mitmdump -s credential.py -q把手机代理指到这个抓包端点、打开微信阅读一篇目标文章插件就会捕获并回传上面那组参数。拿到后下载器会在抓取阅读量时把pass_ticket和wap_sid2拼进 cookie 重新请求一遍文章 HTML再从window.cgiDataNew的appmsg_bar_data里解析出read_num阅读、old_like_count赞、share_count分享等数值写入元数据缓存。评论与评论回复则走appmsg_comment接口分页拉取每页 100 条靠buffercontinue_flag翻页并发会被强制降到 2 以避开风控。⏰ 两个限制要心里有数key默认按25 分钟管理CREDENTIAL_LIVE_MINUTES 25过期就得重新抓包付费文章的完整正文也必须带 credentials 才能取到is_pay_subscribe 1时自动切换为带凭证请求。六种导出里HTML 为什么能做到 100% 还原拿到正文后导出由 utils/download/Exporter.ts 统一调度支持 html / json / excel / txt / markdown / docx 六种格式外加 PDF。它们的实现差异很大用一个表看清楚格式实现方式还原度 / 备注html本地化资源 重写 DOM打包图片与样式100% 还原排版excelexceljs 生成 .xlsx含阅读/点赞/原创/合集等列json序列化整篇文章对象可含正文与留言txt / markdown渲染后转纯文本 / Turndown去样式、去广告docx浏览器端 html-docx-js适合二次编辑HTML 这条管线最复杂分三步。先提取资源解析已缓存正文收集所有img、link relstylesheet以及 CSS 里background/background-image: url(...)三类远程地址。再并发下载这些图片与样式文件写入资源缓存。最后本地化重写把远程路径替换成./assets/uuid.ext同时删掉顶部广告、底部推荐、二维码和script等无用 DOM移除#js_content上隐藏正文的style从oriCreateTime渲染真实发布时间、渲染 IP 属地与标题已修改标记把卡券、语音、视频、小程序等特殊元素降级为[图片]、[视频]、[小程序]等文本占位并可选地渲染阅读量与留言。最终每篇文章是一个index.htmlassets/目录双击即可离线浏览排版与原文一致。Markdown 则走 TurndownATX 标题、-列表、fenced 代码块并显式移除style/script/link等标签避免 CSS 与 data URI 图标混进正文。导出目录名支持模板变量例如${YYYY}-${MM}-${DD}_${title}可替换${account}、${title}、${aid}、${author}及年月日时分exportDirName逐一替换见 utils/download/Exporter.ts无法用于文件名的字符会被过滤。批量下载合集时程序先逐篇抓正文再用 JSZip 按日期 标题目录打包成 zip见 composables/useBatchDownload.ts。并发、代理、重试大批量抓取怎么不被风控单次抓取靠代理、批量抓取靠调度。所有下载任务统一由 utils/download/BaseDownloader.ts 管理采用 Promise.race 并发模型维护一个进行中的任务集合在不超并发上限且队列非空时持续启动新任务。默认参数如下见 utils/download/constants.ts参数默认值作用timeout20 秒单资源下载超时maxRetries3 次失败重试次数maxFailures3 次连续失败进入冷却的阈值cooldownPeriod30 秒代理冷却时间代理调度由 utils/download/ProxyManager.ts 负责每次优先选失败次数最少、最近未使用的代理某代理连续失败达到阈值就进 30 秒冷却池若全部在冷却则回退到最早使用的那一个并重置计数。下载失败采用指数退避2^n秒延迟重试。并发数默认按代理数量自动计算bestConcurrencyCount宁可慢一点也不触发风控。代理来源分两级优先用你在设置 → 代理节点里填的私有代理列表需以http/https开头支持自动拼接?url...可配Authorization没配置时回退到内置的公共代理池config/public-proxy.ts由 6 个域名 × 16 个二级子域共 96 个节点组成。公共池的额度是有限且需要抢的追求稳定就建议填自己的节点。三种跑起来的方式在线、Docker、Cloudflare如果你只是想试试直接用在线站点即可无需装任何东西。想完全掌控数据与请求就自托管——它有两条路子差异主要在存储与 PDF 支持。Docker 私有化是功能最全的方式。仓库提供 Dockerfile两阶段构建构建层node:22-alpine锁 Yarn 1.22.22 装依赖并nuxt build构建时设NITRO_KV_DRIVERfs、NITRO_KV_BASE.data/kv用本地文件系统模拟 KV运行层node:22-slim通过 apt 装 Chromium、Noto CJK 中文字体与 CA 证书设PUPPETEER_EXECUTABLE_PATH/usr/bin/chromiumPuppeteer 直接复用系统 Chromium、跳过下载以非 root 的node用户监听0.0.0.0:3000。构建并运行# 构建镜像版本自动取 package.json yarn docker:build # 挂载 KV 目录持久化登录态与文章缓存否则容器重建后登录失效 docker run -d -p 3000:3000 -v /本地目录:/app/.data/kv \ ghcr.io/wechat-article/wechat-article-exporter:version正因运行层内嵌了 Puppeteer ChromiumDocker 是唯一支持 PDF 导出的自托管方式本地nuxt dev亦可KV 数据务必挂卷免得重建容器后登录态丢失。Cloudflare Workers走的是轻量路线配置在 wrangler.toml。要点yarn build:cf用NITRO_PRESETcloudflare_moduleNITRO_KV_DRIVERcloudflare-kv-binding构建KV 绑定名固定为KV与 nuxt.config.ts 中storage.kv.binding保持一致cookie:*与member:*数据存在 KV 里构建后wrangler deploy上线。这里有个坑已在配置里规避——Cloudflare 驱动下不能再设base否则键会变成.data/kv:member:xxx导致读不到数据。同时 nuxt.config.ts 开了experimental.wasm让 QuickJS-WASM 沙箱在主 Worker 内执行 cgi 脚本无需 Dynamic Workers能显著降低成本。 两种部署要分清一件事Workers 环境没有 PuppeteerPDF 导出不可用而公开的会员/限速层NUXT_PUBLIC_MEMBERSHIP_ENABLED只作用于官方托管站点fork 私有部署时membership.enabled默认关闭无限速开放 API 的 markdown / text / json 格式也全部放开。结语与许可一句话记住它的设计用一次扫码换取一个完整归档通道把抓取、渲染、资源打包全部下沉到浏览器端File System Access API服务端只负责转发与存 cookie从而保持轻量也让 HTML 导出能做到 100% 还原排版。抓 credentials、维护代理池、跑 QuickJS 沙箱这三件事分别对应互动数据稳定抓取结构化解析三条能力线理解它们就理解了整个项目。项目采用MIT 许可证可自由使用、修改与二次分发作者承诺不利用你扫码的账号做公共爬虫、不存在账号池你抓到的内容版权归原作者所有请合理使用。它同时致谢了 Deno Deploy、Cloudflare Workers 的免费托管以及 WeChat_Article 项目提供的原理思路。若需自行 clone仓库地址为https://gitcode.com/gh_mirrors/we/wechat-article-exporter。【免费下载链接】wechat-article-exporter一款在线的 微信公众号文章批量下载 工具支持导出阅读量与评论数据无需搭建任何环境可通过 在线网站 使用支持 docker 私有化部署和 Cloudflare 部署。 支持下载各种文件格式其中 HTML 格式可100%还原文章排版与样式。项目地址: https://gitcode.com/gh_mirrors/we/wechat-article-exporter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?