# 微信公众号网页抓取与PPT生成经验总结

## 日期：2026年3月24日

---

## 一、微信公众号内容抓取要点

### 1. 反爬机制应对
- 微信公众号有安全验证机制，直接请求会被拦截
- **解决方案**：使用 `curl_cffi` 库模拟浏览器请求，设置 `impersonate='chrome110'`
- 添加必要的请求头：`User-Agent`、`Referer`（必须是 `https://mp.weixin.qq.com/`）

### 2. 图片URL获取
- 微信公众号图片不在 `src` 属性中，而是在 `data-src` 属性
- 需要遍历所有后代元素：`elem.iter()` 而不是只遍历直接子元素
- 图片URL格式：`https://mmbiz.qpic.cn/mmbiz_png/...`

### 3. 关键HTML选择器
- 文章内容区域：`#js_content`
- 文章标题：`//h1[@id="activity-name"]//text()`
- 作者信息：`//a[@id="js_name"]/text()`
- 图片路径：`img[data-src]`
- 跳过头像和表情：`class` 包含 `avatar`、`emoji`、`follow_avatar`

### 4. 关键代码片段

```python
from curl_cffi import requests
from lxml import etree

# 抓取网页
response = requests.get(url, headers={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Referer': 'https://mp.weixin.qq.com/',
}, impersonate='chrome110', timeout=30)

# 解析HTML
tree = etree.HTML(response.text)

# 获取标题（使用XPath）
title_elem = tree.xpath('//h1[@id="activity-name"]//text()')
title = ''.join([t.strip() for t in title_elem if t.strip()])

# 获取作者
author_elem = tree.xpath('//a[@id="js_name"]/text()')
author = ''.join([a.strip() for a in author_elem if a.strip()])

# 获取正文
js_content = tree.xpath('//div[@id="js_content"]')[0]

# 提取图片（遍历所有后代元素）
img_urls = []
for elem in js_content.iter():
    tag = elem.tag.lower() if hasattr(elem, 'tag') else ''
    if tag == 'img':
        data_src = elem.get('data-src', '')
        if data_src and 'mmbiz' in data_src:
            img_urls.append(data_src)
```

### 5. 下载图片

```python
def download_image(url, path):
    headers = {
        'User-Agent': 'Mozilla/5.0...',
        'Referer': 'https://mp.weixin.qq.com/',  # 必须是微信域名
    }
    r = requests.get(url, headers=headers, impersonate='chrome110', timeout=30)
    if r.status_code == 200:
        with open(path, 'wb') as f:
            f.write(r.content)
        return True
    return False
```

**注意**：微信图片有防盗链，必须设置正确的 Referer

---

## 二、PPT生成要点

### 1. python-pptx 库使用

```python
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.enum.shapes import MSO_SHAPE

# 创建演示文稿
prs = Presentation()
prs.slide_width = Inches(13.333)  # 16:9 宽屏
prs.slide_height = Inches(7.5)

# 添加空白幻灯片
slide = prs.slides.add_slide(prs.slide_layouts[6])
```

### 2. 添加标题栏

```python
def add_slide_header(slide, title_text):
    header = slide.shapes.add_shape(
        MSO_SHAPE.RECTANGLE,
        Inches(0), Inches(0), Inches(13.333), Inches(1.2)
    )
    header.fill.solid()
    header.fill.fore_color.rgb = RGBColor(0x1A, 0x73, 0xE8)
    header.line.fill.background()

    title_shape = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(0.8))
    tf = title_shape.text_frame
    p = tf.paragraphs[0]
    p.text = title_text
    p.font.size = Pt(32)
    p.font.bold = True
    p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
```

### 3. 添加图片

```python
from PIL import Image

def add_image_slide(prs, img_path, slide_title="配图"):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    add_slide_header(slide, slide_title)

    # 获取图片尺寸
    img = Image.open(img_path)
    img_width, img_height = img.size

    # 计算缩放比例
    max_width = Inches(12)
    max_height = Inches(5.8)
    scale = min(max_width / img_width, max_height / img_height)

    final_width = img_width * scale
    final_height = img_height * scale

    # 居中定位
    left = (Inches(13.333) - final_width) / 2
    top = Inches(1.5)

    # 添加图片
    slide.shapes.add_picture(img_path, left, top, width=final_width, height=final_height)
```

---

## 三、图片格式处理

### 问题
微信公众号下载的图片可能是PNG/WEBP格式，但保存为.jpg扩展名

### 解决方案

```python
from PIL import Image
import os

def fix_image_format(img_path):
    img = Image.open(img_path)
    if img.format == 'PNG':
        new_path = img_path.replace('.jpg', '.png')
        img.save(new_path, 'PNG')
        os.remove(img_path)
        return new_path
    elif img.format == 'WEBP':
        rgb_img = img.convert('RGB')
        new_path = img_path.replace('.jpg', '.jpeg')
        rgb_img.save(new_path, 'JPEG')
        os.remove(img_path)
        return new_path
    return img_path
```

### 支持的图片格式
python-pptx支持：`BMP`、`GIF`、`JPEG`、`PNG`、`TIFF`、`WMF`

---

## 四、完整工作流程

1. **抓取网页** - 使用curl_cffi模拟浏览器
2. **解析HTML** - 使用lxml提取标题、正文、图片URL
3. **下载图片** - 保存到本地（注意防盗链）
4. **修正图片格式** - 确保扩展名与格式匹配
5. **生成PPT** - 按文章结构组织幻灯片
6. **复制到共享目录** - 用于外部访问

---

## 五、依赖包

```bash
pip install curl_cffi python-pptx Pillow lxml
```

### 关键库说明
- `curl_cffi`：模拟浏览器请求，绕过微信反爬
- `python-pptx`：生成PPT文件
- `Pillow`：图片格式处理
- `lxml`：HTML解析

---

## 六、常见问题与解决方案

| 问题 | 原因 | 解决方案 |
|------|------|----------|
| 微信请求被拦截 | 安全验证机制 | 使用curl_cffi模拟浏览器 |
| 图片下载失败 | Referer不正确 | 设置Referer为mp.weixin.qq.com |
| PPT图片不显示 | 图片格式错误 | 使用Pillow转换图片格式 |
| 文字乱码 | 字体问题 | PPT中使用默认字体 |
| 内容解析错误 | HTML结构理解错误 | 使用elem.iter()遍历所有后代 |
| 图片扩展名不匹配 | 下载时使用了错误扩展名 | 保存后检查并修正格式 |

---

## 七、经验总结

1. **curl_cffi是关键**：相比requests，curl_cffi能更好地模拟浏览器行为
2. **data-src属性**：微信图片URL在data-src而非src中
3. **elem.iter()**：要遍历所有后代元素才能获取所有图片
4. **图片格式检查**：下载后要检查实际格式，修正扩展名
5. **Referer防盗链**：下载微信图片必须设置正确的Referer
