表形式が混在したPDFをMarkdown形式へと変換にトライしたのでメモです。前提としてはすでにPDFにテキストが埋め込まれているものを対象とします。拾い物のPDFでお試ししたので変換後の結果は割愛しています。問題のないPDFで試したら追記したいと思います。Microsoftが公開しているMarkItDownとPyMuPDFとPyMuPDF4LLM、markdropで試してみました。
結果としては、PyMuPDF4LLMがお手軽で有料モデルを利用しなくとも表交じりも頑張って抽出します。さらにmarkdropではmicrosoftのtable-transformerを利用している効果もあって、表のコラムが良く取れています。文書によって使い分けることになりそうです。MarkItDownではLLMを指定してみましたが、テキストがすでに埋め込まれたPDFでは利用されてないようで結果に変化はありませんでした。Markitdownから呼び出す利点はないかもしれませんが、有料ですがAzure AI Document Intelligenceと併用するのがよいのでしょうか。
また、markdropはLLMモデルを使って図や表の説明文を生成ができるのは魅力です。図の切り出しや数式も一定の精度があります。
# markdropを追記
MarkItDown
llm設定なし
from markitdown import MarkItDown
md = MarkItDown(enable_plugins=False) # Set to True to enable plugins
result = md.convert("/content/pdf_sample-ja.pdf")
print(result.text_content)
llm設定あり
from markitdown import MarkItDown
from openai import OpenAIclient = OpenAI()
md = MarkItDown(llm_client=client, llm_model="gpt-4o", preserve_layout=True)
result = md.convert("/content/pdf_sample-ja.pdf")
print(result.markdown)
簡単に実装できるのは助かります。しかし、どうも、テキスト埋め込み済みのPDFの場合、llmは利用しておらず、表を認知できていない。残念。
PyMuPDF
import fitz # PyMuPDF
import pandas as pddef pdf_to_markdown_with_tables(pdf_path):
"""
表を含むPDFをMarkdown形式に変換する関数Args:
pdf_path (str): PDFファイルへのパスReturns:
str: Markdown形式の文字列
"""doc = fitz.open(pdf_path)
markdown_text = ""for page in doc:
# ページ内のテキストと表を抽出
text_blocks = page.get_text("blocks") # テキストブロックを取得
tables = page.find_tables() # 表を検出# 表の領域と重なるテキストブロックを削除
text_blocks_to_keep = []
for block in text_blocks:
block_bbox = fitz.Rect(block[:4]) # テキストブロックの範囲を取得
overlap = False
for table in tables:
if block_bbox.intersects(table.bbox): # 表と重なっているか判定
overlap = True
break
if not overlap: # 重なっていない場合のみ保持
text_blocks_to_keep.append(block)# テキストブロックと表を位置情報でソート
all_elements = text_blocks_to_keep + [(table.bbox, table) for table in tables]
all_elements.sort(key=lambda x: (x[0][1], x[0][0]) if isinstance(x[0], tuple) else (x[1], x[0]))# Markdownテキストを生成
for element in all_elements:
if isinstance(element[0], tuple): # 表の場合
table = element[1]
table_data = table.extract()
df = pd.DataFrame(table_data)
markdown_text += df.to_markdown(index=False) + "\n\n"
else: # テキストブロックの場合
markdown_text += element[4] + "\n" # テキストブロックのテキストを追加return markdown_text
# 使用例
pdf_path = "/content/pdf_sample-ja.pdf" # PDFファイルへのパスを指定
markdown_output = pdf_to_markdown_with_tables(pdf_path)
個別に表を抽出できるようなので、それを利用する。表の抽出と文字の抽出は別々に行われるようなので、表のテキストブロックを表に置き換えます。そうすると、テキスト中にMarkitdownの表を埋め込めました。
pymupdf4llm
import pymupdf4llm
def pdf_to_markdown_file(pdf_path, output_md_path):
# to_markdown 関数を直接呼び出してPDFからMarkdownテキストを生成
md_text = pymupdf4llm.to_markdown(pdf_path, write_images=True)
print(md_text) # 変換状況を表示with open(output_md_path, "w", encoding="utf-8") as f:
f.write(md_text)
print(f"Markdown file saved to {output_md_path}")# 使用例
pdf_to_markdown_file("/content/pdf_sample-ja.pdf", "output.md")
LLM用として開発されているだけあって表や見出しのつけ方も一番良かったです。論文にありがちな2段組みも流れに沿って抽出できる。画像もちょっと怪しいところもあるけれど、別途抽出してくれるのは良好。
markdrop
!pip install markdrop
from markdrop import extract_images, make_markdown, extract_tables_from_pdf
source_pdf = '/content/pdf_sample-ja.pdf' # Replace with your local PDF file path or a URL
output_dir = './' # Replace with desired output directory's pathmake_markdown(source_pdf, output_dir)
extract_images(source_pdf, output_dir)
extract_tables_from_pdf(source_pdf, output_dir=output_dir)
表の表記だけではなく、図の切り出しや数式も可能な範囲で良い感じで加工されます。まだ発展途中の様子です。