#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 微信公众号文章抓取脚本 用途:抓取微信公众号文章原始 HTML,提取文字内容与图片 URL 列表。 使用方法: python3 extract_wechat.py <微信文章URL> [输出目录] 示例: python3 extract_wechat.py "https://mp.weixin.qq.com/s/xxxx" ./output 输出文件: - raw_article.html 原始 HTML 源码 - extracted_content.txt 提取的图片列表与纯文本摘要 """ import sys import os import re import urllib.request import html as html_lib from pathlib import Path def fetch_wechat_html(url: str) -> str: """ 使用桌面微信 User-Agent 抓取微信公众号文章原始 HTML。 :param url: 微信公众号文章 URL :return: 原始 HTML 字符串 :raises: urllib.error.URLError 如果请求失败 """ headers = { 'User-Agent': ( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/120.0.0.0 Safari/537.36 ' 'MicroMessenger/8.0.0' ), 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', } req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req, timeout=30) as resp: return resp.read().decode('utf-8', errors='replace') def extractTitle(htmlContent: str) -> str: """从 HTML 中提取文章标题。""" match = re.search(r']*>(.*?)', htmlContent, re.DOTALL | re.IGNORECASE) if match: title = re.sub(r'<[^>]+>', '', match.group(1)).strip() return html_lib.unescape(title) return "未知标题" def extractImages(htmlContent: str) -> list: """ 从微信文章 HTML 中提取所有图片 URL。 优先提取 data-src 属性(微信懒加载),其次 src 属性。 :param htmlContent: 原始 HTML 字符串 :return: 去重后的图片 URL 列表 """ imgUrls = [] seen = set() # 优先匹配 data-src(微信懒加载图片) for match in re.finditer(r'data-src=["\']([^"\']+mmbiz[^"\']+)["\']', htmlContent): url = html_lib.unescape(match.group(1)) if url not in seen: imgUrls.append(url) seen.add(url) # 补充匹配 src(部分图片无 data-src) for match in re.finditer(r']+src=["\']([^"\']+mmbiz[^"\']+)["\']', htmlContent): url = html_lib.unescape(match.group(1)) if url not in seen: imgUrls.append(url) seen.add(url) return imgUrls def extractText(htmlContent: str) -> str: """ 提取文章正文区域的纯文本(js_content 内部)。 :param htmlContent: 原始 HTML 字符串 :return: 清理后的纯文本 """ contentMatch = re.search( r'id=["\']js_content["\'][^>]*>(.*?)(?=]+>', ' ', text) # 解码 HTML 实体 text = html_lib.unescape(text) # 清理多余空白 text = re.sub(r'\s+', ' ', text).strip() return text def main(): """主入口函数。""" if len(sys.argv) < 2: print("用法: python3 extract_wechat.py <微信文章URL> [输出目录]") print("示例: python3 extract_wechat.py 'https://mp.weixin.qq.com/s/xxxx' ./output") sys.exit(1) url = sys.argv[1] outputDir = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("./output") outputDir.mkdir(parents=True, exist_ok=True) print(f"📡 正在抓取: {url}") htmlContent = fetch_wechat_html(url) # 保存原始 HTML rawPath = outputDir / "raw_article.html" rawPath.write_text(htmlContent, encoding='utf-8') print(f"💾 原始 HTML 已保存: {rawPath} ({len(htmlContent):,} 字节)") # 提取标题 title = extractTitle(htmlContent) print(f"📰 标题: {title}") # 提取图片 imgUrls = extractImages(htmlContent) print(f"🖼️ 发现 {len(imgUrls)} 张图片") # 提取文本 textContent = extractText(htmlContent) # 写入提取结果 extractPath = outputDir / "extracted_content.txt" with open(extractPath, 'w', encoding='utf-8') as f: f.write(f"Title: {title}\n\n") f.write(f"Images ({len(imgUrls)}):\n") for i, imgUrl in enumerate(imgUrls, 1): f.write(f"[{i}] {imgUrl}\n") f.write(f"\n--- 正文摘要 (前 2000 字) ---\n\n") f.write(textContent[:2000]) print(f"📄 提取结果已保存: {extractPath}") print(f"\n✅ 完成!共提取 {len(imgUrls)} 张图片,正文约 {len(textContent)} 字。") if __name__ == "__main__": main()