質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Q&A

1回答

1375閲覧

gasを利用しgmail本文の文字列(繰り返し)をすべてスプレッドシートに抽出する方法

mixa3328

総合スコア0

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

0グッド

0クリップ

投稿2021/10/22 10:55

編集2021/10/22 15:59

前提・実現したいこと

以前、同じような質問をしましたが、そちらは解決し
新たな問題発生のため質問しました

今回は、google apps scriptを利用した際にメール本文側に問題があった時の対処法をお教えいただけたらと思い質問させていただきました。

※注意:今回のプログラムはメールに送信されたカード決算の合計を計算するために作成しようとしています。

メール本文から抽出したいのは、「利用日」「利用先」「利用金額」の三点です

発生している問題・エラーメッセージ

メール本文の中に読み取りたい箇所が二か所以上ある場合一番上のものしか読み込んでくれない

 ○○様 いつもカードをご利用頂きありがとうございます。 お客様のカードご利用明細の内容をお知らせいたします。 ご利用カード:○○ ◇利用日:2021/10/26 ◇利用先:○○商店 ◇利用取引:買物 ◇利用金額:990円 ◇利用日:2021/10/31 ◇利用先:○○デパート ◇利用取引:買物 ◇利用金額:680円

とあった場合、上の26日の支払い分しかスプレッドシートに抽出できない

該当のソースコード

googleappsscript

1function getPayments() { 2 // メール検索クエリを作成 3 const SUBJECT = 'お支払金額のお知らせ'; // 利用お知らせメールの件名 4 const SUBJECT2 = 'ご指定金額到達のお知らせ'; 5 const ADDRESS = 'statement@vpass.ne.jp'; // お知らせメールの送信元 6 const LABEL_NAME = '読み込み済み'; // ラベル名 7 const LABEL_NAME2 = 'カード利用明細メール'; 8 const QUERY = '-subject:' + SUBJECT + '-subject:'+ SUBJECT2 + ' from:' + ADDRESS + ' -label:' + LABEL_NAME + ' label:' + LABEL_NAME2 ; 9 10 // メールを検索 11 var threads = GmailApp.search(QUERY); 12 13 // 該当メールがあった場合 14 if(threads.length > 0) { 15 const KEYWDS = ['利用日:', '利用先:', '利用金額:', '利用取引:']; 16 const LABEL = GmailApp.getUserLabelByName(LABEL_NAME); 17 18 var msgs = GmailApp.getMessagesForThreads(threads); 19 var sheet = SpreadsheetApp.getActiveSheet(); 20 21 for(i=0; i<msgs.length; i++){ 22 // 本文を取得 23 Body = msgs[i][0].getBody(); 24 if(Body.match(/利用先:.*/)){ 25 26 27 sheet.appendRow([ 28 Body.match(/利用日:.*/g)[0].replace(KEYWDS[0], ''), 29 Body.match(/利用先:.*/)[0].replace(KEYWDS[1], ''), 30 Body.match(/利用金額:[\d,]+/)[0].replace(KEYWDS[2], ''), 31 ]); 32 }else{ 33 sheet.appendRow([ 34 Body.match(/利用日:.*/g)[0].replace(KEYWDS[0], ''), 35 Body.match(/利用取引:.*/)[0].replace(KEYWDS[3], ''), 36 Body.match(/利用金額:[\d,]+/)[0].replace(KEYWDS[2], ''), 37 ]); 38 } 39 // 処理の完了後、ラベルを付与 40 threads[i].addLabel(LABEL); 41 }; 42 43 // 料金列に通貨表示を適用 44 amounts = sheet.getRange('B:B'); 45 setCurrencyLayout(amounts); 46 } 47} 48 49// 選択セルを通貨表示にフォーマット 50function setCurrencyLayout(range) { 51 range.setNumberFormat('[$¥-411]#,##0'); 52}

回答に求めたいこと

この質問での最終目標は、

二個ある決済記録を両方別々にスプレッドシートに書き込む

この1点です。

補足情報(FW/ツールのバージョンなど)

改変元のソースコードは
こちらの

@shika-e様作成のクレジットカード利用明細を自動で作るGoogle Apps Script
引用テキストhttps://qiita.com/shika-e/items/2e88df51b748c85fbd15

となっています。

回答いただけたら幸いです。
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2021/10/22 12:11

java ではないようです。javascript であれば、そのようにタグを変更して頂けますか。
mixa3328

2021/10/22 12:27

大変失礼しました。 正確にはgoogle apps scriptのみです。 よろしくお願いします
guest

回答1

0

この質問での最終目標は、
二個ある決済記録を両方別々にスプレッドシートに書き込む
この1点です。

引用元のコードに対して行われた加工から、明細として利用先がないパターンを想定しないといけないのだと認識しています。

↑の時点でメールの明細のバリエーションの想定が実例を見ない絞り込めないので、便宜的に「利用日」で始まる複数行のまとまりのなかに、取得したい情報がまとまっていることを仮定しました。

その複数行のまとまりのなかから、必要と思われる情報を埋めて 1 行分の明細として取得するようにしました。

余談ですが、利用先と利用取引の出現関係がわからない限りは"明細のまとまり"を認識するように処理する方向で考えるのかなと思います。
空白行は明細の区切りとしては利用できなさそうです。
◇が先頭にくることが確定できていれば、利用日が先頭でない場合に対応することは可能だと思います。
「:」も他の文中に出て来そうで、明細を識別する方法として微妙かなと思いました。

javascript

1const q365736 = () => { 2 // メール検索クエリを作成 3 const subjectToInclude = 'お支払金額のお知らせ'; // 利用お知らせメールの件名 4 const subjectToExclude = 'ご指定金額到達のお知らせ'; 5 const sender = 'statement@vpass.ne.jp'; // お知らせメールの送信元 6 const read = '読み込み済み'; // ラベル名 7 const creditUsage = 'カード利用明細メール'; 8 const query = `subject:${subjectToInclude} -subject:${subjectToExclude} from:${sender} -label:${read} label:${creditUsage}`; 9 10 const threads = GmailApp.search(query); 11 12 // 条件に合致するメールを含むスレッドがないので終わる 13 if(threads.length < 1) { 14 return; 15 } 16 const labelToSet = GmailApp.getUserLabelByName(read); 17 const threadMessages = GmailApp.getMessagesForThreads(threads); 18 const sheet = SpreadsheetApp.getActiveSheet(); 19 for(let i = 0; i < threadMessages.length; i++){ 20 const body = threadMessages[i][0].getBody(); 21 for(const row of parseTextToRow(body)) { 22 sheet.appendRow(row) 23 } 24 // 処理の完了後、ラベルを付与して、次の実行時にヒットしないようにする。 25 threads[i].addLabel(labelToSet); 26 } 27 28 // 料金列に通貨表示を適用 29 const amounts = sheet.getRange('C:C'); 30 setCurrencyLayout(amounts); 31 32} 33function* parseTextToRow(text) { 34 const lines = text.split("\n"); 35 const dateIndicator = /利用日:(.*)/; 36 const shop = /利用先:(.*)/; 37 const contractType = /利用取引:(.*)/; 38 const amount = /利用金額:([\d,]+)/; 39 const starts = lines.reduce((a,c,i)=> dateIndicator.test(c) ? [...a,i]: a,[]); 40 const ends = starts.slice(1).concat(lines.length); 41 const poses = starts.map((s,i)=>[s,ends[i] - 1]); 42 for(const [s,e] of poses) { 43 let candidate = ["","","",""]; 44 for(let i = s; i <= e; i++) { 45 const line = lines[i]; 46 if(dateIndicator.test(line)) { 47 candidate[0] = line.match(dateIndicator)[1]; 48 continue; 49 } 50 if(amount.test(line)) { 51 candidate[1] = line.match(amount)[1]; 52 continue; 53 } 54 if(shop.test(line)) { 55 candidate[2] = line.match(shop)[1]; 56 continue; 57 } 58 if(contractType.test(line)) { 59 candidate[3] = line.match(contractType)[1]; 60 continue; 61 } 62 } 63 yield [candidate[0],(candidate[2] || candidate[3]) ,candidate[1]]; 64 } 65} 66 67// 選択セルを通貨表示にフォーマット 68const setCurrencyLayout = (range) => range.setNumberFormat('[$¥-411]#,##0');

投稿2021/10/22 20:35

papinianus

総合スコア12705

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mixa3328

2021/10/23 02:46

回答ありがとうございます。 説明が足りませんでした。 メールのバリエーションとして 上記質問本文(利用日から始まる複数列が一つのものから複数あるものまで)とともに コメント欄に書かせていただくものがございます。 ホール本文として ```メール ご利用カード:○○カード ◇利用日:2021/04/24 ◇利用取引:買物 ◇利用金額:2,700円 ``` のようになっており 利用先のみ消滅しているような本文となっております。 そして、この度いただいたコードなのですが、実行しても一秒足らずで終了してしまいます。 検索にヒットするメールがないのでしょうか? また、初心者質問で恐縮ですが コードをマルコピして利用させていただいたのですが、「constで始まるコード部分」と「function* parseTextToRow」とが、わかれているためうまく機能しているのかわからなくなってしまいました。 いろいろお教えいただけると幸いです。 よろしくお願いします。
papinianus

2021/10/25 11:08

ヒットしてないんじゃないですかね。 メールフォルダがみえないので、わからないです。 > const query = `subject:${subjectToInclude} -subject:${subjectToExclude} from:${sender} -label:${read} label:${creditUsage}`; を > const query = `-subject:${subjectToInclude} -subject:${subjectToExclude} from:${sender} -label:${read} label:${creditUsage}`; に戻してください(これ間違いじゃなかったんですね。) その書き方のパターンには対応しているはず。 qなんちゃらのやつだけ実行すればいいようになってます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問