PDFからテキストを抽出し要約するPythonスクリプト

プログラミング

PDFからのテキスト抽出と要約Pythonスクリプト

PDFファイルは、文書のレイアウトを保持したまま情報を共有できる便利な形式ですが、その構造上、テキストの直接的な編集や検索が難しい場合があります。Pythonを使用することで、PDFからテキストを抽出し、さらにその内容を要約することが可能になります。このスクリプトは、大量のPDF文書を効率的に処理し、重要な情報を素早く把握するための強力なツールとなります。

スクリプトの目的と利点

このPythonスクリプトの主な目的は、以下の2点に集約されます。

  • PDFファイルからテキストデータを正確に抽出すること。
  • 抽出されたテキストデータから、内容を簡潔にまとめた要約を生成すること。

このスクリプトを利用することで、以下のような利点が得られます。

  • 情報収集の効率化: 複数のPDFファイルの内容を短時間で把握できるようになります。
  • データ分析の容易化: 抽出されたテキストデータを、さらに高度な分析や処理に活用できます。
  • 研究・学習の促進: 膨大な文献からの情報収集や、学習内容の理解を深めるのに役立ちます。
  • 業務プロセスの自動化: 書類作成、レポート作成、情報整理といった定型業務を自動化し、生産性を向上させます。

必要なライブラリ

このスクリプトを実行するには、いくつかのPythonライブラリが必要です。主に以下のライブラリが使用されます。

  • PyPDF2:PDFファイルの読み込み、ページ分割、テキスト抽出などの基本的なPDF操作を行います。
  • NLTK (Natural Language Toolkit):自然言語処理のための包括的なライブラリです。単語の分割(トークン化)、ストップワードの除去、ステミング(単語の語幹化)などの機能を提供し、テキストのクリーニングと前処理に役立ちます。
  • gensim:トピックモデリングや文書類似度計算に特化したライブラリですが、ここではテキストのベクトル化やTF-IDF(Term Frequency-Inverse Document Frequency)の計算に利用します。TF-IDFは、文書内で単語がどれだけ重要かを示す指標であり、要約生成の基盤となります。
  • scikit-learn:機械学習ライブラリですが、ここではTF-IDF計算のために利用することもあります。

これらのライブラリは、pipコマンドを使用して簡単にインストールできます。

pip install PyPDF2 nltk scikit-learn gensim

NLTKについては、初回実行時に一部のデータ(コーパス)をダウンロードする必要があります。Pythonのインタラクティブシェルで以下を実行してください。

import nltk
nltk.download('punkt')
nltk.download('stopwords')

スクリプトの実装(テキスト抽出部分)

PDFからテキストを抽出する基本的な処理は、PyPDF2ライブラリを使用して行います。

PDFファイルを開く

まず、対象のPDFファイルを開きます。

from PyPDF2 import PdfReader

def extract_text_from_pdf(pdf_path):
    text = ""
    try:
        with open(pdf_path, 'rb') as file:
            reader = PdfReader(file)
            num_pages = len(reader.pages)
            for page_num in range(num_pages):
                page = reader.pages[page_num]
                text += page.extract_text() or "" # extract_text()がNoneを返す場合があるので空文字列で補完
    except Exception as e:
        print(f"Error extracting text from {pdf_path}: {e}")
    return text

この関数は、指定されたPDFファイルのパスを受け取り、全ページのテキストを連結して返します。`rb`モードでファイルを開くのは、バイナリモードでPDFを読み込むためです。`page.extract_text()`メソッドがテキストを抽出し、`or “”`は、テキストが抽出できなかった場合にエラーを防ぐための処理です。

スクリプトの実装(テキスト前処理部分)

抽出されたテキストは、そのままではノイズが多く、要約生成に適していません。そのため、自然言語処理の技術を用いてテキストをクリーニングする必要があります。

トークン化とストップワード除去

NLTKライブラリを使用すると、テキストを単語に分割(トークン化)し、文書の重要性に影響を与えにくい一般的な単語(ストップワード)を除去できます。

import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

def preprocess_text(text):
    # 小文字に変換
    text = text.lower()
    # トークン化
    tokens = word_tokenize(text)
    # ストップワードと句読点の除去
    stop_words = set(stopwords.words('english')) # 日本語の場合は 'japanese'
    processed_tokens = [word for word in tokens if word.isalnum() and word not in stop_words]
    return processed_tokens

この関数では、まずテキスト全体を小文字に変換し、`word_tokenize`で単語に分割します。その後、`isalnum()`で英数字のみを保持し、`stopwords.words(‘english’)`で定義されている英語のストップワードを除外します。日本語のPDFを扱う場合は、`stopwords.words(‘japanese’)`に変更する必要があります。

スクリプトの実装(要約生成部分)

テキストの前処理が完了したら、いよいよ要約を生成します。ここでは、TF-IDFを利用した抽出型要約の手法を検討します。抽出型要約とは、元の文書から重要な文をそのまま抜き出す手法です。

TF-IDFによる文の重要度評価

TF-IDFは、単語の出現頻度と、その単語がどれだけ多くの文書に出現するかを考慮して、単語の重要度を計算します。このTF-IDFを用いて、各文に含まれる単語の重要度を合計し、文の重要度を評価します。

from sklearn.feature_extraction.text import TfidfVectorizer
from collections import defaultdict

def generate_summary_tfidf(text, num_sentences=3):
    # 文に分割
    sentences = nltk.sent_tokenize(text)
    if not sentences:
        return "No sentences found in the text."

    # TF-IDFベクトライザの初期化
    vectorizer = TfidfVectorizer(stop_words='english') # 日本語の場合は適切なストップワードリストを使用

    # TF-IDF行列の計算
    try:
        tfidf_matrix = vectorizer.fit_transform(sentences)
    except ValueError:
        return "Could not compute TF-IDF. Possibly due to empty vocabulary."

    # 各文のスコアを計算
    sentence_scores = defaultdict(float)
    for i, sentence in enumerate(sentences):
        for word, 
            score in zip(vectorizer.get_feature_names_out(), tfidf_matrix[i].toarray()[0]):
            sentence_scores[sentence] += score

    # スコアの高い文を上位から取得
    summary_sentences = sorted(sentence_scores, key=sentence_scores.get, reverse=True)[:num_sentences]
    summary = " ".join(summary_sentences)
    return summary

この関数では、まず`nltk.sent_tokenize`でテキストを文に分割します。次に、`TfidfVectorizer`を用いて、各文のTF-IDFベクトルを計算します。各文のスコアは、その文に含まれる単語のTF-IDF値の合計として計算され、スコアの高い文が要約として選択されます。`num_sentences`で要約に含める文の数を指定できます。

スクリプトの統合と実行例

上記で定義した関数を組み合わせて、PDFからテキストを抽出し、要約を生成する完全なスクリプトを作成します。

from PyPDF2 import PdfReader
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize, sent_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
from collections import defaultdict

def extract_text_from_pdf(pdf_path):
    text = ""
    try:
        with open(pdf_path, 'rb') as file:
            reader = PdfReader(file)
            num_pages = len(reader.pages)
            for page_num in range(num_pages):
                page = reader.pages[page_num]
                text += page.extract_text() or ""
    except Exception as e:
        print(f"Error extracting text from {pdf_path}: {e}")
    return text

def preprocess_text(text):
    text = text.lower()
    tokens = word_tokenize(text)
    stop_words = set(stopwords.words('english')) # 日本語の場合は 'japanese'
    processed_tokens = [word for word in tokens if word.isalnum() and word not in stop_words]
    return processed_tokens

def generate_summary_tfidf(text, num_sentences=3):
    sentences = sent_tokenize(text)
    if not sentences:
        return "No sentences found in the text."

    vectorizer = TfidfVectorizer(stop_words='english') # 日本語の場合は適切なストップワードリストを使用

    try:
        tfidf_matrix = vectorizer.fit_transform(sentences)
    except ValueError:
        return "Could not compute TF-IDF. Possibly due to empty vocabulary."

    sentence_scores = defaultdict(float)
    for i, sentence in enumerate(sentences):
        # vectorizer.get_feature_names_out()はAPI変更の可能性あり、後方互換性のため確認
        feature_names = vectorizer.get_feature_names_out()
        if not feature_names.size: # feature_namesが空の場合の処理
            continue
        for word, score in zip(feature_names, tfidf_matrix[i].toarray()[0]):
            sentence_scores[sentence] += score

    summary_sentences = sorted(sentence_scores, key=sentence_scores.get, reverse=True)[:num_sentences]
    summary = " ".join(summary_sentences)
    return summary

# --- 実行部分 ---
if __name__ == "__main__":
    pdf_file_path = "your_document.pdf" # ここに対象PDFファイルのパスを指定してください
    summary_length = 3 # 生成したい要約の文数

    print(f"Extracting text from: {pdf_file_path}")
    extracted_text = extract_text_from_pdf(pdf_file_path)

    if extracted_text:
        print("n--- Extracted Text (first 200 chars) ---")
        print(extracted_text[:200] + "...")

        # 要約生成には生のテキストを使用(前処理は要約関数内で行う)
        generated_summary = generate_summary_tfidf(extracted_text, num_sentences=summary_length)
        print(f"n--- Generated Summary ({summary_length} sentences) ---")
        print(generated_summary)
    else:
        print("No text extracted from the PDF.")

このスクリプトを実行するには、`pdf_file_path`変数に対象のPDFファイルのパスを設定し、`summary_length`で要約の長さを調整してください。

発展的な利用と考慮事項

このスクリプトは基本的なPDFテキスト抽出と要約の機能を提供しますが、さらに発展的な利用も可能です。

多言語対応

日本語のPDFを扱う場合、ストップワードリストを日本語用に変更する必要があります。NLTKの日本語ストップワードリストは追加でダウンロードする必要がある場合があります。

より高度な要約手法

抽出型要約以外にも、生成型要約という手法があります。これは、元の文書の内容を理解し、新しい文章で要約を生成する手法であり、より自然な要約を作成できますが、実装にはより高度な自然言語生成モデル(例:Transformerベースのモデル)が必要となります。

PDFのレイアウトと画像

このスクリプトは、テキストベースのPDFに最適です。画像としてスキャンされたPDFや、複雑なレイアウトを持つPDFからのテキスト抽出は、OCR(光学文字認識)ライブラリ(例:Tesseract OCRとpytesseract)の導入が必要になります。

エラーハンドリングと堅牢性

実際の運用では、PDFの破損、パスワード保護、エンコーディングの問題など、様々なエラーが発生する可能性があります。これらのエラーに対して、より詳細なエラーハンドリングと例外処理を実装することで、スクリプトの堅牢性を高めることができます。

パフォーマンスの最適化

大量のPDFファイルを処理する場合、パフォーマンスの最適化が重要になります。並列処理(multiprocessingライブラリなど)を活用したり、不要なメモリ使用量を削減したりすることで、処理速度を向上させることができます。

まとめ

Pythonと主要なライブラリを活用することで、PDFからのテキスト抽出と要約という一連のプロセスを自動化できます。これにより、情報管理、研究活動、業務効率化など、様々な場面で大きなメリットを享受できます。このスクリプトを基盤として、ご自身のニーズに合わせてカスタマイズし、さらに高度な機能を追加していくことが可能です。