おじさんのブログ

おじさんになってからPython、GAS、SEOなどを学習した内容をアウトプットするブログです。

GASでGmailからスプレッドシートへ文面を抽出するのに参考にした記事

GASを使ってGmailから特定のメールを選出し、欲しい文面を出力できるようになるまでに参考にした記事のリスト。やはりほぼコピペになったが、カラムごとに欲しい箇所を抽出するのに正規表現を用いる必要がありそこが今回の学びだった。

今回参考にさせていただいたGASのコードの記事

Gmailで受信した情報をGoogleAppsScriptで抽出してGoogle Spreadsheetに転記する - Qiita

[Google Apps Script]メール情報をスプレッドシートに書き出す | Developers.IO

今回は過去のメール群から欲しい文面をある程度の数を単発で集められて、分析ができればよかったので、こちらのコードに助けられました。 ただ、自分がやりたかったことを実現するために、「文面を丸ごと取り出す→正規表現で条件を満たすテキストを抽出し変数に入れる」というやり方になってしまったが、何度もテキスト丸ごとを取り出すのは明らかに無駄なので、なんとかしたい…。

正規表現の書き方で参考にさせていただいた記事

正規表現の例文-複数行にまたがる処理 - Qiita

正規表現自体は普段の業務でスプレッドシートへちょろっと書くぐらいだったので、普通に書くと1行単位で検索されてしまうということを知らなかった…。今回はメール本文から【お問い合わせ内容】のような複数行にまたがるテキストを持ってくる必要があり、かなりつまづいてしまった。

[\s\S]*?

これは今後も使うはずなので覚えておこう。

ストレイライトエンジニア: 正規表現で特定の文字より前/特定の文字より後を抜き出す

(?<=あ)(.)  →「あ」よりあとを抜き出す (.)(?=あ)  →「あ」よりまえをぬきだす

【お問い合わせ日時】 2020/03/11

【お問い合わせ内容】 ホゲホゲ。 フガフガ。 . . . のようなメール文面なので、見出しに対応したテキストを取得したかったため、この書き方がやりたいことずばりだった。

最終的なコード(ほぼ完全コピペ)

function fetchContactMail() {
  /* Gmailから特定条件のスレッドを検索しメールを取り出す */
  var strTerms = '***';
  var myThreads = GmailApp.search(strTerms, 0, 500); //条件にマッチしたスレッドを取得
  var myMsgs = GmailApp.getMessagesForThreads(myThreads); //スレッドからメールを取得する →二次元配列で格納
  var valMsgs = [];
  /* 各メールから日時、送信元、件名、内容を取り出す*/
  for(var i = 0;i < myMsgs.length;i++){
  valMsgs[i] = [];
  valMsgs[i][0] = myMsgs[i][0].getDate();
  valMsgs[i][1] = myMsgs[i][0].getPlainBody().slice(0,3000).match(  /(?<=【hanamogera】)[\s\S]*?(.*)(?=【)/);  // メール本文の中から特定のワードの前後を正規表現で抜き出す(以下、同様)
  valMsgs[i][2] = myMsgs[i][0].getPlainBody().slice(0,3000).match(  /(?<=【hoge】)[\s\S]*?(.*)(?=【)/);
  valMsgs[i][3] = myMsgs[i][0].getPlainBody().slice(0,3000).match(  /(?<=【fuga】)[\s\S]*?(.*)(?=piyo)/);
  valMsgs[i][4] = myMsgs[i][0].getPlainBody().slice(0,3000).match(  /(?<=【foo】)[\s\S]*?(.*)(?=【)/);
  valMsgs[i][5] = myMsgs[i][0].getPlainBody().slice(0,3000).match(  /(?<=\[bar\])[\s\S]*?(.*)(?=\[1\])/);
  valMsgs[i][6] = myThreads[i].getPermalink();
 } 
  /* スプレッドシートに出力 */
 if(myMsgs.length>0){
    var mySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('メール'); //シートを取得
    var maxRow = mySheet.getDataRange().getLastRow(); //シートの使用範囲のうち最終行を取得
    mySheet.getRange(maxRow+1, 1, i, 7).setValues(valMsgs); //シートに取得したメッセージのデータを追加
  }
}

GASでスプレッドシート→slack投稿の改修

前回やったコードだと以下のような課題があったため改修してみたのでその記録。

  • slack投稿しなくて良いステータス変更は投稿しないようにしたい
  • 一部のslack投稿ではメンションを飛ばしたい
  • ステータス”完了”時に日付がスプレッドシートに記入されるようにしたい

slack投稿しなくて良いステータス変更は投稿しないようにしたい

これはif文で条件分岐すれば良いとはわかるが「いずれかの条件を満たすとき」の書き方がわからなかった。 結局、特定のステータスになったときのフラグを0,1にしてスプレッドシート上の関数で分けて、それをGASで判断するというやり方になったが、これはスマートでないのは素人にもわかる。

同じことをやるにしても、GAS上でやった方が良さそう。 もしくは↓のようにsomeメソッド?を使う? 方がスマートかもしれない雰囲気。

GASで列データの配列に特定の値が含まれているかを判定するsomeメソッドの使い方

今後も同じようなことは必要となりそうなので、もっとちゃんと勉強した方が良さそう。

一部のslack投稿ではメンションを飛ばしたい

メンションを飛ばすのに”@hogehoge"ではダメと知り、↓のように書けば良いと学んだ。

SlackのIncoming Webhooksでメンションを飛ばす方法 - Qiita

これもスプレッドシート上にUSER_IDのテーブルを作って、スプレッドシートの関数でなんとかしたが、これももっと良いやり方がありそう。

ステータス”完了”時に日付がスプレッドシートに記入されるようにしたい

setValueメソッドでセルの入力をする、New DATE()で今日の日付を取得する、を組み合わせることでいけた。 シンプルだけど、これを自動化できると割と省力化&抜け漏れ防止につながった。

GASでスプレッドシートのステータス変更をslackへ投稿するのに参考にした記事

GASが全くわからない状態から、GASを使って進捗を管理しているスプレッドシートに変更があった際にslackへ通知するということをできるようになるまでに参考にした記事のリスト。業務で使うことを優先したので結局コピペになってしまったので、うまく動かなったのはなぜかや応用を効かせる際に見返すために。

slackとスプレッドシート連携の参考にしたページ

SlackのWebhook URL取得手順 - Qiita

具体的なコードの参考にしたページ

スプレッドシートで作ったtodoリストでステータスを変更するとslackに流す - Qiita

まずは動くモノをということを優先した結果、↑をコピペさせていただくことに…。 これをベースに応用を効かせることを目指そう。

今回うまくいかなかったページ

Slackにスプレッドシートの変更がある度に内容を通知する方法 | ビジネスチャットマスター|働き方を変えるビジネスチャットに関する情報をお届けします

GASを使って、スプレッドシート更新時にSlack通知を飛ばしてくれるbotを作ってみた - Qiita

これも今回やりたかったことに近いはずなのに、どうにもエラーを解決することができなかった…。 後で見返すために。

ちゃんと理解する必要がありそうな内容

Google Apps Scriptで値の変更をトリガーにしつつ変更されたセルの行番号と列番号を知る

スプレッドシートの内容をトリガーにするにはこの理解が必要(だけど今回は…)。 勉強のために買ったGASの本『詳解! Google Apps Script完全入門』の著者の方のWebサイトのようで、他にもGASを学習するなら参考になる内容がたくさんあるっぽい。

最終的なコード(完全コピペ)

var postUrl = "***";
var postChannel = "#***";

function myOnEdit()
{
  var keywordCol = 8;         // ステータス列idx
  var inChargePersonCol = 7;  // 担当者列idx
  var dataStartCol = 2;       // データが開始する列のidx
  var taskContentCol = 2;     // タスク内容列idx

  var colOffset = 1;  // A列はoffset

  var dataColNum = 8;  // データの列数
  var range = SpreadsheetApp.getActiveRange();
  var rowidx = range.getRow();
  var colidx = range.getColumn();
  if(colidx != keywordCol){ return ; }
  var sheet = SpreadsheetApp.getActiveSheet();

  // ステータスに変更のあったtodo1行
  var aLine = sheet.getRange(rowidx, dataStartCol, 1, dataColNum);
  if(!aLine){
    return;
  }

  // ステータスセルの値
  var status = aLine.getCell(1,keywordCol-colOffset).getValue();

  // 担当者セルの値
  var username = aLine.getCell(1, inChargePersonCol-colOffset).getValue();
  if(!username){
    username = "unknown";
  }

  // タスクの内容
  var taskContent = aLine.getCell(1, taskContentCol-colOffset).getValue();  
  if(!taskContent){
    return;
  }

  // ステータスを変更した人の名前(googleのアカウント名) 
  // todo: めんどくさそうなので一旦なしで
  // var editor = Session.getActiveUser().

  var message = username;
  message += "さんが、タスク「";
  message += taskContent;  
  message += "」のステータスを";

  if( status == '完了' ){
    aLine.setBackgroundColor("#efefef");
    aLine.setFontColor('black');
    message += "【完了】";

  }
  else if(status == '未着手' || !status){
    aLine.setBackgroundColor('white');
    message += "【未着手】";
  }
  else if(status == '入力中'){
    aLine.setBackgroundColor('#fce5cd');
    message += "【入力中】";
  }

  message += "にしました";

  sendHttpPost(message, "TODOテスト");
}

function test()
{
  sendHttpPost("上様が、タスク「ほげほげほげ」のステータスを【進行中】にしました", "TODOテスト");
}

function sendHttpPost(message, username)
{
  var jsonData =
  {
     "channel" : postChannel,
     "username" : username,
     "text" : message
  };
  var payload = JSON.stringify(jsonData);
  var options =
  {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : payload
  };

  UrlFetchApp.fetch(postUrl, options);
}

初めてPythonでツイートを取得するのに参考にした記事

プログラミング初心者が、写経&書き換えでツイートを取得できるようになるまでに参考にした記事のリスト。 分析用のデータ取得を目的にやったので、あとできちんと見返すようとして。

具体的なコードの参考にしたページ

TwitterAPIでツイートを全件取得する方法【Python】|かえるのほんだな

PythonでTwitterの検索とタイムラインの取得 - iMind Developers Blog

Tweepyでツイートを取得 - Qiita

User Object(ユーザーオブジェクト)の説明

Python自体の書き方の参考にしたページ

【技術】pythonのopen関数の第二引数の違いが分かった - エンジニアリングとお金の話

https://ai-inter1.com/python-comment/

Python: テキストファイルに書き込み - write()、writelines()メソッド | Yukun's Blog

Pythonでcsvのデータを読み書きする方法

PythonでCSVの読み書き - Qiita

Pythonでファイルの読み込み、書き込み(作成・追記) | note.nkmk.me

with構文とは何なのか - 年中アイス

最終的なコード(ほぼコピペです)

import tweepy
import csv

Consumer_key = '****'
Consumer_secret = '****'
Access_token = '****'
Access_secret = '****'

auth = tweepy.OAuthHandler(Consumer_key, Consumer_secret)
auth.set_access_token(Access_token, Access_secret)
api = tweepy.API(auth)

data = []
pages = [1, 2, 3]

for page in pages:
    results = api.user_timeline(screen_name="****", count = 200, page = page)
    for r in results:
        data.append([r.created_at
            , r.text
            , r.user.name
            , r.favorite_count
            , r.retweet_count])

with open("text.csv", 'w') as f:
    writer = csv.writer(f, lineterminator = '\n')
    writer.writerow(["created_at"
            , "text"
            , "user.name"
            , "favorite_count"
            , "retweet_count"])
    writer.writerows(data)