Google Apps ScriptとMistral OCRを使用してPDFファイルをテキスト変換する方法

# Google Apps ScriptとMistral OCRを使用してPDFファイルをテキスト変換する方法

こんにちは！今回はGoogle Apps Script(GAS)とMistral AIのOCR APIを組み合わせて、Google ドライブ上のPDFファイルを自動的にテキスト形式に変換する方法をご紹介します。

## はじめに

多くの企業や個人が日々PDFファイルを扱っていますが、その内容をテキストとして抽出したいケースは少なくありません。Mistral AIが提供するOCR APIとGoogle Apps Scriptを組み合わせることで、Google ドライブ上のPDFファイルを自動的にテキスト化することができます。

## 必要なもの

- Google アカウント
- Mistral AI APIキー（[Mistral AI公式サイト](https://docs.mistral.ai/)から取得可能）
- Google ドライブ上のPDFファイル

## 実装コード
```javascript
/**
 * Google Apps Script to process PDF files using Mistral AI OCR API
 * This implementation:
 * 1. Retrieves the public URL of the PDF file from Google Drive
 * 2. Processes the file using Mistral OCR API
 * 3. Saves the OCR results as a text file in the specified output folder
 */

// Configuration Constants - MODIFY THESE
const MISTRAL_API_KEY = PropertiesService.getScriptProperties().getProperty('MISTRAL_API_KEY');
const TARGET_FOLDER_ID = 'YOUR_INPUT_FOLDER_ID'; // Google Drive folder ID containing PDFs
const OUTPUT_FOLDER_ID = 'YOUR_OUTPUT_FOLDER_ID'; // Folder ID where text files will be saved
```
```javascript
/**
 * Main function to process PDFs with Mistral OCR
 */
function processPDFsWithMistralOCR() {
  try {
    // Validate configuration
    if (!MISTRAL_API_KEY) throw new Error("Mistral API Key is not set.");
    
    // Get folder references
    const targetFolder = DriveApp.getFolderById(TARGET_FOLDER_ID);
    const outputFolder = DriveApp.getFolderById(OUTPUT_FOLDER_ID);
    
    // Get all PDF files
    const pdfFiles = targetFolder.getFilesByType(MimeType.PDF);
    
    // Process counters
    let processedCount = 0, errorCount = 0;
    
    // Process each PDF file
    while (pdfFiles.hasNext()) {
      const pdfFile = pdfFiles.next();
      const fileName = pdfFile.getName();
      
      Logger.log(`Processing: ${fileName}`);
      
      try {
        // Step 1: Get public URL of the file
        Logger.log("Getting public URL of the file...");
        const fileUrl = getPublicUrl(pdfFile);
        Logger.log(`Got public URL: ${fileUrl}`);
        
        // Step 2: Process file with OCR
        Logger.log("Processing with OCR...");
        const ocrResult = processOCR(fileUrl);
        Logger.log("OCR processing complete");
        
        // Validate OCR result
        if (!ocrResult || !ocrResult.trim()) {
          throw new Error("No text extracted from PDF");
        }
        
        // Step 3: Save the OCR result
        const textFileName = fileName.replace('.pdf', '.txt');
        outputFolder.createFile(textFileName, ocrResult, MimeType.PLAIN_TEXT);
        
        processedCount++;
        
      } catch (fileError) {
        // Handle file-specific errors
        errorCount++;
        logError(outputFolder, pdfFile, fileError);
      }
      
      // Prevent rate limiting issues
      Utilities.sleep(2000);
    }
    
    Logger.log(`Processing completed: Success=${processedCount}, Failures=${errorCount}`);
    return { success: processedCount, failures: errorCount };
  } catch (error) {
    Logger.log(`Error: ${error.toString()}`);
    throw error;
  }
}
```
```javascript
/**
 * Get the public URL of a Google Drive file
 * @param {GoogleAppsScript.Drive.File} file - The file to get the URL for
 * @returns {string} - The public URL of the file
 */
function getPublicUrl(file) {
  // Make the file publicly accessible
  file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
  
  // Return the download URL
  return `https://drive.google.com/uc?export=download&id=${file.getId()}`;
}
```
```javascript
/**
 * Process a file with OCR
 * @param {string} fileUrl - The public URL of the file
 * @returns {string} - The OCR result text
 */
function processOCR(fileUrl) {
  const endpoint = "https://api.mistral.ai/v1/ocr";
  
  const payload = {
    model: "mistral-ocr-latest",
    document: {
      type: "document_url",
      document_url: fileUrl
    }
  };
  
  const options = {
    method: "post",
    contentType: "application/json",
    headers: {
      "Authorization": "Bearer " + MISTRAL_API_KEY
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };
  
  const response = UrlFetchApp.fetch(endpoint, options);
  
  if (response.getResponseCode() !== 200) {
    throw new Error(`OCR processing failed: ${response.getContentText()}`);
  }
  
  const responseData = JSON.parse(response.getContentText());
  
  // Extract text from the response
  let extractedText = "";
  
  if (responseData.pages && responseData.pages.length > 0) {
    // Combine text from all pages
    extractedText = responseData.pages.map(page => page.markdown || page.text || "").join("\n\n");
  } else if (responseData.text) {
    extractedText = responseData.text;
  } else if (responseData.content) {
    extractedText = responseData.content;
  }
  
  return extractedText;
}
```
```javascript
/**
 * エラー情報をGoogle Driveに記録する
 * @param {GoogleAppsScript.Drive.Folder} outputFolder - エラーログを保存するフォルダ
 * @param {GoogleAppsScript.Drive.File} pdfFile - 処理中にエラーが発生したPDFファイル
 * @param {Error} error - キャッチしたエラーオブジェクト
 */
function logError(outputFolder, pdfFile, error) {
  try {
    const errorFileName = pdfFile.getName().replace('.pdf', '_ERROR.txt');
    const errorDetails = `Error: ${error.message}\n` +
                         `File: ${pdfFile.getName()}\n` +
                         `Size: ${Math.round(pdfFile.getSize() / 1024)} KB\n` +
                         `Time: ${new Date().toString()}\n` +
                         `File URL: ${pdfFile.getUrl()}\n` +
                         `Stack Trace:\n${error.stack || "Not available"}`;
    
    outputFolder.createFile(errorFileName, errorDetails, MimeType.PLAIN_TEXT);
    Logger.log(`Error log created: ${errorFileName}`);
  } catch (logError) {
    Logger.log(`Failed to log error: ${logError.message}`);
  }
}
```

## 設定手順

1. **スクリプトエディタを開く**
   - Google ドライブで「新規 > その他 > Google Apps Script」を選択

2. **コードを貼り付ける**
   - 上記のコードをエディタにコピー＆ペースト

3. **APIキーの設定**
   - スクリプトエディタのメニューから「ファイル > プロジェクトのプロパティ > スクリプトのプロパティ」を選択
   - 「行を追加」をクリック
   - 「プロパティ」に `MISTRAL_API_KEY` と入力
   - 「値」にMistral APIキーを入力
   - 「保存」をクリック

4. **フォルダIDの設定**
   - Google ドライブで、PDFファイルを格納するフォルダと出力用フォルダを作成
   - 各フォルダのURLからIDを取得（URLの `folders/` の後の英数字列）
   - コード内の `TARGET_FOLDER_ID` と `OUTPUT_FOLDER_ID` を取得したIDに書き換え

5. **実行**
   - スクリプトエディタのツールバーから関数 `processPDFsWithMistralOCR` を選択
   - 実行ボタンをクリック
   - 初回実行時は権限の承認が必要

## 利用上の注意点

1. **ファイルの共有設定について**
   - このスクリプトは処理対象のPDFファイルを「リンクを知っている全員」に対して閲覧可能な設定に変更します
   - 処理終了後は必要に応じて共有設定を元に戻してください
   - 機密情報を含むPDFの処理には別の方法を検討してください

2. **API使用量と料金について**
   - Mistral OCR APIは使用量に応じて課金される場合があります
   - 大量のPDFファイルを処理する前に、公式サイトで料金プランを確認してください

3. **処理時間と制限について**
   - Google Apps Scriptには実行時間の制限（6分）があります
   - 大量のファイルや大きなPDFファイルを処理する場合は、複数回に分けて実行するか、タイムトリガーを設定してください

4. **OCR精度について**
   - OCR精度はPDFの品質に依存します
   - スキャンPDFの場合、元画像の解像度や傾き、コントラストによって精度が変わります
   - テキスト埋め込み済みのPDFでは高い精度が期待できます

5. **エラーハンドリングについて**
   - スクリプトはエラーが発生した場合、詳細を出力フォルダに記録します
   - ログを確認してエラーの原因を特定してください

## 拡張アイデア

- 特定の文字列パターンを検索してメール通知
- Google スプレッドシートに抽出結果をまとめる
- 定期的に実行するようトリガーを設定
- OCRされたテキストを使って要約や分析を行う

## まとめ

Mistral OCR APIとGoogle Apps Scriptを組み合わせることで、Google ドライブ上のPDFファイルを効率的にテキスト化できます。ビジネスプロセスの自動化や文書管理の効率化にぜひ活用してみてください。

詳しいAPIの使用方法や最新情報については、[Mistral AI公式ドキュメント](https://docs.mistral.ai/)をご参照ください。