Back to skills
extension
Category: Data & AnalyticsNo API key required

公众号多图转PDF

抓取微信公众号文章的正文图片并合成为 PDF 文件(只转图片,不提取文字,纯文字推文无法转换)。当用户给出 mp.weixin.qq.com 链接(单篇或目录索引页),要求"下载/保存/导出/转为 PDF"、或按章节/单元整理公众号图文内容时使用。支持单篇模式和目录页批量模式(自动解析全部正文链接、按单元/章分文件夹归档)。触发特征:公众号文章转 PDF、微信文章图片下载、把这篇公众号存成 PDF、下载这个公众号的XX图片内容。

personAuthor: user_cce12b87hubcommunity

Wechat To Pdf

概述

抓取微信公众号文章的正文图片(正文多为长图/书页扫描图),按序合成为 PDF。只转图片,不提取文字——图文混排文章的纯文字段落不会进 PDF;适用于图片型内容(如书本扫描页、图文长图)。核心难点是微信反爬:普通浏览器 UA 会被拦截返回 17KB 验证页,必须使用微信内置浏览器 UA 才能拿到完整正文。

触发说明:本技能只处理微信公众号文章,触发基于能力意图("公众号图片内容 → PDF")。范围词(如"第一章")只是批量模式的一个过滤参数,不作为技能识别依据。

两种使用模式

模式一:微信单篇

用户给一个文章链接(https://mp.weixin.qq.com/s/xxxx 或带 __biz/mid/idx/sn 参数的完整链接):

python scripts/wechat_to_pdf.py --url "文章链接" --out "输出目录" [--name "自定义文件名"]

文件命名规则(单篇):识别这篇文章的书信息来命名,书的信息一般见于公众号标题(程序自动读取 og:title,通常即书名)。优先级——① --name 自定义名;② 文章标题(自动清洗掉"(章节目录索引)"等说明性后缀);③ 链接标识兜底(如短链 AbCdEf123),保证不会生成无意义文件名。

模式二:微信目录页批量(核心场景)

用户给一个目录索引页链接(一篇文章里列了所有章节的超链接),并指定范围:

python scripts/wechat_to_pdf.py --index "目录页链接" --scope "第一章" --out "输出目录"
  • 自动解析目录页全部正文链接,按 (mid, idx, sn) 去重
  • --scope 传章节关键词(如"第一章"、"单元一"),只处理匹配条目;不传则处理全部
  • 文件夹与文件都按目录各层级命名:从目录页的单元/章标题自动推导两级文件夹,文件用目录页里该条目的标题(节名/章名)命名,如 输出目录/第一单元 单元名/第一章 章名/第一节 节名.pdf;整体未识别出目录层级时所有 PDF 平铺在输出根目录,个别条目缺单元/章信息时归入「其他」文件夹
  • 文件命名规则(批量):文件名 = 目录条目标题(目录页里该链接的锚文本)→ 文章自身标题(书的信息)→ mid_idx 兜底,保证与目录层级一一对应
  • 完成后在输出目录生成 _report.txt 核对报告(成功/失败明细,失败按原因分类)
  • 其他参数:--max N 限流调试、--retry N 失败自动重试轮数(默认 1 轮)、--skip-existing 跳过已有文件

自检机制(内置)

  1. 抓取诊断修复:抓取失败时按策略池自动换组合重试(桌面/苹果/安卓微信 UA、完整请求头、文章自身作为来源页共 5 套),并诊断失败原因(反爬拦截 / 链接失效 / 网络错误 / 内容异常),报告里写明原因与已尝试策略。
  2. PDF 完整性校验:每篇合成后立即用 pypdf 校验——文件可打开、页数 > 0;校验失败视为该篇失败并记录原因。
  3. 失败自动重试:批量模式跑完后,失败项自动进入下一轮重试(默认 1 轮,--retry 0 关闭,--retry 2 加一轮)。
  4. 核对报告_report.txt 含总体统计、失败原因分类、逐篇明细,方便快速定位。
  5. 断点续传--skip-existing 跳过已生成 PDF,中断后重跑不会重复下载。

关键实现要点(勿改动,实测踩坑)

  1. UA 必须用 MicroMessenger:脚本内 UA 常量已内置,普通浏览器 UA 会命中反爬验证页(verify.js),表现为 len(r.text) < 20000 且无 js_content
  2. 图片在 data-src:正文图片是懒加载,真实地址在 <img data-src="...">,不是 src。且只取 js_content 容器内的,自动排除头部/尾部(头像、封面、推荐、广告位)。图片域名判断必须用 mmbiz.qpic.cn 全域名,不能只匹配子串 mmbiz——否则会混入 res.wx.qq.com/mmbizappmsg/...js 等 JS 文件引用导致下载失败。
  3. 新版 JS 渲染排版兜底:部分公众号正文由 JS 动态渲染(HTML 里无静态 js_content 容器、图片藏在脚本数据里)。容器内提取为空时,回退从 cdn_url: 'https://mmbiz.qpic.cn/...' 数据字段提取正文图片,并排除封面(og:image/twitter:image)与头像,避免把非正文图合成进 PDF。
  4. 广告/组件过滤:正文内如混入广告位、小程序卡片、音视频组件(特征:class/data-type 含 mp-commonjs_sg_barqqmusicmp-miniprogramjs_ad),先整块切除再提取图片。
  5. 链接反转义:目录页链接含 &amp;,需 html.unescape;图片 URL 可能含 # 锚点需截断。
  6. webp 兼容:图片可能是 webp 格式,img2pdf 不支持,合成前用 Pillow 转 png。
  7. 请求限速:图片下载 sleep 0.4、文章之间 sleep 1.5,避免频控封 IP。
  8. 同名去重:目录页同一链接常对应多个锚文本(如"章末提示 / 第一节 XX"共用一个链接),按 (mid, idx, sn) 去重,一篇只处理一次。
  9. 目录页标题解析:章节标题是 <p><strong><span>…</span></strong></p> 结构(非 h1-h4),且章名可能被拆成多个 span,必须匹配整个 <strong> 块拼接文本。扫描范围限定在 js_content 容器内,单遍跟踪 <a> 状态跳过链接内文本(避免页面脚本里的 <a 字符串干扰)。坐标须加正文偏移,与链接节点保持同一基准,否则排序错乱。
  10. 通用层级识别(不依赖特定公众号排版):三策略递进——① 强前缀:第X单元/篇/部分/一、→一级,第X章/专题/(X)→二级,第X节→三级;② 字号聚类兜底:仅当页面无任何强前缀层级词时启用,按字号最大=一级、次大=二级(18px/16px 风格),避免把大字号正文误判为标题;③ 都识别不出则平铺存放。文件夹名直接用上下文标题列表(一级在前、二级在后),不做特定内容匹配,因此"第一部分/模块一/专题"等风格同样支持。

环境依赖

  • Python 3 + requests + Pillow + img2pdf(自检用 pypdf,已随 venv 安装)
  • 运行环境:C:/Users/jengson/.workbuddy/binaries/python/envs/default/Scripts/python.exe(已装齐依赖)
  • 若在其他环境运行:pip install requests pillow img2pdf pypdf

环境适配说明

  • 若运行环境的回收站不可用、或 os.remove 删临时文件被安全策略拦截,脚本内置 _force_delete() 会用系统底层 API 兜底删除,图片文件都能删掉。
  • 极端情况下可能残留一个空的 _tmp_imgs 目录(仅限删除被拦截的环境,正常环境不会有),不影响 PDF 产物,手动删掉即可。

常见问题

  • 抓取失败:脚本会自动诊断原因并显示,常见原因包括——「反爬拦截」(自动换 5 套微信 UA 策略重试)、「链接失效」(文章被删除或链接过期,需人工确认)、「网络错误」(网络波动,稍后重跑)、「内容异常」(页面返回异常)。批量模式下失败项会在报告中按原因分类。
  • 某篇无图片:可能是纯文字推文(本技能只转图片,不提取文字),如实告知用户。
  • 图文混排文章:只转图片,文字段落不会进 PDF。若需要图文都转,属另一套 HTML 渲染方案,超出本技能范围。
  • 无关小图自动过滤:脚本默认过滤掉明显不是正文的图片——开头/末尾的装饰条、小图标、公众号关注二维码图等。规则是:①单图短边过小(宽 < 280 或高 < 220)、像素过少(< 9 万)、比例极端(宽高比 > 5 或 < 0.2)直接丢弃;②末尾 1 张如果像素明显小于正文主流书页(< 1/3 且差值 > 10 万),也丢弃。下载时会在终端打印"已过滤无关小图 N 张"和原因,正常书页内容不会被误杀。
  • 输出文件名:单篇按书的信息(文章标题清洗后)命名,批量按目录条目标题命名;脚本已自动清洗 \/:*?"<>| 非法字符,空标题会自动用链接标识/mid_idx 兜底,不会出现无意义文件名。

资源

scripts/wechat_to_pdf.py

完整抓取-下载-合成主脚本,含两种模式、自检与重试机制。