Парсинг PDF: извлечение текста и таблиц, OCR и примеры на разных языках

Извлечение текста и таблиц из PDF: текстовые слои против сканов, OCR через Tesseract и примеры на Python, JavaScript и PHP.

КP
Команда Parsing.agency
Сбор данных под задачи бизнеса
Опубликовано: 30 марта 2025

PDF — формат, придуманный для печати и просмотра, а не для хранения данных. Именно поэтому извлекать из него информацию надёжно сложнее всего среди всех форматов нашего обзора парсинга документов. В PDF приходят счета, договоры, спецификации, прайсы, банковские и государственные выписки — и почти всегда их нужно превратить в структурированные данные.

Главная развилка: текст или картинка

Первое, что определяет всю стратегию, — как именно текст хранится в файле. В «цифровом» PDF (сгенерированном из Word, верстки или браузера) текст лежит как выбираемые символы — его можно извлечь напрямую. В сканированном PDF страница это изображение, и без распознавания (OCR) символов в нём нет — извлечётся пустота. Проверить просто: если в просмотрщике текст выделяется мышью, он цифровой; если нет — нужен OCR. Эти два случая требуют совершенно разных инструментов, поэтому начинать всегда стоит с проверки.

Извлечение текста из цифровых PDF

Python

Для извлечения текста и, что важнее, таблиц лучший выбор — pdfplumber. Он понимает координаты символов и линий, поэтому умеет восстанавливать табличную структуру.

python
import pdfplumber

with pdfplumber.open("invoice.pdf") as pdf:
    page = pdf.pages[0]

    text = page.extract_text()
    print(text)

    for table in page.extract_tables():
        for row in table:
            print(row)  # row — список ячеек строки таблицы

Когда нужен только текст и важна скорость, берут PyMuPDF (импортируется как fitz) — он очень быстрый и хорошо держит раскладку.

python
import fitz  # PyMuPDF

doc = fitz.open("contract.pdf")
for page in doc:
    print(page.get_text())

Для простых задач вроде извлечения текста постранично или работы с метаданными подойдёт pypdf (преемник устаревшего PyPDF2).

python
from pypdf import PdfReader

reader = PdfReader("report.pdf")
for page in reader.pages:
    print(page.extract_text())

JavaScript / Node.js

В Node быстрый способ получить весь текст документа — pdf-parse.

javascript
const fs = require("fs");
const pdf = require("pdf-parse");

const buffer = fs.readFileSync("invoice.pdf");
pdf(buffer).then((data) => {
  console.log(data.text);   // весь текст
  console.log(data.numpages);
});

Когда нужен низкоуровневый контроль или работа в браузере, используют pdf.js от Mozilla — он даёт доступ к отдельным текстовым фрагментам с их координатами.

Java

В корпоративных проектах распространён Apache PDFBox. Учтите смену API между версиями: в PDFBox 3.x документ открывается через Loader.loadPDF(...), тогда как в 2.x был PDDocument.load(...).

java
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import java.io.File;

try (PDDocument doc = Loader.loadPDF(new File("report.pdf"))) {
    String text = new PDFTextStripper().getText(doc);
    System.out.println(text);
}

Таблицы — отдельная боль

В PDF нет понятия «таблица»: то, что человек видит как таблицу, на самом деле набор текстовых блоков и линий с координатами. Поэтому извлечение таблиц всегда эвристика. pdfplumber восстанавливает их по линиям сетки и хорошо работает, когда таблица расчерчена. Для сложных таблиц без линий в Python применяют Camelot (метод по разметке) или tabula-py (обёртка над Java-движком Tabula). Если таблиц много и они однотипные, обычно подбирают один инструмент и калибруют его под конкретный макет.

Часто результат удобнее всего сразу выгрузить в таблицу — тогда дальнейшая обработка идёт через Excel или CSV, как с любым табличным источником.

Сканы и OCR

Если текст не выделяется, перед извлечением страницу нужно распознать. Классический стек в Python — рендер страниц в изображения (через тот же PyMuPDF) и распознавание движком Tesseract через обёртку pytesseract.

python
import fitz
import pytesseract
from PIL import Image
import io

doc = fitz.open("scan.pdf")
for page in doc:
    pix = page.get_pixmap(dpi=300)              # рендерим страницу в картинку
    img = Image.open(io.BytesIO(pix.tobytes()))
    text = pytesseract.image_to_string(img, lang="rus")
    print(text)

Качество OCR сильно зависит от разрешения (берите от 300 DPI), чистоты скана и языка. Для русских документов обязательно указывайте языковой пакет rus. Стопроцентной точности от OCR ждать не стоит — результат почти всегда требует постобработки и проверки.

Когда PDF лучше не парсить «в лоб»

Иногда оказывается, что нужный документ существует и в другом формате — например, та же выписка доступна как CSV выгрузка или данные отдаёт API в JSON. Это почти всегда надёжнее, чем извлекать таблицу из печатной версии, поэтому стоит проверить источник, прежде чем браться за PDF.

PDF — формат, где «универсального» решения не существует: счёт, договор и скан требуют разных инструментов. Если у вас регулярный поток однотипных документов — счетов, актов, спецификаций — мы настроим извлечение данных из PDF под конкретный макет, включая таблицы и OCR для сканов, с выгрузкой результата в нужную систему.