【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経由で送る場合、数値となっているようだったので、桁数で判断しました。
在留カードの情報は、マイナンバーと同様に、重要な個人情報であるため、サンプルでも例示せず、********としています。
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
実際に運用してみた結果、在留カードの場合、偽造防止の透かしや反射のせいで読み取り精度が、結構変わる印象。また日本語が中国語に誤変換されてしまう事もあり、それらを考慮したのが上記のスクリプトです。
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に転送するスクリプトも書いています。
この記事が気に入ったらサポートをしてみませんか?