0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PdfPlumber 大量pPDFファイル一括処理:行が揺れてるPDF:コード解説_1

0
Last updated at Posted at 2026-02-07

ここでは

のプロジェクトコードの詳細を初心者の方にも分かりやすく解説します。

コードをの動きを試したい方、以下のサンプルファイルを使って下さい
内容は:
1_dummy_table_3pages.pdf

  • 1ページ目:テーブルあり
  • 2ページ目:別形式のテーブル(分割、改行された行あり) → 行の揺れ
  • 3ページ目:1行のテーブル → ページがなくなる

2_dummy_table_3pages.pdf

  • 1ページ目:テーブルなし → ページの揺れ
  • 2ページ目:テーブルあり
  • 3ページ目:テーブルあり

これらのファイルのテーブルをどのように処理するか?

1.サンプル PDF をダウンロードする

以下の 2 つの PDF を GitHub に置いてあります。
クリックするとブラウザから直接ダウンロードできます。
ダウンロードボタンをクリックするだけ!

2. ダウンロードした PDF を同じフォルダに入れる

C:/Users/あなたの名前/Desktop/Hongo2_C190/
    ├── 1_dummy_table_3pages.pdf
    └── 2_dummy_table_3pages.pdf

フォルダ名は自由ですが、
日本語を含まないパス にしておくとトラブルが少なくなります。

3. フォルダを Desktop に配置する

作成したフォルダを デスクトップに置く と、
Python からパスを指定しやすくなります。

C:/Users/あなたの名前/Desktop/Hongo2_C190/

4. コードの src_dir にフォルダのパスを設定する

コード内の Config の部分を開きます。

config = Config(
    src_dir=Path(r"C:/Users/あなたの名前/Desktop/Hongo2_C190"),
    dst_dir=Path(r"C:/Users/あなたの名前/Desktop/Qiita_投稿_Hongo/Collection_Hongo"),
    file_slice=slice(None),
    page_slice=slice(0,1),
    row_slice=slice(1, 2),
    output_csv='Smart_extracted5.csv'
)

✔ 変更するのは src_dir の 1 行だけ

※ バックスラッシュ \ の代わりに、スラッシュ / を使ってください。
Python では / の方が安全です。

この記事で分かること

- 実務で採用するpythonチュートリアル
- プロジェクト全体の実行フロー
- Config クラスの役割
- 関数定義と実行の違い
- main() から convert_pdfs が呼ばれるまでの流れ

ここは初心者向きですので関数実行のフローが見えてる方はスキップして下さい

AIのClaudeサン、Copilotサンのお手伝い頂きました

1. :convert_pdfを呼び出すまで

コード全体の構造

┌────────────────────────────────────────────────┐
│  1. ログ設定(プログラム起動時に1回だけ)      
├────────────────────────────────────────────────
│  2. Configクラスの定義                         
├────────────────────────────────────────────────
│  3. 関数定義(prepare_files, extract_table等) 
├────────────────────────────────────────────────
│  4. メイン処理(if __name__ == "__main__":)   
│     ├─ Configインスタンス作成                  
│     ├─ convert_pdfs(config) 呼び出し           
│     └─ cleanup(config.dst_dir) 呼び出し        
└────────────────────────────────────────────────┘

🎬 実行の流れ(ステップバイステップ)
ステップ1️⃣ インポート文の実行(最初の方)

import logging
from pathlib import Path
from dataclasses import dataclass
import shutil
...

ステップ2️⃣ログ設定の実行

# ===== ログ設定 =====
logging.basicConfig(
    level=logging.INFO,
    format='[%(levelname)s] %(message)s',
    handlers=[
        logging.FileHandler("log.txt", encoding="utf-8"),
        logging.StreamHandler(),
    ]
)

logger = logging.getLogger(__name__)

何が起こる?

┌─────────────────────────────────────┐
│ ログシステムの初期設定                  
├─────────────────────────────────────
│ ✅ ログレベル: INFO                   
│ ✅ 出力形式: [INFO] メッセージ         
│ ✅ 出力先1: log.txt ファイル          
│ ✅ 出力先2: コンソール(画面)          
└─────────────────────────────────────┘
        ↓
logger という名前のロガーを作成
        ↓
これ以降、logger.info("...") でログ出力できる

ステップ3️⃣ Configクラスの定義

@dataclass
class Config:
    src_dir: Path
    dst_dir: Path
    file_slice: slice
    page_slice: slice
    row_slice: slice
    output_csv: str

何が起こる?

Configという「設計図」を定義
    ↓
まだインスタンス(実体)は作られていない
    ↓
「こういう構造のデータを保存できますよ」という定義だけ

イメージ:

設計図: Config
┌──────────────────┐
│ src_dir: Path    │ ← PDFの元フォルダ
│ dst_dir: Path    │ ← 作業フォルダ
│ file_slice: slice│ ← ファイル範囲
│ page_slice: slice│ ← ページ範囲
│ row_slice: slice │ ← 行範囲
│ output_csv: str  │ ← 出力ファイル名
└──────────────────┘


ステップ5️⃣: 関数定義(prepare_files, extract_table等)

def prepare_files(config: Config) -> list[Path]:
    """PDFファイルを作業フォルダに移動"""
    ...

def extract_table(...):
    ...

def create_df(...):
    ...

def convert_pdfs(config: Config):
    ...

何が起こる?

関数の「定義」だけが行われる
    ↓
まだ実行はされない!
    ↓
「こういう処理ができますよ」という登録だけ
関数は呼び出されて初めて実行されます

イメージ:
```text
関数リスト(まだ実行されていない)
├─ prepare_files() ← PDFファイル準備
├─ extract_table() ← データ抽出
├─ create_df()     ← DataFrame作成
└─ convert_pdfs()  ← メイン処理

全部「待機中」の状態

ステップ5️⃣: メイン処理に到達

if __name__ == "__main__":
    # ここから実行開始!

if name == "main": の意味

このファイルが直接実行された場合のみ、以下を実行
    ↓
他のファイルからimportされた場合は実行しない
    ↓
「このファイルをメインとして実行したとき」という条件

いまいちの理解の方
      👇

ステップ6️⃣: Configインスタンスの作成

config = Config(
    src_dir=Path(r"C:/Users/xxx/Desktop/Hongo_C190"),
    dst_dir=Path(r"C:/Users/xxx/Desktop/PythonProject/Collection_Hongo"),
    file_slice=slice(None),  # 全ファイル
    page_slice=slice(0, 1),  # 1ページ目のみ
    row_slice=slice(1, 2),   # 2行目のみ
    output_csv='Smart_extracted.csv'
)

何が起こる:

設計図(Config)を使って、実体(インスタンス)を作成
    ↓
具体的な値を設定

視覚化:

【ステップ4で作った設計図】
Config (設計図)
┌──────────────────┐
│ src_dir: Path    │
│ dst_dir: Path    │
│ ...              │
└──────────────────┘

        ↓ 値を入れて実体化

【今作ったインスタンス】
config (実体)
┌────────────────────────────────────────────┐
│ src_dir: C:/Users/xxx/Desktop/Hongo_C190   │
│ dst_dir: C:/.../Collection_Hongo           │
│ file_slice: slice(None, None, None)        │
│ page_slice: slice(0, 1, None)              │
│ row_slice: slice(1, 2, None)               │
│ output_csv: 'Smart_extracted.csv'          │
└────────────────────────────────────────────┘

各設定の意味:

src_dir = C:/Users/godel/Desktop/Hongo_C190
    ↓
元のPDFファイルがあるフォルダ

dst_dir = C:/.../Collection_Hongo
    ↓
作業用フォルダ(ここにPDFをコピーして処理)

file_slice = slice(None)
    ↓
全ファイルを処理(制限なし)

page_slice = slice(0, 1)
    ↓
各PDFの1ページ目のみ抽出
0から始まって1の手前まで = 0番目 = 1ページ目

row_slice = slice(1, 2)
    ↓
テーブルの2行目のみ抽出
1から始まって2の手前まで = 1番目 = 2行目

output_csv = 'Smart_extracted.csv'
    ↓
出力ファイル名

サンプルPDFファイルを使ってsliceの値を変更して試して!

ステップ7️⃣: convert_pdfs(config) の呼び出し

convert_pdfs(config)

ここで初めて処理が始まる!

PdfPlumber 大量pPDFファイル一括処理:行が揺れてるPDF:コード解説_2に続く

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?