PythonでPDF操作を完全マスター【PyPDF2・reportlab徹底ガイド】
PDF(Portable Document Format)は、ビジネスや学術分野で最も使用される文書形式の一つです。本記事では、Pythonを使ったPDF操作の方法を、読み取りから作成、編集まで徹底解説します。初心者から上級者まで、実用的なサンプルコードとともに学べる内容です。
目次
PDFとは
PDFは、Adobe Systemsが開発した文書形式で、異なるプラットフォーム間でも同じレイアウトを保持できる特徴があります。主な用途:
- 公式文書・契約書
- レポート・論文
- 電子書籍
- プレゼンテーション資料
- フォーム・申請書
主要なPython PDFライブラリ
1. PyPDF2 / pypdf
- PDF読み取り・分割・結合に特化
- 軽量で使いやすい
- テキスト抽出機能
2. reportlab
- PDF作成に特化
- 高度なレイアウト制御
- グラフ・図表作成
3. pdfplumber
- 高精度なテキスト・表抽出
- 座標情報も取得可能
- データ分析に最適
4. PyMuPDF (fitz)
- 高速処理
- 画像抽出・変換
- 注釈・編集機能
環境構築とインストール
# 基本ライブラリ
pip install pypdf reportlab pdfplumber
# 追加ライブラリ
pip install PyMuPDF pillow matplotlib
PDF読み取りの基本
PyPDFでテキスト抽出
import pypdf
with open('sample.pdf', 'rb') as file:
reader = pypdf.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text()
print(text)
特定ページの読み取り
import pypdf
with open('sample.pdf', 'rb') as file:
reader = pypdf.PdfReader(file)
page = reader.pages[0] # 1ページ目
print(page.extract_text())
PDFの基本情報取得
import pypdf
with open('sample.pdf', 'rb') as file:
reader = pypdf.PdfReader(file)
info = reader.metadata
print(f"ページ数: {len(reader.pages)}")
print(f"タイトル: {info.title}")
print(f"作成者: {info.author}")
pdfplumberを使った高精度抽出
テキストと表の抽出
import pdfplumber
with pdfplumber.open('sample.pdf') as pdf:
for page in pdf.pages:
text = page.extract_text()
tables = page.extract_tables()
print(f"テキスト: {text}")
print(f"表データ: {tables}")
CSVファイルへの表データ出力
import pdfplumber
import csv
with pdfplumber.open('table.pdf') as pdf:
table = pdf.pages[0].extract_table()
with open('output.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(table)
PDF作成の基本(reportlab)
最小のPDF作成
from reportlab.pdfgen import canvas
c = canvas.Canvas("hello.pdf")
c.drawString(100, 750, "Hello World!")
c.save()
日本語対応PDF作成
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))
c = canvas.Canvas("japanese.pdf")
c.setFont('HeiseiKakuGo-W5', 12)
c.drawString(100, 750, "こんにちは、世界!")
c.save()
複数ページのPDF作成
from reportlab.pdfgen import canvas
c = canvas.Canvas("multipage.pdf")
for i in range(3):
c.drawString(100, 750, f"Page {i+1}")
c.showPage() # 新しいページ
c.save()
PDF操作の応用
PDFファイルの結合
import pypdf
merger = pypdf.PdfMerger()
files = ['file1.pdf', 'file2.pdf', 'file3.pdf']
for file in files:
merger.append(file)
merger.write('merged.pdf')
merger.close()
PDFファイルの分割
import pypdf
with open('source.pdf', 'rb') as file:
reader = pypdf.PdfReader(file)
for i, page in enumerate(reader.pages):
writer = pypdf.PdfWriter()
writer.add_page(page)
with open(f'page_{i+1}.pdf', 'wb') as output:
writer.write(output)
PDFページの回転
import pypdf
with open('source.pdf', 'rb') as file:
reader = pypdf.PdfReader(file)
writer = pypdf.PdfWriter()
for page in reader.pages:
page.rotate(90) # 90度回転
writer.add_page(page)
with open('rotated.pdf', 'wb') as output:
writer.write(output)
高度なPDF作成
表付きPDFの作成
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
from reportlab.lib import colors
doc = SimpleDocTemplate("table.pdf", pagesize=A4)
data = [['名前', '年齢', '職業'], ['田中', '30', 'エンジニア'], ['佐藤', '25', 'デザイナー']]
table = Table(data)
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 14),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
doc.build([table])
グラフ付きPDF作成
from reportlab.pdfgen import canvas
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.renderPDF import drawToFile
# グラフ作成
drawing = Drawing(400, 200)
chart = VerticalBarChart()
chart.x = 50
chart.y = 50
chart.height = 125
chart.width = 300
chart.data = [[10, 20, 30, 40]]
chart.categoryAxis.categoryNames = ['A', 'B', 'C', 'D']
drawing.add(chart)
# PDF保存
drawToFile(drawing, fmt='PDF', fnRoot='chart')
実用的な応用例
請求書生成システム
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
import datetime
def create_invoice(invoice_data):
c = canvas.Canvas("invoice.pdf", pagesize=A4)
width, height = A4
# ヘッダー
c.setFont("Helvetica-Bold", 16)
c.drawString(50, height-50, "請求書")
# 日付
c.setFont("Helvetica", 12)
c.drawString(50, height-100, f"発行日: {datetime.date.today()}")
# 明細
y = height - 150
for item in invoice_data:
c.drawString(50, y, f"{item['name']}: ¥{item['price']:,}")
y -= 20
c.save()
# 使用例
invoice_data = [{'name': '商品A', 'price': 1000}, {'name': '商品B', 'price': 2000}]
create_invoice(invoice_data)
Web上のPDFダウンロード・処理
import requests
import pypdf
from io import BytesIO
def download_and_extract(url):
response = requests.get(url)
pdf_file = BytesIO(response.content)
reader = pypdf.PdfReader(pdf_file)
text = ""
for page in reader.pages:
text += page.extract_text()
return text
# 使用例(架空のURL)
# text = download_and_extract('https://example.com/sample.pdf')
PDF内画像抽出
import fitz # PyMuPDF
def extract_images(pdf_path):
doc = fitz.open(pdf_path)
for page_num in range(doc.page_count):
page = doc[page_num]
image_list = page.get_images()
for img_index, img in enumerate(image_list):
xref = img[0]
pix = fitz.Pixmap(doc, xref)
if pix.n - pix.alpha < 4: # GRAY or RGBカラースペース
pix.save(f"image_page{page_num}_{img_index}.png")
pix = None
doc.close()
PDFフォーム処理
フォームフィールドの読み取り
import pypdf
with open('form.pdf', 'rb') as file:
reader = pypdf.PdfReader(file)
if reader.is_encrypted:
reader.decrypt('')
fields = reader.get_form_text_fields()
for field_name, field_value in fields.items():
print(f"{field_name}: {field_value}")
フォームフィールドの書き込み
import pypdf
reader = pypdf.PdfReader('form.pdf')
writer = pypdf.PdfWriter()
# フォームフィールドに値を設定
writer.clone_reader_document_root(reader)
writer.update_page_form_field_values(
writer.pages[0], {'field_name': 'new_value'}
)
with open('filled_form.pdf', 'wb') as output:
writer.write(output)
パフォーマンス最適化
メモリ効率的な大容量PDF処理
import pypdf
def process_large_pdf(pdf_path, output_path):
with open(pdf_path, 'rb') as input_file:
reader = pypdf.PdfReader(input_file)
writer = pypdf.PdfWriter()
# ページ単位で処理
for page_num in range(len(reader.pages)):
page = reader.pages[page_num]
# 必要な処理を実行
writer.add_page(page)
# メモリ解放
if page_num % 100 == 0:
print(f"処理中: {page_num}/{len(reader.pages)}")
with open(output_path, 'wb') as output_file:
writer.write(output_file)
バッチ処理
import os
import pypdf
from concurrent.futures import ThreadPoolExecutor
def process_pdf(file_path):
try:
with open(file_path, 'rb') as file:
reader = pypdf.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text()
return f"{file_path}: {len(text)} characters"
except Exception as e:
return f"{file_path}: Error - {e}"
def batch_process(folder_path):
pdf_files = [f for f in os.listdir(folder_path) if f.endswith('.pdf')]
full_paths = [os.path.join(folder_path, f) for f in pdf_files]
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(process_pdf, full_paths))
return results
エラーハンドリング
暗号化PDF対応
import pypdf
import getpass
def read_encrypted_pdf(pdf_path):
try:
with open(pdf_path, 'rb') as file:
reader = pypdf.PdfReader(file)
if reader.is_encrypted:
password = getpass.getpass("PDFパスワードを入力: ")
if not reader.decrypt(password):
print("パスワードが間違っています")
return None
text = ""
for page in reader.pages:
text += page.extract_text()
return text
except Exception as e:
print(f"エラー: {e}")
return None
堅牢なPDF処理
import pypdf
import logging
def safe_pdf_process(pdf_path):
try:
with open(pdf_path, 'rb') as file:
reader = pypdf.PdfReader(file)
# PDFの整合性チェック
if len(reader.pages) == 0:
raise ValueError("PDFにページがありません")
results = []
for i, page in enumerate(reader.pages):
try:
text = page.extract_text()
results.append(text)
except Exception as page_error:
logging.warning(f"ページ {i+1} の処理でエラー: {page_error}")
results.append("")
return results
except Exception as e:
logging.error(f"PDF処理エラー: {e}")
return []
PDFライブラリ比較表
| ライブラリ | 読み取り | 作成 | 編集 | 速度 | 日本語対応 |
|---|---|---|---|---|---|
| PyPDF2/pypdf | ○ | △ | ○ | 中 | ○ |
| reportlab | × | ◎ | × | 中 | ○ |
| pdfplumber | ◎ | × | × | 低 | ○ |
| PyMuPDF | ◎ | ○ | ◎ | 高 | ○ |
トラブルシューティング
よくある問題と対処法
# 文字化け対策
import pypdf
def fix_encoding_issues(pdf_path):
with open(pdf_path, 'rb') as file:
reader = pypdf.PdfReader(file)
text = ""
for page in reader.pages:
page_text = page.extract_text()
# エンコーディング問題の修正
try:
page_text = page_text.encode('latin1').decode('utf-8')
except:
pass
text += page_text
return text
# ファイルサイズ最適化
def optimize_pdf_size(input_path, output_path):
reader = pypdf.PdfReader(input_path)
writer = pypdf.PdfWriter()
for page in reader.pages:
writer.add_page(page)
# 圧縮設定
writer.compress_identical_objects()
with open(output_path, 'wb') as output:
writer.write(output)
まとめ
PythonでのPDF操作は、適切なライブラリ選択により様々な処理が可能です。読み取りにはpypdfやpdfplumber、作成にはreportlab、高度な編集にはPyMuPDFを使い分けることで、効率的なPDF処理システムを構築できます。
本記事のサンプルコードを参考に、あなたのプロジェクトに最適なPDFソリューションを実装してください。エラーハンドリングとパフォーマンス最適化も忘れずに実装することで、実用的なシステムが完成します。
参考文献
- PyPDF2/pypdf公式ドキュメント
- reportlab公式ドキュメント
- pdfplumber GitHub
- PyMuPDF公式ドキュメント
■らくらくPython塾 – 読むだけでマスター
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座


