pdf-parsing · git:20260713.9b7b32b · 2026-07-13 · sha256 7d05be3a004fc063

pdf-parsing git:20260713.9b7b32bA

Immutable. This exact content is served forever at /api/v1/blob/7d05be3a004fc063.

---
name: pdf-parsing
description: "当需要用 structai.read_pdf 将 PDF 文档解析成本地 Markdown、抽取图片资源,并处理 MinerU 解析缓存或代理重试问题时使用。"
---

# PDF 解析

## 目标

把 PDF 文档解析成本地可读取、可搜索、可复制的 Markdown,并抽取 PDF 中的图片资源。解析完成后,后续阅读应优先使用本地 Markdown 文件;重复调用 `structai.read_pdf` 也会优先复用本地解析结果,不会在本地结果已存在时重复上传同一个 PDF。

## 依赖安装

当前 skill 对照的关键解析依赖:

- `structai` package version:`0.1.24`。
- `structai` GitHub HEAD:`d20df602578bee124a9c93b293493e63212d0aab`。
- 更新本 skill 前,先确认 `structai.read_pdf` 行为是否变化;如果上游版本或 HEAD 变化,必须重新阅读 `read_pdf` 源码和 `structai_skill()` 输出。

```bash
pip install structai
```

如果包索引中找不到 `structai`,或需要使用 GitHub 仓库中的最新代码:

```bash
pip install "git+https://github.com/black-yt/structai.git"
```

校验:

```bash
python3 -c "from structai import read_pdf; import inspect; print(inspect.signature(read_pdf))"
```

如果当前环境同时有多个 Python,请确认 `pip` 和 `python3` 指向同一个环境:

```bash
python3 -m pip show structai
python3 -c "import structai; print(structai.__file__)"
```

## `read_pdf` 源码追溯

- `read_pdf` 行为以当前安装版本源码为准;如果缓存、上传、下载、图片路径或返回值和预期不一致,先读源码再判断。
- `inspect.signature(read_pdf)` 用于看函数参数。
- `inspect.getsource(read_pdf)` 用于看 `read_pdf` 入口逻辑。
- `structai.__file__` 和 `structai.pdf.__file__` 用于定位安装包源码文件。
- 源码只用于阅读和定位问题,不要改源码,不要直接修改 `site-packages` 或共享环境;需要改库时,先 clone `https://github.com/black-yt/structai`,在用户确认后用 editable install。

```bash
python3 - <<'PY'
import inspect
import structai
import structai.pdf as pdf_mod
from structai import read_pdf

print("structai:", structai.__file__)
print("structai.pdf:", pdf_mod.__file__)
print("signature:", inspect.signature(read_pdf))
print(inspect.getsource(read_pdf))
PY
```

如果要继续追 `read_pdf` 调用的内部函数,先查看 `structai.pdf` 模块源码路径,再按函数名读取:

```bash
python3 - <<'PY'
import inspect
import structai.pdf as pdf_mod

print(pdf_mod.__file__)
for name in ["get_headers"]:
    obj = getattr(pdf_mod, name, None)
    if obj is not None:
        print(f"\n===== {name} =====")
        print(inspect.getsource(obj))
PY
```

## MinerU Token

`structai.read_pdf` 会调用 MinerU 精准解析 API。该 API 需要 Token,且请求头格式为 `Authorization: Bearer <Token>`。

获取方式:

1. 打开 <https://mineru.net/>。
2. 注册或登录账号。
3. 进入 API / Token 管理页面,免费申请 API Token。
4. 把 Token 设置为环境变量 `MINERU_TOKEN`。

Linux / macOS / WSL:

```bash
export MINERU_TOKEN="your_mineru_token"
```

Windows PowerShell:

```powershell
$env:MINERU_TOKEN = "your_mineru_token"
```

验证环境变量是否已设置:

```bash
python3 -c "import os; print('MINERU_TOKEN set:', bool(os.environ.get('MINERU_TOKEN')))"
```

验证 `structai` 能读到 Token:

```bash
python3 -c "from structai.pdf import get_headers; h=get_headers(); print(h['Authorization'][:16] + '...')"
```

不要把真实 Token 写进代码、文档、Git 仓库或聊天记录。需要长期生效时,把 `export MINERU_TOKEN=...` 放到自己的 shell 配置文件或系统环境变量中。

如果未设置 Token,`structai.read_pdf` 会报类似错误:

```text
MINERU_TOKEN not found. Please register a free account at https://mineru.net/ and set the environment variable.
```

为了避免解析时生成 `__pycache__`,建议运行命令时加上:

```bash
PYTHONDONTWRITEBYTECODE=1
```

## 基本解析

当前 `structai.read_pdf` 封装使用 MinerU 批量本地文件上传接口 `/api/v4/file-urls/batch`,适合解析本地 PDF 文件。MinerU 官方文档说明:精准解析 API 需要 Token,支持表格和公式识别,单个文件大小上限为 200 MB,页数上限为 200 页;批量上传接口单次申请上传链接不能超过 50 个文件。

如果 PDF 超过接口限制,先按页拆分或压缩,再分别解析。

Python 调用:

```python
from structai import read_pdf

result = read_pdf("paper.pdf")
```

命令行解析:

```bash
env PYTHONDONTWRITEBYTECODE=1 python3 -c "from structai import read_pdf; read_pdf('paper.pdf')"
```

带摘要输出:

```bash
env PYTHONDONTWRITEBYTECODE=1 python3 -c "from structai import read_pdf
p='paper.pdf'
r=read_pdf(p)
print('success', bool(r))
if r:
    print('path', r['path'])
    print('text_length', len(r['text']))
    print('image_count', len(r['img_paths']))
    print(r['text'][:1200])"
```

## 本地解析结果

对 `paper.pdf` 调用 `read_pdf` 后,通常会在 PDF 同级位置生成同名目录:

```text
paper/
  full.md
  layout.json
  *_content_list.json
  图片资源子目录
```

常用文件:

- `full.md`:解析后的全文 Markdown。
- `layout.json`:版面结构信息,可用于排查版面解析问题。
- `*_content_list.json`:内容块列表,可辅助定位段落、标题、表格和图片。
- 图片资源子目录:保存从 PDF 中抽取出来的图片或版面切片。

如果同名目录下已经存在 `full.md`,`read_pdf` 会优先复用本地解析结果,并直接读取本地 Markdown 和图片路径;这种情况下重复调用不会再次把同一个 PDF 上传到 MinerU。因此不用担心为了获取返回值而重复调用 `read_pdf("paper.pdf")` 会产生重复上传。只有删除本地解析目录、缺失 `full.md`、更换 PDF 路径或替换原始 PDF 后,才需要重新上传解析。

实际阅读时仍建议直接打开 `full.md`,这样最快,也方便手工检查解析质量。

## 返回值结构

成功时通常返回:

```python
{
    "path": "paper.pdf",
    "text": "...",
    "img_paths": [...],
    "imgs": [...]
}
```

字段含义:

- `path`:原始 PDF 路径。
- `text`:`full.md` 的全文内容。
- `img_paths`:Markdown 正文实际引用到的图片路径。
- `imgs`:对应的 PIL 图片对象。

## 读取与检查

读取开头:

```bash
sed -n '1,120p' paper/full.md
```

查看标题结构:

```bash
rg -n "^#|^##|^###" paper/full.md
```

查看图片引用:

```bash
rg -n "!\\[" paper/full.md
```

至少确认:

- `full.md` 存在且非空。
- 标题、摘要、章节标题基本可读。
- 图表引用能对应到本地图片资源。
- 关键表格没有严重错行。
- 公式和特殊符号没有影响理解。

## 常见问题

- `MINERU_TOKEN not found`:先到 <https://mineru.net/> 申请 Token,再设置 `MINERU_TOKEN` 环境变量。
- `401`、`403` 或 `Authorization` 相关错误:检查 Token 是否复制完整、是否过期、当前 shell 是否能读取 `MINERU_TOKEN`。
- 解析返回 `None`:确认 Token 已设置;关闭代理重试;检查是否已有可读 `full.md`;如果仍失败,记录错误信息。
- Markdown 局部乱码或断词:用 `full.md` 快速定位章节,对关键段落、公式和表格回到 PDF 原文或抽取图片核对。
- 图片目录文件很多:优先看 Markdown 中实际引用的图片,再按后续任务需要浏览其他图片。
- 网络问题与代理处理:MinerU 处理和结果下载需要网络。如果遇到 `SSLError`、`UNEXPECTED_EOF_WHILE_READING`、`Max retries exceeded`、`Connection reset`、上传成功但结果压缩包下载失败等问题,优先关闭代理后重试。关闭代理时仍要保留 `MINERU_TOKEN`:

  ```bash
  env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u http_proxy -u https_proxy -u all_proxy -u NO_PROXY -u no_proxy MINERU_TOKEN="$MINERU_TOKEN" PYTHONDONTWRITEBYTECODE=1 python3 -c "from structai import read_pdf; read_pdf('paper.pdf')"
  ```

  处理顺序:

  1. 保留当前目录,不要删除已经生成的解析缓存。
  2. 确认 `MINERU_TOKEN` 已设置。
  3. 用关闭代理的命令重试同一个 PDF。
  4. 如果重试成功,后续直接读取本地 `full.md`。
  5. 如果重试失败但已有可读 `full.md`,直接使用本地 Markdown。
  6. 如果没有 `full.md`,记录完整错误信息,稍后重试或更换网络环境。