ProxynetProxynet

استخراج داده از PDF: جدول، متن و OCR

تاریخ انتشار:

15 دقیقه مطالعه

Acar Diveroli
نویسنده: Acar Diveroli
پشته‌ای ایزومتریک از مکعب‌های تیره با نام بخش‌های PDF، یک مکعب آبی CSV در بالا و شکافی خط‌چین با برچسب needs OCR

یک تأمین‌کننده هر ماه فهرست قیمتش را در یک PDF چهل‌صفحه‌ای می‌فرستد. تیم مالی صورت‌حساب‌های بانکی را به همین شکل می‌گیرد و گزارش بازاری که هفته پیش خریدید بهترین جدولش را در صفحه 17 دارد. این سطرها را در یک صفحه‌گسترده لازم دارید، و کپی دستی ستون‌ها را به هم می‌ریزد، علامت‌های منفی را جا می‌اندازد و یک ساعت وقت می‌گیرد که ماه بعد هم باید دوباره صرفش کنید.

این نوشته با پرسشی شروع می‌شود که روش را تعیین می‌کند، یعنی اینکه فایل لایه متنی دارد یا نه. سپس رابط PDF در Excel، کتابخانه‌های pdfplumber و Camelot و tabula-py، نویسه‌خوانی با Tesseract برای فایل‌های اسکن‌شده و اسکریپتی دسته‌ای را مرور می‌کند که یک پوشه PDF را به یک فایل CSV تبدیل می‌کند. همه نمونه‌ها را روی PDFهای آزمایشی‌ای اجرا کرده‌ایم که با ReportLab ساختیم.

استخراج داده از PDF یعنی چه؟

استخراج داده از PDF یعنی محتوایی را که برای چاپ صفحه‌آرایی شده به سطرها و فیلدهایی تبدیل کنید که بتوانید مرتبشان کنید و جای دیگری بارگذاری‌شان کنید. PDF «یک جدول چهارستونی» را ذخیره نمی‌کند. دستورهایی مانند «1,007.50 را در این موقعیت با این قلم بکش» و «از اینجا تا آنجا خطی بکش» را ذخیره می‌کند. هر ابزار استخراج داده جدول را از روی همین مختصات دوباره می‌سازد، و به همین دلیل یک فایل در یک ابزار تمیز و در ابزار دیگر درهم‌ریخته بیرون می‌آید.

این همان کاری است که برای بیرون کشیدن فیلدها از یک صفحه وب انجام می‌شود (استخراج داده از وب‌سایت)، با این تفاوت که به‌جای تگ‌های HTML با نویسه‌های موقعیت‌دار سروکار دارید. جمع‌آوری PDF از وب‌سایت‌ها در مقیاس بزرگ بخشی از یک زنجیره گسترده‌تر استخراج داده است.

PDF متنی یا PDF اسکن‌شده: چطور تشخیص دهیم؟

  • PDF متنی (دیجیتال). از Word، یک نرم‌افزار حسابداری یا یک ابزار گزارش‌گیری خروجی گرفته شده است. نویسه‌ها به‌صورت متن ذخیره شده‌اند، پس استخراج همان عددهای دقیق فایل را می‌خواند.
  • PDF اسکن‌شده (تصویری). هر صفحه عکسی از یک کاغذ است. تا وقتی OCR (نویسه‌خوانی نوری) نویسه‌ها را از روی پیکسل‌ها حدس نزند، هیچ متنی وجود ندارد.

آزمون سریع: کلید ⁦Ctrl+F⁩ را بزنید و واژه‌ای را که روی صفحه می‌بینید جست‌وجو کنید. اگر پیدا شد، لایه متنی وجود دارد. در کد، خالی بودن فهرست page.chars در pdfplumber یعنی آن صفحه اسکن است. برخی فایل‌ها هر دو را دارند (یک گزارش دیجیتال با یک صفحه امضای اسکن‌شده)، پس صفحه‌به‌صفحه بررسی کنید، نه فایل‌به‌فایل.

استخراج جدول از PDF چگونه کار می‌کند؟

چه از Excel استفاده کنید، چه از pdfplumber یا Camelot، همین گام‌ها در پس‌زمینه اجرا می‌شوند:

  1. صفحه تحلیل می‌شود. هر نویسه با موقعیت، قلم و اندازه‌اش خوانده می‌شود، همراه با خط‌ها و مستطیل‌های رسم‌شده.
  2. نویسه‌ها به واژه و سطر تبدیل می‌شوند. نویسه‌های نزدیک به هم روی یک خط پایه واژه می‌سازند و واژه‌های هم‌ارتفاع یک سطر.
  3. ناحیه‌های جدول پیدا می‌شوند. از روی خط‌هایی که یک شبکه می‌سازند، یا از روی ستون‌هایی از واژه‌ها که با فاصله سفید از هم جدا شده‌اند.
  4. سلول‌ها ساخته می‌شوند. محل تقاطع خط‌ها یا فاصله میان ستون‌ها مرز سلول‌ها را تعیین می‌کند و هر واژه به سلولی می‌رود که آن را در بر دارد.
  5. سطرها برگردانده می‌شوند. فهرستی از سطرها (یا یک DataFrame) که تمیزش می‌کنید و ذخیره‌اش می‌کنید.

بیشتر مشکلات از گام 3 می‌آید. یک شبکه کامل آسان است؛ جدول بدون خط ابزار را وادار می‌کند ستون‌ها را از روی فاصله سفید حدس بزند، و یک نام محصول بلند می‌تواند همه ستون‌ها را جابه‌جا کند.

مقایسه روش‌های استخراج از PDF

روشکدنویسی لازم است؟اسکن را می‌خواند؟مناسب برایکجا گیر می‌کند
کپی و چسباندنخیرخیریک جدول کوچک، یک بارستون‌ها در یک سلول جمع می‌شوند، جدول‌های چندصفحه‌ای
Excel، گزینه ⁦Get Data > From PDF⁩خیرخیرچند فایل در ماه، تحلیل در Excelجدول‌های بدون خط، فایل‌های زیاد با صفحه‌آرایی متفاوت
pdfplumber (پایتون)بلهخیرمتن و جدول، کار دسته‌ای، کنترل دقیقبرای جدول بدون خط تنظیم لازم دارد
Camelot (پایتون)بلهافزونه اختیاریجدول‌های خط‌دار، گزارش دقت برای هر جدولمتن آزاد پیرامون جدول‌ها
tabula-py (پایتون)بلهخیرDataFrame سریع از فایل‌های پرجدولبه Java روی سیستم نیاز دارد
Tesseract با pytesseractبلهبلهصفحه‌های اسکن‌شده، عکس اسناددقت به کیفیت اسکن بستگی دارد؛ ساختار جدول از دست می‌رود

گزینه‌های بدون کد: کپی و Excel

کپی. برای یک جدول کوچک، آن را در نمایشگر انتخاب کنید و بچسبانید. اگر هر سطر در یک سلول افتاد، از ⁦Data > Text to Columns⁩ (داده > تبدیل متن به ستون) استفاده کنید. به محض اینکه نام محصولی فاصله داشته باشد، این روش خراب می‌شود.

رابط PDF در Excel. نسخه Excel برای Microsoft 365 در Windows می‌تواند فایل‌های PDF را از طریق Power Query بخواند:

  1. یک کارپوشه خالی باز کنید و به ⁦Data > Get Data > From File > From PDF⁩ بروید.
  2. فایل PDF را انتخاب کنید و روی Open بزنید.
  3. پنجره Navigator آنچه Power Query پیدا کرده را فهرست می‌کند: هر جدول شناسایی‌شده (Table001، Table002…) و هر صفحه کامل (Page001…). روی هر مورد بزنید تا پیش‌نمایشش را ببینید.
  4. گزینه Load را برای آوردن جدول به یک برگه انتخاب کنید، یا Transform Data را تا پیش از آن در ویرایشگر Power Query تمیزش کنید (حذف سطرهای سرتیتر تکراری، تغییر نوع ستون قیمت به عدد).
  5. ماه بعد، فایل را جایگزین کنید و روی ⁦Data > Refresh All⁩ (داده > تازه‌سازی همه) بزنید؛ همان گام‌ها دوباره اجرا می‌شوند.

مستندات رابط PDF مایکروسافت گزینه‌های پشت آن را فهرست می‌کند: Pdf.Tables برای فایل‌های بزرگ StartPage و EndPage را می‌پذیرد و گزینه‌ای به نام MultiPageTables دارد که جدولی را که در چند صفحه ادامه دارد یکی می‌کند. این رابط OCR انجام نمی‌دهد و Power Query در Excel برای Mac اصلاً PDF را به‌عنوان منبع نشان نمی‌دهد. رابط Folder فایل‌های PDF با صفحه‌آرایی یکسان را ترکیب می‌کند؛ وقتی صفحه‌آرایی‌ها متفاوت باشند، نگهداری یک اسکریپت آسان‌تر است.

استخراج متن و جدول با pdfplumber

pdfplumber کتابخانه‌ای پایتونی است که روی pdfminer.six ساخته شده و جدول‌یاب خودش را دارد. نسخه فعلی 0.11.10 است (ژوئن 2026)؛ فایل README آن می‌گوید روی PDFهای ماشین‌ساخته بهترین کارکرد را دارد و OCR انجام نمی‌دهد. آن را در یک محیط مجازی نصب کنید:

bash
python -m venv .venv
.venv\Scripts\activate        # macOS/Linux: source .venv/bin/activate
pip install pdfplumber pandas

اصول کار در چند خط جا می‌شود:

python
import pdfplumber

with pdfplumber.open("pdfs/price-list-2026-08.pdf") as pdf:
    page = pdf.pages[0]
    print(page.extract_text())          # all text, line by line
    table = page.extract_table()        # the largest table on the page
    print(table[:2])
# [['SKU', 'Product', 'Unit', 'Price'], ['B-001', 'Cable set 1', 'box', '13.40']]

تابع extract_tables() همه جدول‌های صفحه را به‌صورت فهرست‌هایی از رشته‌های سلول برمی‌گرداند. pdfplumber به‌طور پیش‌فرض دنبال خط‌های رسم‌شده می‌گردد؛ برای جدول بدون خط، راهبرد متنی را انتخاب کنید:

python
settings = {"vertical_strategy": "text", "horizontal_strategy": "text"}
rows = page.extract_tables(table_settings=settings)

راهبرد را با جدول هماهنگ کنید. روی فهرست قیمت خط‌دار ما، راهبرد متنی «Cable set 1» را به دو ستون شکست و «Unit» و «Price» را یکی کرد، چون از روی فاصله واژه‌ها چیزی را حدس زد که خط‌ها از پیش نشان می‌دادند. وقتی فقط بخشی از صفحه جدول است، نخست با page.crop((x0, top, x1, bottom)) آن را برش دهید.

یک اسکریپت کامل: پوشه‌ای از PDFها به یک CSV

اسکریپت زیر همه PDFهای یک پوشه را می‌خواند، شماره سند را از متن صفحه نخست برمی‌دارد، سطرهای جدول را از همه صفحه‌ها بیرون می‌کشد، سرتیترهای تکراری را رد می‌کند، قیمت‌ها را به عدد تبدیل می‌کند و یک فایل CSV می‌نویسد. صفحه‌های بدون لایه متنی برای OCR ثبت می‌شوند و یک فایل خراب کل دسته را متوقف نمی‌کند.

python
import csv
import logging
import re
from pathlib import Path

import pdfplumber

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

IN_DIR = Path("pdfs")
OUT_CSV = Path("price_lists.csv")
HEADER = ["SKU", "Product", "Unit", "Price"]
DOC_NO = re.compile(r"price list (\S+)", re.IGNORECASE)


def to_number(text):
    """'1,007.50' -> 1007.5; returns None for empty or broken cells."""
    if not text:
        return None
    try:
        return float(text.replace(",", "").strip())
    except ValueError:
        return None


def rows_from_pdf(path):
    with pdfplumber.open(path) as pdf:
        first_text = pdf.pages[0].extract_text() or ""
        match = DOC_NO.search(first_text)
        doc_no = match.group(1) if match else path.stem

        for page in pdf.pages:
            if not page.chars:  # no text layer: probably a scan
                logging.warning("%s p.%d has no text layer, needs OCR", path.name, page.page_number)
                continue
            for table in page.extract_tables():
                for row in table:
                    cells = [(c or "").strip() for c in row]
                    if cells == HEADER:  # header repeated on every page
                        continue
                    if len(cells) != len(HEADER):
                        logging.warning("%s p.%d skipped row %r", path.name, page.page_number, cells)
                        continue
                    sku, product, unit, price = cells
                    yield {
                        "file": path.name,
                        "doc_no": doc_no,
                        "page": page.page_number,
                        "sku": sku,
                        "product": product,
                        "unit": unit,
                        "price": to_number(price),
                    }


def main():
    fields = ["file", "doc_no", "page", "sku", "product", "unit", "price"]
    count = 0
    with OUT_CSV.open("w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        for path in sorted(IN_DIR.glob("*.pdf")):
            try:
                for row in rows_from_pdf(path):
                    writer.writerow(row)
                    count += 1
            except Exception as exc:  # one broken file should not stop the batch
                logging.error("%s failed: %s", path.name, exc)
    logging.info("wrote %d rows to %s", count, OUT_CSV)


if __name__ == "__main__":
    main()

روی پوشه آزمایشی ما (یک فهرست قیمت 70سطری در دو صفحه، یک فهرست 8سطری و یک نسخه اسکن‌شده) خروجی این بود:

bash
WARNING price-list-scan.pdf p.1 has no text layer, needs OCR
INFO wrote 78 rows to price_lists.csv

کدگذاری utf-8-sig باعث می‌شود Excel نام‌های دارای حروف تکیه‌دار مانند «Café» را درست نشان دهد، و ستون‌های file و doc_no و page در هر سطر هر عدد را به صفحه‌اش برمی‌گردانند. اینکه سطرها را پس از این کجا ذخیره کنید در ذخیره داده‌های اسکرپینگ در CSV، JSON و SQLite آمده و مرحله پاک‌سازی در پاک‌سازی داده‌های اسکرپینگ با pandas در Python.

Camelot و tabula-py: چه زمانی به کارشان ببریم

دو کتابخانه دیگر فقط روی جدول تمرکز دارند.

Camelot در ژوئن 2026 به نسخه 2.0.0 رسید. تابع read_pdf() به‌طور پیش‌فرض برای جدول‌های خط‌دار از حالت lattice استفاده می‌کند؛ حالت‌های stream و network جدول‌های بدون خط را پوشش می‌دهند و auto برای هر صفحه خودش انتخاب می‌کند. هر جدول یک parsing_report با امتیاز دقت دارد که برای علامت‌گذاری صفحه‌های مشکل‌دار به کار می‌آید. مستندات Camelot را ببینید.

tabula-py پوششی پایتونی روی tabula-java است، پس به نصب Java نیاز دارد. تابع tabula.read_pdf() مستقیم فهرستی از DataFrameهای pandas برمی‌گرداند.

python
import camelot
import tabula

PDF = "pdfs/price-list-2026-09.pdf"

tables = camelot.read_pdf(PDF, pages="1-end", flavor="lattice")
for t in tables:
    print(t.page, t.shape, t.parsing_report["accuracy"])
tables[0].df.to_csv("camelot_page1.csv", index=False)

dfs = tabula.read_pdf(PDF, pages="all", lattice=True)
print(len(dfs), [df.shape for df in dfs])

روی فهرست قیمت دوصفحه‌ای ما، هر دو کتابخانه در هر صفحه یک جدول پیدا کردند. Camelot دقت 100.0 گزارش داد و سرتیتر را به‌عنوان سطر نخست نگه داشت؛ tabula از آن به‌عنوان نام ستون‌ها استفاده کرد. هر سه کتابخانه جدول خط‌دار را درست خواندند، پس بر اساس بقیه کار انتخاب کنید: pdfplumber اگر متن آزاد هم لازم دارید، Camelot برای امتیاز جداگانه هر جدول و tabula-py برای گرفتن DataFrame با کمترین کد، به شرط نصب بودن Java.

PDFهای اسکن‌شده: OCR با Tesseract

وقتی صفحه لایه متنی ندارد، باید نویسه‌ها را از روی تصویر تشخیص دهید. Tesseract موتور OCR متن‌باز رایج است و pytesseract پوشش پایتونی آن. این پوشش خود موتور را در بر ندارد: نخست خود Tesseract را نصب کنید (اگر پس از نصب در PATH نبود، مانند نمونه زیر tesseract_cmd را تنظیم کنید)، سپس:

bash
pip install pytesseract pypdfium2

این اسکریپت هر صفحه را با 300 DPI و در طیف خاکستری رندر می‌کند، OCR می‌گیرد و میانگین اطمینان واژه‌ها را چاپ می‌کند که نشان می‌دهد کدام صفحه‌ها به بازبینی انسانی نیاز دارند:

python
import sys

import pypdfium2 as pdfium
import pytesseract

# On Windows, point pytesseract at the binary if it is not on PATH:
# pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"


def ocr_pdf(path, lang="eng", dpi=300):
    pdf = pdfium.PdfDocument(path)
    pages = []
    for i, page in enumerate(pdf, start=1):
        image = page.render(scale=dpi / 72).to_pil().convert("L")  # grayscale helps
        text = pytesseract.image_to_string(image, lang=lang, config="--psm 6")
        pages.append(text)
        # Word-level confidence: -1 means "not a word", 0-100 otherwise.
        data = pytesseract.image_to_data(image, lang=lang, output_type=pytesseract.Output.DICT)
        confs = [float(c) for c in data["conf"] if float(c) >= 0]
        if confs:
            print(f"page {i}: mean confidence {sum(confs) / len(confs):.1f}")
    return pages


if __name__ == "__main__":
    try:
        for text in ocr_pdf(sys.argv[1] if len(sys.argv) > 1 else "scans/price-list-scan.pdf"):
            print(text)
    except pytesseract.TesseractNotFoundError:
        sys.exit("Tesseract is not installed or not on PATH")

عدد 300 DPI از راهنمای بهبود کیفیت Tesseract آمده است. گزینه --psm 6 صفحه را یک بلوک یکدست متن در نظر می‌گیرد. همین راهنما یادآوری می‌کند که جدول‌ها به قطعه‌بندی سفارشی نیاز دارند و لبه‌های اسکن ممکن است نویسه خوانده شوند.

OCR عدد 0 را با حرف O، عدد 1 را با حرف l و ویرگول را با نقطه در قیمت‌ها اشتباه می‌گیرد و اسکن‌های کم‌رنگ یا کج اوضاع را بدتر می‌کنند. برای عددهای مهم بررسی کنید که جمع اقلام با مبلغ کل بخواند و صفحه‌های کم‌اطمینان را به بازبینی انسانی بسپارید. OCR متن برمی‌گرداند، نه ساختار جدول: ستون‌ها را با یک عبارت باقاعده برای هر سطر بازسازی کنید یا افزونه اختیاری ocr در Camelot را امتحان کنید.

استخراج داده از PDF کجا به کار می‌رود

  • فهرست قیمت تأمین‌کنندگان. PDFهای ماهانه به تاریخچه قیمتی تبدیل می‌شوند که می‌توانید در طول زمان مقایسه‌اش کنید، یعنی ورودی پایش قیمت.
  • گزارش‌های بازار و صنعت. جدول‌های گزارش‌های عمومی به‌جای تایپ دوباره، مستقیم وارد یک مدل تحقیقات بازار می‌شوند.
  • فاکتورها و صورت‌حساب‌ها. جمع‌ها، تاریخ‌ها و شماره‌های مرجع به حسابداری یا تطبیق حساب منتقل می‌شوند، کاری رایج در تیم‌های مالی.
  • اسناد رسمی و آمار عمومی. بسیاری از نهادها هنوز جدول‌ها را فقط به‌صورت PDF منتشر می‌کنند؛ یک اسکریپت سال‌ها از این جدول‌ها را به یک مجموعه‌داده تبدیل می‌کند، گام نخست رایج پیش از داده‌کاوی.
  • زنجیره‌های ETL. تجزیه PDF همان بخش «extract» در یک جریان بزرگ‌تر است؛ نوشته ETL چیست توضیح می‌دهد گام‌های تبدیل و بارگذاری چگونه پیرامون آن قرار می‌گیرند.

دانلود تعداد زیادی PDF از وب‌سایت‌ها

PDFها اغلب در درگاه تأمین‌کننده یا صفحه انتشارات یک نهاد ناظر قرار دارند. دانلود صدها فایل کار وب اسکرپینگ است: اگر دانلود رسمی یا API وجود دارد از آن استفاده کنید، فایل robots.txt و شرایط استفاده را بخوانید و میان درخواست‌ها فاصله بگذارید. دانلودکننده زیر فایل‌هایی را که از قبل دارد رد می‌کند، خطاهای 4xx را دوباره امتحان نمی‌کند، در پاسخ HTTP 429 به Retry-After احترام می‌گذارد و بررسی می‌کند که پاسخ واقعاً PDF باشد:

python
import os
import time
from pathlib import Path
from urllib.parse import urlparse

import requests

PROXY = os.environ.get("PROXY_URL", "http://user:pass@pr.proxynet.io:8000")
URLS = os.environ.get("PDF_URLS", "https://example.com/reports/price-list.pdf").split()
OUT = Path("pdfs")
OUT.mkdir(exist_ok=True)

session = requests.Session()
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "price-list-archiver/1.0 (contact: data@example.com)"


def download(url, retries=3):
    target = OUT / Path(urlparse(url).path).name
    if target.exists():  # already fetched on an earlier run
        return target
    for attempt in range(1, retries + 1):
        try:
            r = session.get(url, timeout=30)
            if r.status_code == 429:
                time.sleep(int(r.headers.get("Retry-After", 30)))
                continue
            if 400 <= r.status_code < 500:  # 403, 404...: retrying will not help
                print(f"{url} returned {r.status_code}, skipped")
                return None
            r.raise_for_status()
            if not r.content.startswith(b"%PDF"):  # an HTML error page, not a PDF
                print(f"{url} did not return a PDF, skipped")
                return None
            target.write_bytes(r.content)
            return target
        except requests.RequestException as exc:
            print(f"attempt {attempt} failed: {exc}")
            time.sleep(2 ** attempt)
    return None


for url in URLS:
    print(url, "->", download(url))
    time.sleep(2)  # be gentle with the host

از طریق یک پروکسی آزمایشی محلی با user:pass، یک PDF معتبر ذخیره شد، یک فایل .pdf جعلی و یک پاسخ 404 رد شدند و اجرای دوم هیچ فایلی را دوباره دریافت نکرد. پروکسی زمانی کمک می‌کند که منبعی بسته به کشور سندهای متفاوتی ارائه دهد (یک پروکسی مسکونی با کشور هدف) یا دانلود طولانی یک بایگانی به محدودیت‌های هر IP بخورد (یک پروکسی چرخشی). با این حال، کند کردن سرعت همچنان در اولویت است، همان‌طور که نوشته ⁦HTTP 429 Too Many Requests⁩ توضیح می‌دهد.

مشکلات رایج و راه‌حل آن‌ها

  • سلول‌های ادغام‌شده به‌صورت None برمی‌گردند. pdfplumber فقط سطر نخست یک سلول ادغام‌شده را پر می‌کند؛ تابع ffill() در pandas آن مقدار را به سطرهای پایین کپی می‌کند (نمونه در ادامه).
  • جدول در صفحه بعد ادامه دارد. صفحه‌به‌صفحه استخراج کنید، سطرهای سرتیتر تکراری را حذف کنید (همان کاری که اسکریپت بالا می‌کند) و نتیجه‌ها را به هم بچسبانید. در Excel گزینه MultiPageTables این کار را برایتان انجام می‌دهد.
  • عددها به‌صورت متن می‌رسند. "1,007.50"، "(250.00)" برای عدد منفی و "1.007,50" در یک فایل آلمانی. بر اساس قالب هر فایل یکدست کنید و هرگز جداکننده اعشار را از روی یک مقدار تنها حدس نزنید.
  • حروف تکیه‌دار به‌هم‌ریخته دیده می‌شوند. معمولاً مشکل از نمایشگر است نه متن: کنسول Windows، یا Excel که ⁦UTF-8⁩ بدون BOM را باز می‌کند (نوشته خطاهای کدگذاری یونیکد در پایتون را ببینید). اگر خود extract_text() نمادهای عجیب برمی‌گرداند، قلم نگاشت یونیکد ندارد و OCR راه مطمئن است.
  • ستون‌ها در جدول‌های بدون خط جابه‌جا می‌شوند. به راهبرد متنی بروید، ناحیه جدول را برش دهید یا موقعیت ستون‌ها را با "vertical_strategy": "explicit" و "explicit_vertical_lines" صریح مشخص کنید.
  • صفر سطر، بدون هیچ خطا. صفحه اسکن است، جدول یک تصویر است یا فایل رمز دارد (برای فایل‌هایی که اجازه باز کردنشان را دارید pdfplumber.open(path, password="...")).

این هم راه‌حل کامل سلول ادغام‌شده، آزموده روی جدولی که در آن «North» دو سطر را پوشش می‌دهد:

python
import pandas as pd
import pdfplumber

with pdfplumber.open("merged.pdf") as pdf:
    rows = pdf.pages[0].extract_table()

df = pd.DataFrame(rows[1:], columns=rows[0])
df["Region"] = df["Region"].ffill()  # a merged cell comes back as None below its first row
df["Price"] = pd.to_numeric(df["Price"].str.replace(",", ""), errors="coerce")
print(df)

راهنمای انتخاب

نیازپیشنهاد
یک جدول از یک فایل، یک بارکپی و چسباندن، سپس ⁦Text to Columns⁩
چند PDF متنی در ماه، تحلیل در ExcelExcel، مسیر ⁦Data > Get Data > From File > From PDF⁩
متن و جدول از یک پوشه، هر ماهاسکریپت pdfplumber که CSV می‌نویسد
جدول‌های خط‌دار با امتیاز کیفیت برای هر جدولCamelot، حالت lattice
DataFrame با کمترین کد، در صورت وجود Javatabula-py
صفحه‌های اسکن‌شده یا عکسرندر با pypdfium2 در 300 DPI به‌علاوه Tesseract، با بازبینی انسانی عددها
صدها PDF از یک وب‌سایتنخست دانلود رسمی یا API؛ در غیر این صورت دانلودکننده‌ای با فاصله میان درخواست‌ها، و پروکسی اگر منبع منطقه‌ای است

پرسش‌های متداول

آیا می‌توان رایگان از PDF داده استخراج کرد؟

بله. pdfplumber، Camelot، tabula-py و Tesseract متن‌باز هستند و رابط PDF بخشی از Excel برای Microsoft 365 در Windows است. ابزارهای پولی بیشتر ویرایشگر قالب و OCR بهتر برای اسکن‌های بی‌کیفیت اضافه می‌کنند.

چطور یک جدول را از PDF به Excel ببرم؟

در Excel برای Microsoft 365 در Windows، مسیر ⁦Data > Get Data > From File > From PDF⁩ را بروید، جدول را در پنجره Navigator انتخاب کنید و روی Load بزنید. در پایتون، سطرها را با pdfplumber استخراج کنید و با to_excel() در pandas یا به‌صورت یک CSV که Excel باز می‌کند بنویسید.

چرا استخراج از PDF من متن خالی برمی‌گرداند؟

به احتمال زیاد صفحه لایه متنی ندارد. فهرست page.chars را در pdfplumber بررسی کنید یا در یک نمایشگر ⁦Ctrl+F⁩ را امتحان کنید؛ اگر چیزی پیدا نشد، OCR بگیرید.

آیا pdfplumber برای جدول از PyPDF بهتر است؟

برای جدول، بله. pypdf برای جدا کردن، ادغام کردن و خواندن متن ساده خوب است، اما جدول‌یاب ندارد. pdfplumber موقعیت هر نویسه را نگه می‌دارد و سلول‌ها را از روی خط‌ها یا هم‌ترازی بازسازی می‌کند، و استخراج جدول دقیقاً به همین نیاز دارد.

دقت OCR روی PDFهای اسکن‌شده چقدر است؟

به اسکن بستگی دارد. صفحه‌های چاپی تمیز با 300 DPI معمولاً خوب درمی‌آیند؛ اسکن‌های کج، کم‌رنگ یا کم‌وضوح، دست‌خط و قلم‌های ریز نه. امتیازهای اطمینان را بررسی کنید و جمع‌ها را تطبیق دهید.

آیا استخراج داده از PDFهایی که دانلود می‌کنم قانونی است؟

استخراج داده از فایل‌هایی که حق استفاده از آن‌ها را دارید (فاکتورهای خودتان، گزارش‌های عمومی) معمولاً مشکلی ندارد، اما محتوا ممکن است همچنان مشمول حق نشر باشد و داده‌های شخصی درون آن تابع قوانینی مانند GDPR است. پیش از دانلود انبوه، شرایط منبع را بخوانید؛ نوشته ما درباره قانونی بودن وب اسکرپینگ چارچوب کلی را توضیح می‌دهد. این مطلب مشاوره حقوقی نیست.

خلاصه

هر کار با PDF با یک بررسی شروع می‌شود: آیا لایه متنی وجود دارد؟ اگر وجود دارد، رابط PDF در Excel جدول‌های گاه‌به‌گاه را پوشش می‌دهد و یک اسکریپت pdfplumber یک پوشه را به یک CSV تبدیل می‌کند، و برای کارهای صرفاً جدولی Camelot یا tabula-py هم هست. اگر وجود ندارد، با 300 DPI رندر کنید، Tesseract را اجرا کنید و عددها را بررسی کنید. فایل مبدأ و شماره صفحه را در هر سطر نگه دارید و با دانلود انبوه PDF مانند هر کار اسکرپینگ دیگری رفتار کنید. وقتی این دانلودها از منابع منطقه‌ای یا دارای محدودیت نرخ می‌آیند، طرح‌های پروکسی ما نشانی‌هایی را که این زنجیره لازم دارد فراهم می‌کنند.

پرسش از ChatGPTپرسش از Claude