はじめに
ねえ、おとん、
楽天市場のお買い物マラソンでたくさん注文したけど、何を注文したか忘れちゃうんだけど。
いくら購入したのかいちいち調べるのも面倒なんだけど。
そお?おとんも結構忘れるよ。
でも、注文リストをスプレッドシートに自動的追加してるから忘れても大丈夫なの。
購入金額も記録しているから、合計はすぐ出るんだよ。
そんなことできるの?
ちょっとなるの環境でも設定してくれる?
ということで、楽天市場でお買い物をした時に、購入日、購入品、購入価格等の一覧を自動的に作る仕組みを作りました。
対象読者
ここで紹介する内容は、以下の条件を満たす方が対象となります。
対象読者
- 楽天市場を使っている
- 楽天市場の注文内容確認メールをGmailで受け取っている
- 楽天市場の注文内容をスプレッドシートに自動追加したい
注文リストの自動生成
まず、Gmailから該当メールを抽出する部分、処理済みメールにラベルを付ける部分は、これまで見てきたカレンダーに登録する処理と同様です。
主な違いは以下の2点です。
- Gmailでメールを抽出する際の検索文字が、楽天市場からの注文確認メール
- メール本文から注文内容を抽出方法が楽天市場購入確認メールに特化している
- メールから抽出した内容をスプレッドシートの最終行に追記する
では、詳細を見ていきましょう。
処理概要
楽天市場で商品を購入すると、以下のような利用明細をメールで受け取ることができます。
そして、ここで紹介する処理を実装すると、以下のようにスプレッドシートの最下行に注文内容を自動追加するようになります。
スプレッドシートに利用内容を登録するためには、以下の処理を考えます。
- 楽天市場からの注文確認メールで未処理のメールを抽出
- 注文確認メールから、商品、値段、数量、送料、使用ポイント額、使用クーポン額、支払額、獲得ポイント、ショップ名を抽出
- スプレッドシートの最終行に②で得た情報を追記
- 注文確認メールに処理済みラベルを付ける
そして、この処理を定期的に行わせます。
未処理メールの抽出
まず、楽天市場からの注文確認メールで、未処理のメールの抽出は以下の検索文字列で実現します。
ここでのメールの抽出条件の構文は、Gmailの抽出条件に設定する構文と同じです。
var SEARCH_QUERY = 'is:unread -label:' + LABEL + ' from:order@rakuten.co.jp 【楽天市場】注文内容ご確認(自動配信メール)';
処理済みを識別するために、処理完了後に該当メールのラベルに「自動処理済」を付けます。
そのため、抽出条件は以下の4つとなります。
- 未読であること(is:unread)
- 「自動処理済」のラベルが付いていないこと(-label:自動処理済)
- 楽天市場のメールであること(from:order@rakuten.co.jp)
- 楽天市場の注文確認メールであること(subject:【楽天市場】注文内容ご確認(自動配信メール))
Gmailへのアクセス方法については以下の記事を参照ください。
注文内容抽出
注文確認メールをシンプルテキストで取り出すと以下のようになります。(一部抜粋)
ここから、 商品、値段、数量、送料、使用ポイント額、使用クーポン額、支払額、獲得ポイント、ショップ名を抽出します。
--------------------------------------------------------------------- 本メールはお客様のご注文情報が楽天市場のサーバに到達した時点で送信 される、自動配信メールです。ショップによる注文の確認 をもって売買契約成立となります。 --------------------------------------------------------------------- この度は楽天市場内のショップ「Happy Smiles」を ご利用いただきまして、誠にありがとうございます。 --------------------------------------------------------------------- [ショップ名] Happy Smiles (39ショップ) ※39(サンキュー)ショップの表示は購入時点での情報を元に表示しています。 ========== [お届け先] 関谷 様 〒000-00000 群馬県 (TEL) 000-0000-0000 [商品] ソニー SONY用互換AVケーブル(S端子付き) VMC-15FS C ソニー SONY AVケーブル(S端子付き) VMC-15FS C互換品(R1802-029) 価格 750(円) x 1(個) = 750(円) ※10%税込 獲得ポイント14 ポイント2倍 ********************************************************************* 送料計 0(円) お支払い金額 750(円) --------------------------------------------------------------------- 今回のお買い物で62ポイント獲得予定 ※その他、ポイントに関する注意事項はメールの下部をご確認ください。 --------------------------------------------------------------------- [受注番号] 331668-20211108-02608804 [日時] 2021-11-08 11:36:08 [注文者] 関谷 (セキヤ) 様 〒000-0000 群馬県 (TEL) 000-0000-0000 [お支払い方法] クレジットカード 一括払い [ポイント利用] なし [配送方法] メール便(ヤマト運輸) [お届け日時]
注文内容の抽出部分は以下のコードように処理します。
基本的には、抽出するためのキーワードを検索し、値を抽出することを繰り返します。
綺麗にまとまりませんでしたが、文字列処理なのでしょうがないかと。
// 注文メールの内容確認
var rows = body.split("\n");
for (var i in rows) {
var row = rows[i];
// 情報抽出
if (row.indexOf(SHOP_NAME) >= 0) {
shopName = row.replace(SHOP_NAME, '');
}
else if(row.match(/^\[商品\]/)){
flgItem = 1;
}
else if(row.match(/^----------/)){
flgItem = 1;
}
else if(row.match(/^価格/)){
var strs = row.split(' ');
price = strs[2].replace('(円)', '');
count = strs[4].replace('(個)', '');
// 価格情報取得段階でスプレッドシートに登録する
libAddOrderToList.registOderInfoToSpreadSheet(
'test', '', startTime, '楽天市場', itemName, price, count, shopName);
}
else if(shipping == null && row.indexOf(SHIPPING) >= 0){
shipping = row.replace(SHIPPING, '').replace('(円)', '').replace('円', '');
}
else if(point_used == null && row.indexOf(POINT) >= 0){
point_used = row.replace(POINT, '').replace('(円)', '').replace('(円)', '');
}
else if(coupon_used == null && row.indexOf(COUPON) >= 0){
coupon_used = row.replace(COUPON, '').replace('(円)', '').replace('(円)', '');
}
else if(coupon_used == null && row.indexOf(RA_COUPON) >= 0){
flgRaCoupon = 1;
}
else if(row.match(/今回のお買い物で.*ポイント獲得予定/)){
point_get = row.replace('今回のお買い物で', '').replace('ポイント獲得予定', '');
}
else if(row.match(/今回のお買い物で獲得するポイント/)){
point_get = row.replace('今回のお買い物で獲得するポイント', '');
}
else if(row.match(/合計.*\(円\)/)){
payment = row.replace('合計', '').replace('(円)', '');
}
else if(payment == null && row.indexOf(PAYMENT) >= 0){
payment = row.replace(PAYMENT, '').replace('(円)', '').replace('(円)', '');
}
if(flgItem == 1){
flgItem++;
}
else if(flgItem == 2){
itemName = row;
flgItem = 0; // 商品名最初の1行のみ抽出する
}
if(flgRaCoupon == 1 || flgRaCoupon == 2){
flgRaCoupon++;
}
else if(flgRaCoupon == 3){
// -10(円) x 1(枚) = 10(円)
var strs = row.split(' ');
coupon_used = '-' + strs[8].replace('(円)', '');
Logger.log('coupon_used=' + coupon_used);
flgRaCoupon = 0;
}
}
スプレッドシート追記
スプレッドシートへの追記は、libAddOrderToListライブラリを作成して呼び出しています。
// 価格情報取得段階でスプレッドシートに登録する(商品、価格、数量、ショップ名登録)
libAddOrderToList.registOderInfoToSpreadSheet(
'test', '', startTime, '楽天市場', itemName, price, count, shopName);
// 追加情報の登録処理(送料、使用ポイント、使用クーポン、支払額、獲得ポイント)
libAddOrderToList.registOderInfoToSpreadSheetSub(
'test', '', shipping, point_used, coupon_used, payment, point_get);
ここでは、スプレッドシート名を「test」としました。※お好みで変更してください。
libAddOrderToListライブラリについてはこちらの記事で説明しています。
全てのコード
/*
* 楽天市場の注文内容をスプレッドシートに登録する
*
* 2021/09/15 Created by N.Sekiya
*/
// GMail検索文字列(楽天市場からの注文確認メール)※未読でラベルなしが条件
var SEARCH_QUERY = 'is:unread -label:' + LABEL + ' from:order@rakuten.co.jp 【楽天市場】注文内容ご確認(自動配信メール)';
// 検索文字列
var SHOP_NAME = '[ショップ名] ';
var SHIPPING = '送料計';
var POINT = 'ポイント利用 ';
var COUPON = 'クーポン利用 ';
var RA_COUPON = 'ラ・クーポン利用';
var PAYMENT = 'お支払い金額';
function rakutenAddOrderList() {
var threads = GmailApp.search(SEARCH_QUERY, 0, 1);
if (threads.length === 0) {
Logger.log('メールが見つかりません');
return;
}
for(var k in threads){
var thread = threads[k];
var messages = thread.getMessages();
for (var j in messages){
var message = messages[j];
var body = message.getPlainBody(); // HtmlメールからPlain textの本文を取得する
var startTime = message.getDate();
//Logger.log("body=" + body);
var itemName = null;
var shopName = null;
var price = null;
var count = null;
var payment = null;
var shipping = null;
var point_used = null;
var point_get = null;
var coupon_used = null;
var flgItem = 0;
var flgRaCoupon = 0;
// 注文メールの内容確認
var rows = body.split("\n");
for (var i in rows) {
var row = rows[i];
// 情報抽出
if (row.indexOf(SHOP_NAME) >= 0) {
shopName = row.replace(SHOP_NAME, '');
}
else if(row.match(/^\[商品\]/)){
flgItem = 1;
}
else if(row.match(/^----------/)){
flgItem = 1;
}
else if(row.match(/^価格/)){
var strs = row.split(' ');
price = strs[2].replace('(円)', '');
count = strs[4].replace('(個)', '');
// 価格情報取得段階でスプレッドシートに登録する
libAddOrderToList.registOderInfoToSpreadSheet(
'test', '', startTime, '楽天市場', itemName, price, count, shopName);
}
else if(shipping == null && row.indexOf(SHIPPING) >= 0){
shipping = row.replace(SHIPPING, '').replace('(円)', '').replace('円', '');
}
else if(point_used == null && row.indexOf(POINT) >= 0){
point_used = row.replace(POINT, '').replace('(円)', '').replace('(円)', '');
}
else if(coupon_used == null && row.indexOf(COUPON) >= 0){
coupon_used = row.replace(COUPON, '').replace('(円)', '').replace('(円)', '');
}
else if(coupon_used == null && row.indexOf(RA_COUPON) >= 0){
flgRaCoupon = 1;
}
else if(row.match(/今回のお買い物で.*ポイント獲得予定/)){
point_get = row.replace('今回のお買い物で', '').replace('ポイント獲得予定', '');
}
else if(row.match(/今回のお買い物で獲得するポイント/)){
point_get = row.replace('今回のお買い物で獲得するポイント', '');
}
else if(row.match(/合計.*\(円\)/)){
payment = row.replace('合計', '').replace('(円)', '');
}
else if(payment == null && row.indexOf(PAYMENT) >= 0){
payment = row.replace(PAYMENT, '').replace('(円)', '').replace('(円)', '');
}
if(flgItem == 1){
flgItem++;
}
else if(flgItem == 2){
itemName = row;
flgItem = 0; // 商品名最初の1行のみ抽出する
}
if(flgRaCoupon == 1 || flgRaCoupon == 2){
flgRaCoupon++;
}
else if(flgRaCoupon == 3){
// -10(円) x 1(枚) = 10(円)
var strs = row.split(' ');
coupon_used = '-' + strs[8].replace('(円)', '');
Logger.log('coupon_used=' + coupon_used);
flgRaCoupon = 0;
}
}
// 追加情報をスプレッドシートに登録
if(shipping == null) shipping = 0;
if(point_used == null) point_used = 0;
if(coupon_used == null) coupon_used = 0;
libAddOrderToList.registOderInfoToSpreadSheetSub(
'test', '', shipping, point_used, coupon_used, payment, point_get);
// 処理済みメールとしてラベルを付ける
libCtrlMail.putLabel(thread);
}
}
}
使用しているライブラリは以下2点です。
- メール処理:libCtrlMail
- スプレッドシート処理:libAddOrderToList
libCtrlMailについてはこちらを参照ください。
libAddOrderToListについてはこちらを参照ください。
繰返し実行
正しく動作することを確認したら、繰り返し実行できるよう設定します。
繰り返し実行には、トリガーの設定を行います。設定方法については、以下の記事を参照ください。
繰り返し実行する間隔は、急ぐ必要のない処理なので5分おきにします。
最後に
まず、手作業でデータ処理を色々するのはホントに嫌です。
単純作業はプログラムにやらせて、結果だけが得られればいいと心の底から思っています。
そのため、少しでも手間が省ければと思い、自動化処理を作り続けています。
正しく動くようになると、楽しくなるものです。
では、今日も良い一日を。