見出し画像

【GAS】Google Apps Script 活用事例 LINEのOCR機能を使って、紙やカード表面などの画像をテキスト化してspreadsheetに書き込む

紙に拘るおじさん社員に苦しめられております。紙に書いてあるものをspreadsheetにデータ入力とか嫌じゃないですか?単調だし、つまらないし。なぜ、紙に拘るのだろう?業務効率化を妨げる要因そのものです。

活字であれば、100%に近い精度がある、LINEのOCR機能

今回は、LINEのOCR機能を使って、画像をテキスト化、送信すると、それがそのままspreadsheetに書き込まれます。用途としては、在留カードの在留番号、名前、その他諸々、そういったデータを写真撮影してテキスト化出来たら、どんなに楽だろうかと思ったのがきっかけです。

Microsoft officeにもOffice Lens というイケてるアプリがあります。

LINE Botなどは、何度も作っているので、この情報を取得して、spreadsheetに追記出来そうだなぁと思って、作ってみたら、超楽でした!!

ソースコードはこんな感じ

function doPost(e) {
 const ACCESS_TOKEN = '***********************';
 // WebHookで受信した応答用Token
 const replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
 // ユーザーのメッセージを取得
 const userMessage = JSON.parse(e.postData.contents).events[0].message.text;
 const url = 'https://api.line.me/v2/bot/message/reply';
 
 const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
 const sheet       = spreadsheet.getSheetByName('LINE連携');
 const lastRow     = sheet.getLastRow()+1;
 
 /*DBから読み取ったデータ*/
 const targetSheet = spreadsheet.getSheetByName('readDB');
 const values      = targetSheet.getDataRange().getDisplayValues();
 values.shift();
 
 const spreadsheetUrl = spreadsheet.getUrl();
 const sheetId        = sheet.getSheetId();
 const marge          = spreadsheetUrl + "#gid=" + sheetId;
 const fileName       = spreadsheet.getName();
 
 
 let results       = [];
 const message1    = 'Staff IDが下記のSpreadsheetに追記されました。' + '\n' + 'LINEのカメラで、在留カードを読み取ってください。'; 
 const message2    = '在留カードの情報が、C列に書き込まれました。' + '\n\n' + fileName + '\n' + marge;
 const length      = userMessage.length;
 
 //5桁もしくは6桁だったら、IDだと判断する。
 if(length === 5 | length === 6 ){
   for(let i = 0; i < values.length; i++){
     if(values[i][0].indexOf(userMessage) !== -1){
       results.push([values[i][0],values[i][1]]);
       
     }//if
   }//for_i
   console.log(results);
   sheet.getRange(lastRow, 1, 1, 2).setValues(results);
   
   UrlFetchApp.fetch(url, {
     'headers': {
       'Content-Type': 'application/json; charset=UTF-8',
       'Authorization': 'Bearer ' + ACCESS_TOKEN,
     },
     'method': 'post',
     'payload': JSON.stringify({
       'replyToken': replyToken,
       'messages': [{
         'type': 'text',
         'text': message1,
       }],
     }),
   });
   return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
 }//if
 else{
   //IDが書き込まれたセルと同じ行に書き込みたいため。
   sheet.getRange(lastRow-1, 3, 1, 1).setValue(userMessage);
   
   UrlFetchApp.fetch(url, {
     'headers': {
       'Content-Type': 'application/json; charset=UTF-8',
       'Authorization': 'Bearer ' + ACCESS_TOKEN,
     },
     'method': 'post',
     'payload': JSON.stringify({
       'replyToken': replyToken,
       'messages': [{
         'type': 'text',
         'text': message2,
       }],
     }),
   });
    return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
 }//else
}//end

今回のスクリプトでは、テキスト化だけが目的ではなかったので、多少長くなっています。まず、スタッフIDを打ち込みます。テキストであれば、matchメソッドがヒットするのですが、LINE経由で送る場合、数値となっているようだったので、桁数で判断しました。

在留カードの情報は、マイナンバーと同様に、重要な個人情報であるため、サンプルでも例示せず、********としています。

スクリーンショット 2020-04-18 12.06.57

if(length === 5 | length === 6 )

IDが5桁もしくは6桁だったら、現状は5桁なのですが、労働集約系で、将来的に、6桁行く事もあるかなと思い、一応書いています。その時、僕はこの会社にいないでしょう。

IDだと判断したら、データベースと照合し、IDと名前を配列に追加してシートに書き出すという処理をしています。VLOOKUPっぽい動きをする処理です。

逆にIDではないと判断したら、データを隣の列に書きます。このスクリプトのelse部分が、在留カードの情報であることを想定しています。

実際の運用としては、IDを入力、撮影、OCRでテキスト化というフローが出来そうです!!あとは正規表現で、必要な情報を取り出したり、情報別に列を分けたり出来ればもっと良いですね!!

2020.04.17追記 在留カード番号を抽出するスクリプト

function regExpression() {
 const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
 const sheet       = spreadsheet.getSheetByName('LINE連携');
 const values      = sheet.getDataRange().getValues();
 values.shift();
 
 let cardNumbersArray = [];
 
 for(let i = 0; i < values.length; i++){
   if(values[i][2] === ''){continue};
   
   //OCRの結果で、番号が改行されてしまった場合の処理
   if(values[i][2].match(/[A-Za-z0-9].*/) !== null || values[i][2].match(/交付年月日.*?\n/) !== null){
     
     const cardNumber = values[i][2]
     .match(/[A-Za-z0-9].*/)[0];
     
     const period = values[i][2]
     .match(/交付年月日.*?\n/)[0].replace('交付年月日','');
     
     const expire = values[i][2]
     .match(/.*まで有効/)[0]
     .replace('のカードは','')
     .replace('このヶードは','')
     .replace('このカードは','')
     .replace('まで有効','')
     .replace('且','日');
     
     cardNumbersArray.push([cardNumber,period,expire]);
   }//if
   
   //OCRの結果が中国語で読み込まれた場合の処理
   else if(values[i][2].match(/[费号|番号].*?/) !== null || values[i][2].match(/交付年月日.*?\n/) !== null){
     const cardNumber = values[i][2]
     .match(/[费号|番号].*?/)[0].replace('费号','').replace('番号','');
     
     const period = values[i][2]
     .match(/交付年月日.*?\n/)[0].replace('交付年月日','');
     
     const expire = values[i][2]
     .match(/.*まで有効/)[0]
     .replace('のカードは','')
     .replace('このヶードは','')
     .replace('このカードは','')
     .replace('まで有効','')
     .replace('且','日');
     
     cardNumbersArray.push([cardNumber,period,expire]);
   }//else
   
   else{
   
   cardNumbersArray.push(['','','']);
   }
 }//for_i
 console.log('正規表現の結果 %s ',cardNumbersArray);
 
 //D2から
 sheet.getRange(2, 4, cardNumbersArray.length, cardNumbersArray[0].length).setValues(cardNumbersArray);
}//end

実際に運用してみた結果、在留カードの場合、偽造防止の透かしや反射のせいで読み取り精度が、結構変わる印象。また日本語が中国語に誤変換されてしまう事もあり、それらを考慮したのが上記のスクリプトです。

スクリーンショット 2020-04-18 12.06.57

D列以降に配列で書き出します。上手くいった時は、ガッツポーズするくらい嬉しい!!

1. 在留カード番号
2. 交付日
3. 有効期限日(原則、交付日の1年後)

上記の情報を抽出しています。

在留カード番号を正規表現で抽出する。

1. 番号DE***********DF(読み取り成功)
2. 费号GL***********AV(一部が中国語になってしまう)
3. 番号
  YH*************LK(改行されてしまう)

この3つのパターンのうち、いづれかに合致すれば、抽出可能という感じにしました。matchメソッドが一つでもコケると、処理がエラーになってしまうので、そうなった場合に、REGEXTRACT関数を埋め込む処理をしようかなと思っています。そちらも完成次第、追記しようと思います。

const expire = values[i][2]
     .match(/.*まで有効/)[0]
     .replace('のカードは','')
     .replace('このヶードは','')
     .replace('このカードは','')
     .replace('まで有効','')
     .replace('且','日');

めっちゃ、replaceしとるー

Evernoteに転送するスクリプトも書いています。


この記事が気に入ったらサポートをしてみませんか?