PDF から取り出した文字が(cid:12345) だらけだった件のまとめ(原因・検証・最終解)
以下のようなコードを書いてpdfplumberを使ってpdfからテキストを抽出したのだけど、ある時に(cid:12345)(cid:12345)(cid:12345)みたいな文字列が出力されるだけで、文字列が全然抽出できなかった。
import pdfplumber
import re
PDF = "XXXXX.pdf"
texts = []
with pdfplumber.open(PDF) as pdf:
for page in pdf.pages:
page_text = page.extract_text() or ""
texts.append(page_text)
text = "\n\n".join(texts)
text = re.sub(r'\n\s*\n', '\n\n', text) # 複数の改行を2つの改行(段落区切り)にまとめる
text = re.sub(r' +', ' ', text) # 連続するスペースを1つにまとめる
text = text.replace(' ', ' ') # 全角スペースも半角スペースに変換
text = re.sub(r'[\u0000-\u001F\u007F-\u009F]', '', text) # 制御文字(非表示文字など)を除去
text = text.lower()
with open("check2.txt", "w", encoding="utf-8") as f:
f.write(text)
原因
PDF内にCIDフォントと、Unicodeの対応表(ToUnicode CMap)が無い/壊れていること。
対応表がない、壊れていると、Unicodeが復元できずCIDフォントのまま吐き出されてしまう。
CIDフォント:Adobeが開発したフォント形式**「Character IDentifier(CID)」のこと。**
解消に向けて色々調べてみたところ、以下の方法が提案されていたので、検証結果を書いてきます。
❌ 方法1:正規表現で置換
(cid:\d+)
を正規表現で拾って chr()
置換してみる
正規表現を使えば、できたということで以下のコードを試してみたが、結局文字化け。
def prune_text(text):
def replace_cid(match):
ascii_num = int(match.group(1))
try:
return chr(ascii_num)
except:
return ''
cid_pattern = re.compile(r'\(cid:(\d+)\)')
pruned_text = re.sub(cid_pattern, replace_cid, text)
return pruned_text
PDF = "XXXXX.pdf"
texts = []
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
page_text = page.extract_text() or ""
page_text = prune_text(page_text)
texts.append(page_text)
full_text = '\n'.join(texts)
with open("extracted_text_plumber_pruned.txt", "w", encoding="utf-8-sig") as f:
f.write(full_text)
❌ 方法2:PyPDF2を使ったテキスト抽出
PyPDF2を使えばいいんじゃないかという案があったので、以下の様にインストールして、テキスト抽出してみたけど、文字化けしてNG。(以下のコード上ではutf-8-sig)にしてるけど、他のものでも同様。
# ライブラリのインストール
pip install PyPDF2
from PyPDF2 import PdfReader
reader = PdfReader("XXXXX.pdf")
texts = [page.extract_text() for page in reader.pages]
full_text = '\n'.join(texts)
with open("extracted_text.txt", "w", encoding="utf-8") as f:
f.write(full_text)
❌ 方法3:Mupdfを使う
[MuPDF: The ultimate library for managing PDF documents](https://mupdf.com/)
# ライブラリのインストール
pip install pymupdf
import fitz # pymupdf
PDF = "XXXXX.pdf"
texts = []
with fitz.open(pdf_path) as doc:
for page in doc:
page_text = page.get_text() or ""
texts.append(page_text)
full_text = '\n'.join(texts)
with open("extracted_text_pymupdf.txt", "w", encoding="utf-8") as f:
f.write(full_text)
❌ 方法4: pdf2docxを使って、PDFをword変換
該当するpdfをpdf2docxを使うと、文字化けではなく、そもそも取り出しができなかった。本当にやっかいだ。このPDF。
# ライブラリのインストール
pip install python-docx
pip install pdf2docx
✅ 方法5:Acrobatを使ってPDFを変換
上記4つを試してみてもダメだったので、Adobe Acrobat(Readerじゃないやつ)を使って、wordに変換する方法を試してみた。
結果、さすがPDFの胴元Adobe。こちらはフォントは変わってしまったものの、テキストとして読む分には変じゃない。
また、一部の表や改行などが、ずれたりする見た目のきれいさはの悪化は一定程度許容できた。
❌ 方法6:Wordを使ってPDFを変換
その後、wordを使ってPDFを変換してみたらどうだろう?と考えて試してみましたが、こちらも文字化けしてしまってNG。
その他
他にもOCRで読み取ることも考慮にいれたけど、こちらは誤変換がいやだったので、今回は却下。
まとめ
「CIDフォント」を「Unicode(文字コード)」に変換する対応表(CMap)がなければ、現時点でのコードでの修正箇所は難しい。
結果、対応は2つ
- OCRで読み取り(精度が懸念)
元データが、壊れていたりする場合は、「大元から根本を断て」という結果になった。