Files
hdPaiban/skills/hundun-wechat-layout/scripts/extract_wechat.py
T
lidf a777665775 feat: 初始化混沌学园微信公众号 HTML 排版 Skill
- SKILL.md: 完整设计规范(色彩体系、微信公众号兼容规则、图片排版方案)
- scripts/extract_wechat.py: 微信公众号文章抓取脚本
- scripts/build_hundun_html.py: 混沌风格 HTML 生成脚本(组件化)
- examples/hundun-lead-article.html: 领教营二期排版示例
- resources/color-palette.md: 混沌品牌色彩参考
2026-07-22 12:06:55 +08:00

157 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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'<title[^>]*>(.*?)</title>', 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'<img[^>]+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["\'][^>]*>(.*?)(?=<div\s+class=["\']ct_mpda_wrp|$)',
htmlContent,
re.DOTALL | re.IGNORECASE
)
if not contentMatch:
return ""
text = contentMatch.group(1)
# 移除所有 HTML 标签
text = re.sub(r'<[^>]+>', ' ', 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()