GoogleAppsScriptで実効性のあるコードを書く。
メチャメチャ放置していました。
こんにちは。
今回から、実務ですぐに使えるをテーマにGoogleAppsScriptとスプレッドシートの関数を組み合わせたスキルを共有していきたいと思います。
なんでGASなんよ?
これはもう、私がいわゆる「ランチェスターの弱者戦略」に特化して活動してるからにほかなりません。
自動化や効率化の王道は現在までにエクセルのVBAやPowerAutomateなどがメジャーどころですが、そもそも企業でGoogleWorkSpaceを導入しているところがけた違いに少なく、書籍も限られていてノウハウがあまり共有されていません。
また、書籍もいわゆる「痒い所に手が届く」ものは限られていて、リファレンスとして利用できる基本のメソッドや文法にかなりのページを割いているものが多いのが実情です。
ですので、初心者が学びやすい、「すぐに使える」、「実効性がある」スキル情報が市場に出回っていないのが実情です。
情報が少ないということはつまりノウハウに貴重価値があるということです。
ある意味、このノウハウを集積しておくと使い勝手がいいメンツとして重宝されるんじゃないかと思います。
他にも理由はありますが、早く本題に入った方がいいと思うのでまたGASを習得するメリットについては追々、そのキースキルを共有する際に述べてゆきたいと思います。
あらためて、
本Blogでは、実際に”仕事ですぐにつかえる”をモットーに、以下の構成でしばらくGASとスプレッドシートを組み合わせたスキル紹介をしていきます。
下準備
このコードは以下のような業務を想定しています。
- 定期的に、なにかしらのデータがテキスト形式フィルで送られてくる。
- そのファイルには空白で区切られている。
- ファイルの2行目にファイル情報が記述されている。
- ファイルの4行目にヘッダー情報がある。
- ファイルの5行目からがデータ
手作業で行っている、このファイルの整形作業をGASで自動化したい。
- 受信ボックスから任意の条件でメールを抽出。
- 添付されているテキストファイルを任意のGoogleドライブに保存する。
- 保存したテキストファイルのファイル名と、URLを取得する。
- スプレッドシートに”受信ファイルリスト”シートにファイル名とURLなどを入力する。
- このファイル情報を構造化データに1列繰り返し挿入したい
- メール受信日も、構造化データに1列繰り返し挿入したい
- テキストファイルの内容をパースして、5行目以降のデータを”データ”シートに入力する。
- テキストファイルの内容の中で、3列目と4列目の、日付を意味する8桁の文字列は日付型に変更する。
まとめるとこんな感じ。
1列目:メール受信日時
2列目:元のテキストファイルの2行目にあるファイル情報
3列目~:元のテキストファイルの構造化データ
データ型の操作:元のテキストファイルの3列目、4列目の8桁の数字(20230924みたいな)を日付型に変換。
function onOpen(){
//メニュー配列
var myMenu=[
{name: "受信バックアップ", functionName: "processEmails"}
];
SpreadsheetApp.getActiveSpreadsheet().addMenu("自動化",myMenu); //メニューを追加
}
function processEmails() {
// Gmailから特定の件名のメールを検索
const query = "subject:任意のメールタイトル";//Gmailのメソッドにある検索方法は何でも使えます。
const threads = GmailApp.search(query);
// GoogleドライブのIDをスプレッドシートから取得
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const driveSheet = spreadsheet.getSheetByName("id");
const driveFolderId = driveSheet.getRange("A1").getValue();//Googleドライブのidを入力
//ファイルリストのシートを取得
const orderListSheet = spreadsheet.getSheetByName("受信ファイルリスト"); // シート名は任意で変更してください。
// 処理済みメールのIDをセットに取得
const processedEmailIds = new Set(orderListSheet.getRange("B:B").getValues().flat());
for (let i = 0; i < threads.length; i++) {
const messages = threads[i].getMessages();
for (let j = 0; j < messages.length; j++) {
const message = messages[j];
// メッセージIDを取得
const messageId = message.getId();
// すでに処理済みのメールならスキップ。受信ファイルリストに入力したメールidで判断。
if (processedEmailIds.has(messageId)) {
continue;
}
const attachments = message.getAttachments({ mimeType: "text/plain" }); // .txtファイルのみを取得
for (let k = 0; k < attachments.length; k++) {
const attachment = attachments[k];
const fileName = attachment.getName();
const fileBlob = attachment.copyBlob();
// Googleドライブに.txtファイルを保存
const folder = DriveApp.getFolderById(driveFolderId);
const file = folder.createFile(fileBlob.setName(fileName));
// ファイルの内容をテキストとして取得
const fileText = fileBlob.getDataAsString("MS932").trim().split('\n');
// ファイル情報を取得
const fileinfo = fileText[1]; // テキストデータの2行目をファイル情報として取得
// メール受信日を取得
const orderDate = message.getDate();
const fileId = file.getId(); // ファイルのIDを取得
// メール受信日時をフォーマット
const mailseceivedDate = Utilities.formatDate(orderDate, "JST", "yyyy/MM/dd HH:mm:ss");
// シートにデータを追記
orderListSheet.appendRow([mailseceivedDate, messageId, fileName, file.getDownloadUrl()]);
// データをパースして"2023_注文履歴"シートに追記
const orderHistorySheetName = "データ"; // シート名を変更してください
const orderHistorySheet = spreadsheet.getSheetByName(orderHistorySheetName);
const dataLines = fileText.slice(4); // 5行目以降のデータを取得
const dataToInsert = dataLines.map(line => {
const data = line.trim().split(/\s+/); // 空白文字で分割
// 3列目と4列目を日付型に変換
const orderDate = new Date(data[2].replace(/(\d{4})(\d{2})(\d{2})/, '$1/$2/$3'));
const deliveryDate = new Date(data[3].replace(/(\d{4})(\d{2})(\d{2})/, '$1/$2/$3'));
// 日付をyyyy/MM/ddの形式にフォーマット
const formattedOrderDate = Utilities.formatDate(orderDate, "JST", "yyyy/MM/dd");
const formattedDeliveryDate = Utilities.formatDate(deliveryDate, "JST", "yyyy/MM/dd");
return [formattedOrderDate, fileinfo, ...data.slice(0, 2), formattedOrderDate, formattedDeliveryDate, ...data.slice(5)];
});
// 2次元配列をシートに追記
const ordered = orderHistorySheet.getDataRange().getValues();
const length = ordered.length;
orderHistorySheet.getRange(Number(length) + 1, 1, dataToInsert.length, dataToInsert[0].length).setValues(dataToInsert);
// 処理済みメールのIDをセットに追加
processedEmailIds.add(messageId);
}
}
}
}
こんな感じです。
そのままテキストファイルを張り付けるだけでは芸がないので、テキストファイル内に一定の法則性で記述されている、「構造化データ以外の情報」を、構造化データに盛り込む機能も仕込みました。
こうすることで、データの抽出や分岐に利用できま宇。
次回以降、コードの部分的な説明をしていきたいと思います。