微信公众号文章采集与导出系统实战:Python爬虫与批量归档
简介这是一款面向个人学习者与技术爱好者设计的微信公众号文章数据采集与导出系统旨在解决公开内容批量获取、结构化存档与多维分析的实际需求。资源包共131个文件涵盖34个TypeScript核心逻辑文件、24个Vue前端组件、29张PNG界面素材及10份Markdown说明文档辅以JSON配置、CSS样式与Docker部署相关脚本整体压缩后仅12.3MB轻量易部署。已有199人下载学习适用于需研究新媒体内容生态、训练NLP语料或构建自有知识库的技术实践者。用户可直接运行免环境依赖的HTML导出模块完整保留图文排版通过关键词检索公众号、标题筛选文章、专题批量下载并提取阅读量、留言及转发等交互数据项目支持Docker容器化部署与定时自动订阅开放API便于二次集成配套说明文档详述抓包授权等关键操作步骤。 做这个系统的念头最初来自一个很现实的需求个人知识库整理。我平时会收藏大量公众号文章但公众号生态相对封闭文章一旦被作者删除、迁移或者账号注销内容就再也找不回来。零散收藏不仅查起来费劲更怕哪天想引用的时候发现链接已经失效。于是我就萌生了一个想法能不能自己写一套工具把需要长期保存的文章完整抓下来按我自己的规则归档、检索、二次分发这套“微信公众号文章数据采集与导出系统”就是这么诞生的。这个概念其实很简单通过微信公众平台或微信客户端可访问的接口把指定公众号的历史文章列表抓下来再逐篇获取正文、作者、发布时间、封面图等数据最后批量导出成可离线阅读的格式。整个过程涉及登录态维护、列表抓包、正文解析、图片处理、批量渲染等多个环节每一环都有不少坑。这篇内容就围绕我在实际搭建过程中踩过的坑、试过的方案、最终跑通的流程展开适合有Python基础、想自己搭建内容归档工具的朋友参考也适合团队用来做公众号内容资产沉淀、舆情备份、行业报告素材收集等场景。1. 为什么需要自建一套公众号文章采集导出工具1.1 需求场景从收藏到归档的痛点公众号文章最大的问题在于“所有权”不在读者手里。你收藏了十篇深度好文可能某天作者因为转载纠纷删文或者账号被平台回收内容就彻底消失。我们在做行业调研时尤其依赖公众号内容一个细分领域往往有一批垂直账号持续输出高质量文章这些内容分散在不同账号下没有统一入口也没法像网页那样直接离线保存。自建系统解决的就是三件事全量备份、统一检索、格式转换。全量备份是把某账号下的历史文章尽可能完整地保存下来包括正文、图片、原文链接、发布时间等关键信息统一检索是把所有采集到的文章放入一个本地库按标题、作者、时间、关键词都能快速筛选格式转换是把网页内容变成Markdown、PDF、Word等适合阅读、分享和存档的格式。这三个需求听起来不复杂但真正动手做的时候光“拿到文章列表”这一步就够折腾。1.2 方案选型现成工具与自己写的边界市面上其实已经有一些在线的公众号文章采集工具和浏览器插件简单场景下确实能用但局限性很明显。免费的通常有数量限制每天只能导几篇收费的工具格式固定导出的样式是别人定好的不能按自己的需求调整更重要的是在线工具需要把公众号链接复制过去遇到批量抓取几十个账号的时候就非常低效。我自己也尝试过纯手工方式也就是直接在PC端微信里一篇文章一篇文章地复制粘贴再统一排版但效率太低了。整理一百篇文章可能要花一整天而且复制过来的图片链接经过防盗链处理后经常无法显示。最终我决定自建一套半自动化的采集系统核心思路是借助PC端微信登录后的会话数据拿到公众号列表接口的参数用Python脚本定时抓取然后做数据清洗和批量导出。这样既能绕过内容平台对第三方登录的种种限制又能完全掌控数据格式归档效率大幅提升。2. 核心设计拆解登录态、列表接口与正文解析2.1 登录态与Cookie维护公众号后台和微信客户端内部有一套完整的内容分发接口其中最关键的是“公众号文章列表”接口这类接口通常需要携带有效的身份凭证才能访问一般表现为Cookie或Token。自建采集系统的第一关就是拿到一个稳定可用的登录态。我在实际开发中选用的是PC端微信的网页调试方式在电脑上登录微信打开某篇公众号文章通过抓包工具观察请求就能看到文章列表接口的请求参数和Cookie。把这个Cookie抽出来放到采集脚本的请求头里就能以当前微信账号的身份去请求指定公众号的历史文章列表。这里有一个容易被忽略的细节Cookie有有效期可能几小时也可能几天取决于账号活跃度和操作频率。建议把Cookie保存在独立的配置文件中脚本启动时读取过期时重新抓包更新不要写死在代码里否则每换一次环境都要改代码。另一个关键是User-Agent和Referer头。微信接口虽然不像普通网站那样严格校验User-Agent但为了减少风控误伤最好保持和PC端微信一致的浏览器标识。Referer字段通常要指到公众号的某篇文章页或主页否则部分接口可能返回异常数据。我自己是把所有请求头都封装成了一个独立函数抓包一次后统一配置后续脚本只负责调函数省心很多。提示微信的登录凭证属于个人敏感信息任何情况下都不要把Cookie明文提交到公共代码仓库。建议用环境变量或者本地配置文件管理并加入.gitignore忽略规则。2.2 列表接口与数据字段公众号文章列表的接口地址在不同版本下会有变化但核心逻辑一致传入账号标识fakeid和分页偏移量offset返回该账号下历史文章的JSON数据。这里面的fakeid可以从公众号主页或会话列表请求里解析出来通常是profile页面返回的一串加密字符串。列表返回的数据里每条文章记录一般包含这些核心字段title文章标题digest摘要link文章详情页链接update_time发布时间戳cover封面图地址publish_time有时候会单独返回发布时间相关字段如果只看列表不抓正文系统就只能算一个索引工具离“导出系统”还差很远。所以在拿到列表之后还需要遍历每篇文章的link进入详情页解析正文。这里需要注意详情页的内容是HTML格式直接存下来会出现大量无用标签、内联样式和动态加载的脚本必须做HTML清洗提取正文核心内容。2.3 正文HTML清洗与图片处理公众号文章的正文HTML总体比较规整但夹杂了大量的section、p、span以及各种内联样式。如果直接保存导出的Markdown或PDF会非常凌乱。我采取的方案是先用Python的BeautifulSoup或lxml解析HTML按正文容器定位一般是idjs_content的节点再递归去除脚本、样式标签和空节点提取纯文本的同时保留必要的段落和图片引用。图片处理是另一个大坑。公众号正文里的图片默认使用mmbiz.qpic.cn域名这些图片在外部网络环境直接访问时需要附带正确的Referer否则很容易出现防盗链拦截。采集时最好统一把图片下载到本地目录并把正文里的图片链接替换成相对路径。这样导出的Markdown、PDF或HTML即便脱离网络也能完整显示图片。下载图片时建议设置合理的超时时间比如15秒并针对下载失败的内容做重试机制。3. 实操落地环境准备与关键代码实现3.1 环境准备依赖安装与目录规划项目落地前先把Python环境装好。我本机用的Python 3.10依赖库清单如下pip install requests beautifulsoup4 lxml markdown pdfkit python-docx这几个库的作用分别是requests处理HTTP请求BeautifulSoup4加lxml解析HTMLmarkdown把清洗后的内容转成Markdownpdfkit负责把HTML转PDFpython-docx用来生成Word文档。如果需要把Markdown进一步转成带样式的HTML可以再加一个markdown扩展插件。目录结构我通常这样规划wechat_archiver/ ├── config/ │ ├── cookies.json │ └── target_accounts.txt ├── download/ │ └── images/ ├── output/ │ ├── html/ │ ├── markdown/ │ ├── pdf/ │ └── word/ ├── scripts/ │ ├── fetch_list.py │ ├── fetch_article.py │ ├── parse_content.py │ └── export.py └── logs/配置文件单独放方便多环境切换下载的图片和输出文件分开管理方便后续打包备份日志目录用来记录每次采集的结果排查问题时非常有用。3.2 核心采集流程列表分页与文章详情请求先实现列表分页抓取。公众号列表接口一次最多返回一定数量的文章通常需要多次请求每次把偏移量累加直到返回的数据条数为空才停止。以下是核心逻辑的简化示例import requests import json def fetch_article_list(session, fakeid, offset0, count10): url https://mp.weixin.qq.com/cgi-bin/appmsg params { action: list_ex, begin: offset, count: count, fakeid: fakeid, type: 9, # 9表示图文消息 query: , token: session.token, lang: zh_CN, f: json, ajax: 1, } headers { User-Agent: session.user_agent, Referer: https://mp.weixin.qq.com/, Cookie: session.cookie_str, } resp requests.get(url, paramsparams, headersheaders, timeout20) data resp.json() if data.get(ret) 0: return data.get(app_msg_list, []), data.get(app_msg_cnt, 0) else: return [], 0这里有几个参数需要说明一下。fakeid是公众号的数字标识type9表示抓取图文类型count是单次请求数量建议不超过10太大容易触发风控。token字段在公众平台的请求里经常出现它不是Cookie本身而是和Cookie联动的一个参数一般从Cookie中解析出来。拿到文章列表后对每一篇的link发起详情请求再用parse_content.py里的清洗逻辑提取正文。详情页的响应通常是完整HTML我建议不要直接用正则去抠内容因为公众号编辑器会生成各种奇怪的嵌套结构正则很容易漏。用BeautifulSoup按idjs_content定位正文容器最稳妥。3.3 批量导出从Markdown到PDF、Word清洗并保存好文章正文后导出环节才是真正体现“系统”价值的地方。我默认会把每篇文章同时存一份Markdown和一份HTML然后按需批量生成PDF或Word。Markdown格式适合自己用编辑器阅读、做知识库索引HTML格式适合快速预览和线上分享PDF和Word主要给同事或客户交付。导出PDF时我用的方案是先把正文塞到一个带基本样式的HTML模板里再用pdfkit调用本地安装的wkhtmltopdf来渲染。这里有个坑如果本机没有安装wkhtmltopdf直接调用会报错。安装时要注意把可执行文件的路径加到系统PATH里否则pdfkit找不到程序。import pdfkit options { encoding: UTF-8, page-size: A4, margin-top: 15mm, margin-bottom: 15mm, margin-left: 15mm, margin-right: 15mm, enable-local-file-access: , } html_content f html headmeta charsetutf-8/head body h1{article[title]}/h1 p classmeta作者{article[author]} 发布时间{article[publish_time]}/p hr {article[content_html]} /body /html pdfkit.from_string(html_content, output_path, optionsoptions)导出Word相对简单python-docx可以逐段添加标题和正文段落。需要注意图片的插入方式本地图片路径必须真实存在跑批量导出时最好先检查图片是否下载完整否则生成的Word文档会缺图。3.4 增量抓取与断点续传全量抓取第一次跑完之后后续再抓同一批账号就只需要关心新增文章没必要重新抓全量。我的做法是在本地维护一个简单的数据库文件记录已抓取文章链接的哈希值。每次列表请求返回后先检查链接是否在库里已经在库里的直接跳过新的再进入详情抓取和导出流程。import hashlib import json def is_duplicate(article_url, seen_pathseen.json): url_hash hashlib.md5(article_url.encode(utf-8)).hexdigest() try: with open(seen_path, r, encodingutf-8) as f: seen json.load(f) except FileNotFoundError: seen {} if seen.get(url_hash): return True seen[url_hash] True with open(seen_path, w, encodingutf-8) as f: json.dump(seen, f, ensure_asciiFalse, indent2) return False这样每次跑完一批就只更新增量部分耗时从全量的几十分钟降到几分钟。另外断点续传也很重要尤其是文章数量达到几百上千篇时中间可能因为网络波动、风控拦截导致脚本中断。我每次抓取的文章链接和成功状态都会实时写入日志脚本重启后可以直接从日志中最新的成功位置继续不用从头开始。4. 排障实录常见问题与解决办法4.1 文章列表接口返回空数据抓包时接口正常但脚本跑起来却经常拿不到数据这种问题我遇到过好几次。首先要确认fakeid是不是有效的公众号主页的fakeid有时候会包含URL编码直接复制到参数里可能出错。其次要检查Cookie是否过期接口返回的JSON里ret字段如果是非零值大概率是凭证失效重新抓包更新Cookie即可。还有一个容易忽略的点请求的User-Agent和Referer必须和抓包时一致尤其是Referer。如果列表接口要求必须从公众号主页或某篇文章页跳转进入而你的脚本里缺失了这个头部服务端可能直接拒绝返回数据。我在代码里专门封装了一个make_headers函数把Referer集中管理排查问题时可以快速切换。4.2 登录态频繁失效Cookie隔几个小时就失效是采集系统最大的痛点。我在实践中发现频繁请求接口或者短时间内并发太多会加速Cookie失效。建议控制请求频率抓完一篇文章后间隔2到5秒再抓下一篇模拟真实浏览节奏。另一方面不要在采集当天反复重新登录微信网页端登录状态一旦改变之前的Cookie就全部作废。如果Cookie还是频繁失效可以考虑把关键账号的采集任务分散到不同时间段执行比如每个小时跑一批而不是一口气全部跑完。这样既能降低风控风险也能让采集系统的整体稳定性明显提升。4.3 图片下载失败与防盗链图片下载失败最典型的原因就是防盗链。直接使用requests.get请求图片URL时如果请求头里没有带正确的Referer微信的图片服务器很可能返回403。解决方案是在下载图片时也带上文章页的Referer。另外某些历史文章的图片可能已经被源站清除下载时会返回404这部分需要跳过并在日志里记录不要因为一张图失败就终止整个任务。def download_image(img_url, save_path, referer): headers { User-Agent: Mozilla/5.0 ..., Referer: referer, } resp requests.get(img_url, headersheaders, timeout15) if resp.status_code 200: with open(save_path, wb) as f: f.write(resp.content) else: raise RuntimeError(fdownload failed: {resp.status_code})4.4 导出PDF或者Word时遇到样式错乱经常出现的情况是Markdown里看内容是正常的但转成PDF后段落间距异常、代码块底色丢失或者中文字体之间出现多余空格。原因多半是HTML模板里的CSS样式不够完善比如没有设置font-family导致wkhtmltopdf用了默认英文字体来渲染中文。建议在HTML模板里显式加入中文字体配置比如微软雅黑、思源黑体并且对正文、图片、引用、代码块都定义好基础样式。Word版本如果图片错位通常是因为文章里有多个图片并列python-docx默认按顺序插入没有处理浮动布局解决办法是统一把图片设置为居中显示并限制最大宽度。5. 合规边界与长期维护建议5.1 合规注意事项做数据采集合规这根弦一定要绷紧。公众号文章虽然内容公开但版权仍然属于原作者。自建采集导出系统建议仅用于个人备份、学习研究、内部分析等合法用途不要将采集到的内容批量打包后公开传播更不要用于商业牟利。尤其是涉及付费专栏、原创声明、授权转载等内容的文章使用时必须尊重原作者权利。另外采集行为本身要控制在合理频率内不要对目标平台造成明显压力。平台有风控机制是正常的作为采集方更应该有自律意识控制并发数做好限速避免给他人和自己的账号带来不必要的风险。5.2 长期维护建议这套系统不是一次性开发完就万事大吉它本质上是一个需要长期维护的数据管线。我自己的维护节奏是这样的每周检查一次Cookie是否失效每个月抽查几篇文章确认正文解析规则仍然正确每次微信客户端升级后观察抓包结果是否有变化。如果正文结构出现新的嵌套标签不需要重写解析器只要往清洗规则里补充对应的节点处理逻辑就行。日志是维护系统最重要的辅助手段。每次采集任务的开始时间、结束时间、成功文章数、失败原因都应该记录下来方便回溯。我还加了一个简单的数据统计脚本每周输出一次采集总量和成功率低于某个阈值时自动排查接口是否变更这样做能让你对系统状态心中有数不至于等到内容断更才发现问题。最后分享一个我自己总结的小原则能用配置文件解决的问题就不要写死在代码里能增量采集的地方就不要每次全量跑。这套系统跑了大半年整体稳定偶发问题基本集中在登录态变化上提前做好Cookie更新和异常告警比什么都重要。如果你也在规划类似的内容归档工具建议先跑通一个公众号的完整流程再逐步扩展到批量账号这样磨出来的系统会扎实很多。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →