# Python 办公自动化示例

> [!note]
> 本文整理原笔记中的 Word 文档生成与邮件发送示例。基础知识见 [[计算机系/Python/Python题目知识点梳理.md]]。

## 1. 安装依赖

```powershell
python -m pip install python-docx yagmail
```

建议在虚拟环境中安装，避免不同项目的依赖互相影响。

## 2. 生成 Word 文档

```python
from pathlib import Path

from docx import Document
from docx.shared import Cm


def create_report(output_path: Path, image_path: Path | None = None) -> None:
    document = Document()
    document.add_heading("异地", level=1)
    document.add_paragraph("这是一个段落。")

    styled_paragraph = document.add_paragraph()
    styled_paragraph.add_run("加粗").bold = True
    styled_paragraph.add_run("；")
    styled_paragraph.add_run("斜体").italic = True

    if image_path is not None:
        if not image_path.is_file():
            raise FileNotFoundError(f"找不到图片：{image_path}")
        document.add_picture(str(image_path), width=Cm(4), height=Cm(4))

    records = [
        ["北京市示例科技有限公司", "A 产品", 20000],
        ["天津市示例科技有限公司", "B 产品", 21000],
        ["上海市示例科技有限公司", "C 产品", 18000],
        ["杭州市示例科技有限公司", "D 产品", 25000],
        ["深圳市示例科技有限公司", "E 产品", 19000],
        ["广州市示例科技有限公司", "F 产品", 30000],
    ]

    table = document.add_table(rows=1, cols=3)
    table.style = "Table Grid"
    header = table.rows[0].cells
    header[0].text = "公司"
    header[1].text = "产品"
    header[2].text = "金额"

    for company, product, amount in records:
        cells = table.add_row().cells
        cells[0].text = company
        cells[1].text = product
        cells[2].text = str(amount)

    output_path.parent.mkdir(parents=True, exist_ok=True)
    document.save(output_path)


create_report(
    output_path=Path("Desk.docx"),
    image_path=Path("每颗豆.png"),
)
```

### 知识点

- PyPI 包名是 `python-docx`，导入名是 `docx`；
- `Document` 表示整个 Word 文档；
- `Paragraph` 包含一个或多个 `Run`，粗体和斜体通常设置在 `Run` 上；
- 表格行数应随数据动态增长，不要固定为某个常数；
- 使用 `pathlib.Path` 比手工拼接路径更清晰；
- 保存前可创建输出目录，并检查图片是否存在。

## 3. 通过 SMTP 发送邮件

> [!danger]
> 不要在代码或笔记中写入邮箱密码、SMTP 授权码及真实收件人列表。下面通过环境变量读取授权码。

先在系统中设置环境变量 `QQ_MAIL_AUTH_CODE`，其值应为邮箱服务商生成的 SMTP 授权码，而不是登录密码。

```python
import os

import yagmail


def send_email(
    sender: str,
    recipients: list[str],
    subject: str,
    contents: list[str],
) -> None:
    auth_code = os.environ.get("QQ_MAIL_AUTH_CODE")
    if not auth_code:
        raise RuntimeError("未设置环境变量 QQ_MAIL_AUTH_CODE")

    client = yagmail.SMTP(
        user=sender,
        password=auth_code,
        host="smtp.qq.com",
    )
    client.send(
        to=recipients,
        subject=subject,
        contents=contents,
    )


send_email(
    sender="sender@example.com",
    recipients=["recipient@example.com"],
    subject="Python 邮件测试",
    contents=["第一次用 Python 发送邮件！"],
)
```

### 知识点

- SMTP 服务通常需要先在邮箱网页设置中启用；
- 授权码应放在环境变量或凭据管理器中；
- `to` 可以是单个地址，也可以是收件人列表；
- 生产脚本还应处理网络失败、认证失败和部分收件人失败；
- 自动群发前要确认收件人授权、发送频率和服务商规则，避免误发或被判定为垃圾邮件。

## 4. 可继续改进的方向

- 把表格数据从 CSV 或 Excel 文件读取；
- 将 Word 模板、图片和输出目录改成命令行参数；
- 使用 `logging` 记录批处理结果；
- 为失败操作添加重试，但要限制次数；
- 将邮件正文改为模板，并在发送前生成预览；
- 用测试邮箱和虚构数据验证程序，避免直接操作真实联系人。
