Python3 + google-api-python-clientで、Google Drive APIを使ってpdfファイルをアップロードし、OCR処理をする

手元の画像pdfファイルをOCR処理したいことがありました。

手軽にOCR処理する方法がないかを調べたところ、Google DriveOCR機能があることを知りました。
GoogleDriveのOCR機能を使って、大量の画像ファイルをテキストに変換する

そこで、Google DriveOCRを試してみることにしました。

 
なお、上の記事ではgdirveというCLIツールを使っていました。
prasmussen/gdrive: Google Drive CLI Client

PythonGoogle Drive APIを使ってみたかったので探してみたところ、 PyDrive がありました。
gsuitedevs/PyDrive: Google Drive API Python wrapper library

ただ、今回はファイルをアップロードするだけで複雑なことはしないため、Google APIPythonライブラリ google-api-python-client だけでGoogle Drive APIを使ってみます。
google/google-api-python-client

 
目次

 

環境

 
なお、画像としてpdf化されたファイル例として、今回は情報処理試験の過去問を使ってみます。
IPA 独立行政法人 情報処理推進機構:過去問題

今回は、基本情報処理試験の午前問題である 2017h29a_fe_am_qs.pdf を使います。

最終的なディレクトリ構造は以下となります。

$ tree
.
├── 2017h29a_fe_am_qs.pdf
├── __init__.py
├── client_id.json
└── google_drive_ocr_uploader.py

 

Google Drive APIを使うための事前準備

Python Quickstartを参考に、事前準備を行います。
Python Quickstart  |  Drive REST API  |  Google Developers

プロジェクトの作成まわりは、以前のGmail APIの時と同様で良さそうです。
Python3 + google-api-python-clientで、Gmail APIを使ってメールを送信する - メモ的な思考的な

 
認証情報が含まれる client_id.json ファイルは、実行用スクリプトと同じディレクトリに入れます。

 

調査事項

認証まわり

公式ドキュメントの get_credentials()関数まわりを使用します。
Step 3: Set up the sample - Python Quickstart  |  Drive REST API  |  Google Developers

なお、Python2.6関係のソースコードは削除しました。

 
Scopeはどうしようかと思いましたが、公式ドキュメントに

Per-file access to files created or opened by the app. File authorization is granted on a per-user basis and is revoked when the user deauthorizes the app.

https://developers.google.com/drive/v3/web/about-auth#OAuth2Authorizing

とあったため、 https://www.googleapis.com/auth/drive.file を使うことにしました。

 

アップロードする時のMIMEタイプについて

どのようなMIMEタイプにすればよいか見たところ、

The extracted text will appear in the Google Docs document alongside the embedded image.

https://developers.google.com/drive/v3/web/manage-uploads#importing_to_google_docs_types_wzxhzdk18wzxhzdk19

とありました。

ドキュメントにある表より、pdfの場合はGoogle DocsにするMIMEタイプを指定すれば良さそうでした。

Google DocsMIMEタイプは application/vnd.google-apps.document でした。
Supported MIME Types  |  Drive REST API  |  Google Developers

 

ocrLanguageの設定先について

OCRをどの言語で実行するかについては、 ocrLanguage に設定すれば良さそうでした。
Files: create  |  Drive REST API  |  Google Developers

ライブラリのドキュメントを読むと、create()メソッドの引数に ocrLanguage がありました。
https://developers.google.com/resources/api-libraries/documentation/drive/v3/python/latest/drive_v3.files.html

OCR言語コードは ISO 639-1 codeで指定すればよいので、日本語の場合は ja とします。

 

bodyパラメータで指定するファイル名について

アップロードするファイル名を指定します。

Google Docsにするからといって拡張子を外すと、以下のようにHTTP 400エラーが発生しますので、拡張子をつけたままにします。

googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/upload/drive/v3/files?ocrLanguage=ja&alt=json&uploadType=resumable returned "Bad Request">

 

実装

google_drive_ocr_uploader.py

import argparse
import os
import pathlib

import httplib2
from apiclient import discovery
from googleapiclient.http import MediaFileUpload
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage


UPLOAD_FILE_NAME = '2017h29a_fe_am_qs.pdf'
MIME_TYPE = 'application/vnd.google-apps.document'
SCOPES = 'https://www.googleapis.com/auth/drive.file'
CLIENT_SECRET_FILE = 'client_id.json'
APPLICATION_NAME = 'ipa-google-drive-api-client'

flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'drive-python-quickstart.json')

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        credentials = tools.run_flow(flow, store, flags)
        print('Storing credentials to ' + credential_path)
    return credentials


def upload_with_ocr():
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('drive', 'v3', http=http)

    local_file_path = pathlib.Path(__file__).resolve().parent.joinpath(UPLOAD_FILE_NAME)

    media_body = MediaFileUpload(local_file_path, mimetype=MIME_TYPE, resumable=True)

    body = {
        'name': local_file_path.name,
        'mimeType': MIME_TYPE,
    }

    service.files().create(
        body=body,
        media_body=media_body,
        ocrLanguage='ja',
    ).execute()


if __name__ == '__main__':
    upload_with_ocr()

 

実行結果

$ python google_drive_ocr_uploader.py 

と実行後、自分のGoogleドライブの直下のディレクトリを見ると 2017h29a_fe_am_qs.pdf ファイルが存在します。

ファイルを開くとOCR結果が表示されました*1

複数ページのpdfでも、全ページOCR処理がなされていました。

 

ソースコード

GitHubに上げました。 google_drive_api ディレクトリの中にあるものが今回のファイルです。
thinkAmi-sandbox/google-api-python-client-sample

*1:掲載してよいのか分からないため、結果は省略します