LoginSignup
0
0

OCRで読み込めないPDFをpyocrでなんとかデータ化した話

Posted at

はじめに

最近、Pythonのbar_chart_raceで、色んな統計データを動くグラフを作っていて、古い統計データがPDFでしかなく、データが困難で困っておりました。

具体的には、在日外国人の人口データ、1964年から2005年まで。
2006年からエクセルデータです。

こんな感じ:
image.png

原本:
https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00250012&tstat=000001018034&cycle=7&year=19640&month=0&tclass1=000001060436&result_back=1&cycle_facet=tclass1&tclass2val=0

試したOCR

・iLovePDF(無料版)
・light PDF(無料版)
・PDF Reader(有料版OCR)
 →体験7日間以内に止めたので、実質無料

総じてダメダメでした。
全くExcelに貼り付けられる状態じゃない。

最初の設計

設計:全部OCR化するのは諦めて、重要なデータのみを抽出することに
1.PDFを開いて、国名を[Win+Shift+S]でスクリーンショット
2.PythonのPGでクリップボード→画像ファイル→OCR→文字をクリップボード→Excelへ
3.PDFを開いて、人口を[Win+Shift+S]でスクリーンショット
4.[2]と同じ

環境・インストール

「環境」
・VS Code
・Python 3.11(このバージョンじゃないとbar_chart_raceが動かない)

「pythonのモジュールインストール」
・pyocr
・pyperclip
・Tesseract

参考サイト:
https://note.nkmk.me/python-pyperclip-usage/
https://takaaki-hobby-blog.com/python/python_ocr/

実践

さてやってみたところ、国の精度がよろしくない。

結果 国名 OCR結果
× アフダニスタン アフガニスタン
× アラブ首長国逢芦 アラブ首長国連邦
× ミャシマ ミャンマー
× バハレシ バーレーン
× プタン プータン
× バンクタラテシュ バングラディシュ
プルネイ ブルネイ
× カンポテディア カンボジア
スリランカ スリランカ
× い団 中国
× すキマチホネ サイプラス
× ィシド インド
インドネシア インドネシア
イラン イラン
× ィラク イラク
イスラエル イスラエル
× ジショルダン ジョルダン
× 森国剣鉄 韓国・朝鮮
× クウェイト クウェート
× ラォス ラオス

全体的な精度として、20%くらい。
数字の方は、結構いいです。

国名はそこまで変わらないので、ご認識データベースを用意して、Vlookupで照らし合わせていけばいいかと思い、しばらくデータを作ってました。

Excelデータ部:
image.png

国マスタ:
image.png

改善とプログラム

対応中にPGを色々変えました。

1.OCR結果に改行が多く含まれてしまうので、改行コード2つを、1つにreplace

replace('\n\n','\n')

2.ファイルの有無で国と値を使い分けて、OCRの言語ファイルを切替
これにより、国データと値を一気に貼り付けが出来るように

3.よく間違える文字をreplace
「国名」
.replace(' ','') # 半角スペース除去
.replace('ー','') # なぜかハイフンが多いので、除去
.replace('=','ニ') # カナ[に]がイコールになるので、置換

「数字」
.replace(' ','') # 半角スペース除去
.replace(',','') # カンマ[,]除去
.replace('.','') # ドット[.]除去
.replace('B','8') # B を 8 へ置換

4.2回に分けて実行し、国名とデータをTSV形式でクリップボードへ貼り付けて、Excelに貼り付けられるようにした。

「プログラム」

ocr.py
import os
import sys
import shutil

from datetime import datetime
#PyOCRを読み込む
from PIL import ImageGrab, Image
import pyocr
import pyperclip

# ルートディレクトリのパスを指定
ROOT_PATH = 'C:\\kensho_files\\python_project\\temp\\'

TEMP_IMG_FILE_COUNTRY_V = 'clipboard_image_t.png'
TEMP_IMG_FILE_VALUE_V   = 'clipboard_image_c.png'

TEMP_IMG_FILE_COUNTRY = ROOT_PATH + TEMP_IMG_FILE_COUNTRY_V
TEMP_IMG_FILE_VALUE = ROOT_PATH + TEMP_IMG_FILE_VALUE_V

TEMP_RM_FOLDER_VALUE = ROOT_PATH + 'end'
#sys.path.append(ROOT_PATH)

img = ImageGrab.grabclipboard()

ocr_flg = False

#print(img)
if img != None:
    # <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=200x71 at 0x105E68700>
    #print(isinstance(img, Image.Image))
    # True
    #print(img.size)
    # (200, 71)
    #print(img.mode)
    # RGB

    # 国ファイルが存在するかどうかをチェック
    if os.path.isfile(TEMP_IMG_FILE_COUNTRY):
        # 存在する場合、値として保存
        img.save(TEMP_IMG_FILE_VALUE)
        # OCR実施
        ocr_flg = True
    else:
        # 存在しない場合、国として保存
        img.save(TEMP_IMG_FILE_COUNTRY)
else:
    # 両方のファイルが有ればOCR実施
    if os.path.isfile(TEMP_IMG_FILE_COUNTRY) and os.path.isfile(TEMP_IMG_FILE_VALUE):
        ocr_flg = True

# OCRを実施しないなら、終了
if ocr_flg == False:
    sys.exit()

#################################
# 以下OCR処理
#################################

#Tesseractのインストール場所をOSに教える
tesseract_path = "C:\Program Files\Tesseract-OCR"
if tesseract_path not in os.environ["PATH"].split(os.pathsep):
    os.environ["PATH"] += os.pathsep + tesseract_path

#OCRエンジンを取得する
tools = pyocr.get_available_tools() 
if len(tools) == 0:
    print("OCRエンジンが指定されていません")
    sys.exit(1)
else:
    tool = tools[0]

## OCR読み鉾処理
def exec_ocr(cv_value):
    #画像の読み込み
    if cv_value == 'c':
        img = Image.open(TEMP_IMG_FILE_COUNTRY)
    else:
        img = Image.open(TEMP_IMG_FILE_VALUE)

    #文字を読み取る
    builder = pyocr.builders.TextBuilder(tesseract_layout=6)
    #result = tool.image_to_string(img,lang="jpn",builder=builder)
    if cv_value == 'c':
        result = tool.image_to_string(img,lang="jpn",builder=builder)
    else:
        result = tool.image_to_string(img,lang="eng",builder=builder)

    if result.find('\n\n'):
        result = result.replace('\n\n','\n')

    lines = result.split('\n')

    #print ('件数:'+str(len(lines)))
    print ('**************')
    print ('件数:'+str(len(lines)))
    i = 0
    for a in lines:
        if cv_value == 'c':
           s_ocr_result.append('') 

        if s_ocr_result[i] == '':
            s_ocr_result[i] = a.replace(' ','').replace('','').replace('=','')
        else:
            s_ocr_result[i] += '\t' + a.replace(' ','').replace('.','').replace(',','').replace('B','8')
        i += 1


# OCRの値を配列の取得する
s_ocr_result = []

exec_ocr('c')
exec_ocr('v')

s_value = ''
for s in s_ocr_result:
    if s != '':
        s_value += s + '\r\n'

# クリップボードに値を貼り付け
pyperclip.copy(s_value)

############################
# 事後処理
############################

# 現在の日時を取得
now = datetime.now()
# 日時を文字列に変換(フォルダ名として使用可能な形式)
folder_name = now.strftime("%Y%m%d_%H%M%S")
# フォルダが存在しない場合に新しいフォルダを作成
os.mkdir(TEMP_RM_FOLDER_VALUE + '\\' + folder_name)

# ファイルを移動
shutil.move(TEMP_IMG_FILE_COUNTRY, TEMP_RM_FOLDER_VALUE + '\\' + folder_name + '\\' + TEMP_IMG_FILE_COUNTRY_V)
shutil.move(TEMP_IMG_FILE_VALUE, TEMP_RM_FOLDER_VALUE + '\\' + folder_name + '\\' + TEMP_IMG_FILE_VALUE_V)

sys.exit(1)

使い方(備忘録)

1.[Win + Shift + S] で、国名だけをスクショ

image.png

2.ocr.py の実行

3.[Win + Shift + S] で、数字だけをスクショ

image.png

4.ocr.py の実行

5.Excelへデータの貼り付け

image.png

成果物

半分宣伝もありますが、こんな感じで動画が作れました。

「検証グラフch」
在日外国人数の国籍別ランキング
https://www.youtube.com/shorts/LZxVpG47W8M

以上、もっと精度向上、使いやすくなる方法を模索します。

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