2014年6月5日木曜日

Google apps for Business を検討している方へ

知ってたらGoogleApps使わなかった・・・ってならないように
実際に使ってみて色々ありましたので記録しときます。

えーと・・・

メール
・迷惑メールフィルタはデフォルトです。解除できません。
・Google側が受信拒否してもこちらはそれに気がつきません。
・結構普通の文面でも迷惑メールになるときあります。仕事の内容によっては、迷惑メール扱いされてしまうことが多くなるかも。その時はホワイトリストとか使う。
・メールディストリビュータとか使って一括配信しようとしても80通ぐらいで止まる。
・管理画面とかドンドンブラッシュアップ?されるので、たまに見ると「ん?これなに?」みたいになります。
・1アカウントあたりの送信最大件数とか注意しとくべし。メルマガとかやる人はとくに。
・お年寄りにはgmailは辛いかもしれません。
・転送メール設定しても、迷惑メールにフィルタされたものは転送されません。
案外これが悪さすることがある。
・代理店の方と仲良くしておいた方がいい。何かあったときに困ります。

また思い出したら書き出しておこう

特定タイトルを含む2chスレッドをみつけて、直近の書き込みをメールするスプレッドシートのスクリプト

GoogleAppsのスプレッドシートのスクリプトを使って、2chの特定のタイトルをもつ書き込みをメールさせるというものを作った。ブラウジングすることなくメールで読めるのでなかなか良かったが、2chが検索できなくなったらしいのでもう使えないのかな。

まぁ、Googleのサーバをつかってクロールするというのはなかなか楽しい。
長時間はNGらしいので短い処理にするとよろしい

GoogleAppsのスプレッドシートをいっこ生成。
6つのシートを作る。名前は以下
「ログ」
「メールアドレス」
「キーワード」
「2ch検索結果HtmlTemp」
「2chスレッド一覧」
「結果」

シート「メールアドレス」には送信先メールアドレスをA1セルに記述しておく
シート「キーワード」はA列にキーワード、B列にキーワードのEUC-JP変換したものを記述

スクリプトに以下を記述
webcro2ch を適当な時間で自動実行させればクロールしてメール送信する。


----------------------------------------


//スプレッドシート: 400,000 セル、
//1 シートあたり 256 列まで。
//アップロードして Google スプレッドシートの形式に変換できるスプレッドシート
//ファイルの最大サイズは 20 MB です。また、400,000 セル以下、1 シートあたり 256 列以下である必要があります。
// http://support.google.com/docs/bin/answer.py?hl=ja&answer=37603

function webcro2ch() {
  var SHT_LOG_NAME = 'ログ';//ログ記録するシート名
  var TO_ADDRESS;//メール送信先

  writelog(SHT_LOG_NAME, "-----------");
  writelog(SHT_LOG_NAME, "プログラム開始");
 //現在毎時トリガー
  var beferd = 3;//何日前までのデータを取得するか
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetLog = ss.getSheetByName(SHT_LOG_NAME);
  var sheetKW = ss.getSheetByName('キーワード');//キーワード一覧の入っているページ
  var sheet1 = ss.getSheetByName('2ch検索結果HtmlTemp');
  var sheet2 = ss.getSheetByName('2chスレッド一覧');
  var sheet3 = ss.getSheetByName('結果');
  var sheet4 = ss.getSheetByName('メールアドレス');

  ///////////////////////////////////////////////////////////////////////////////
  //初期化
  ///////////////////////////////////////////////////////////////////////////////
  //メール送信先アドレスの取得
  TO_ADDRESS = sheet4.getRange(1,1).getValue();//
  if(TO_ADDRESS==""){
    //メールアドレスが空
    throw "ERROR:メールアドレスが設定されていません。";
  }
  sheet1.clear();
  sheet2.clear();
  sheet3.clear();
  //ログたまりやすいので、1000行を超えたら、1000行以降は削除する
  var logRow = sheetLog.getMaxRows();
  if(logRow>1000){
    sheetLog.deleteRows(1001, logRow-1000);
  }
  var shtRow = sheet1.getMaxRows();
  if(shtRow>1000){
    sheet1.deleteRows(1001, shtRow-1000);
  }
  shtRow = sheet2.getMaxRows();
  if(shtRow>1000){
    sheet2.deleteRows(1001, shtRow-1000);
  }
  shtRow = sheet3.getMaxRows();
  if(shtRow>1000){
    sheet3.deleteRows(1001, shtRow-1000);
  }
  shtRow = sheet4.getMaxRows();
  if(shtRow>1000){
    sheet4.deleteRows(1001, shtRow-1000);
  }


  ///////////////////////////////////////////////////////////////////////////////
  //設定済キーワードの取得
  ///////////////////////////////////////////////////////////////////////////////
  writelog(SHT_LOG_NAME, "キーワード取得開始");
  var kwMax = sheetKW.getLastRow();
  var swords = new Array(kwMax);
  //キーワードの説明
  for(ii=0;ii    //第一要素 キーワード,第二要素 EUC-JP変換したもの
    swords[ii] = [sheetKW.getRange(ii+1,1).getValue(),sheetKW.getRange(ii+1,2).getValue()];
    writelog(SHT_LOG_NAME, "キーワード:"+swords[ii][0]);
  }
  writelog(SHT_LOG_NAME, "キーワード取得完了");

  ///////////////////////////////////////////////////////////////////////////////
  //2chで各キーワードのスレッドがあるのか確認
  //シート'2ch検索結果HtmlTemp'に記録
  //取得日時,キーワード,検索先(2ch),レスポンスコード,件数,状態(sourceget,linkget),
  ///////////////////////////////////////////////////////////////////////////////
  writelog(SHT_LOG_NAME, "スレッド検索開始");
  var opt={"contentType":"text/html","method":"get"};
  for(var ii=0;ii    sheet1.insertRowBefore(1);//新しく1行目を挿入
    var response;
    var resCode;
    var contentText;
    try{
      response = UrlFetchApp.fetch("http://find.2ch.net/?STR="+swords[ii][1],opt);
      resCode = response.getResponseCode();
      contentText = response.getContentText("EUC-JP");
    }
    catch(e){
      resCode = 999;
      contentText = '';  
    }
    //1行目にデータ登録
    var d = new Date();
    sheet1.getRange(1,1).setValue(d.toString());//日時
    sheet1.getRange(1,2).setValue(swords[ii][0]);//キーワード
    sheet1.getRange(1,3).setValue("2ch");//サイト
    sheet1.getRange(1,4).setValue(resCode);//レスポンスコード
    sheet1.getRange(1,5).setValue(contentText);//HTML
  }
  writelog(SHT_LOG_NAME, "スレッド検索終了");

  ///////////////////////////////////////////////////////////////////////////////
  //リンク先の切り出し
  ///////////////////////////////////////////////////////////////////////////////
  writelog(SHT_LOG_NAME, "スレッド一覧作成開始");
  var rmax = sheet1.getLastRow();

  for(var ii=0;ii    var mode = sheet1.getRange(ii+1,6).getValue();
    var w = sheet1.getRange(ii+1,2).getValue();//キーワード取得
    var str = sheet1.getRange(ii+1,5).getValue();//HTMLを取得
    var xx = str.match(/
.*?<\/a>/gi);

    if(xx){    //xxが配列で無い場合はfalseになる
      for(var jj=0;jj        //sheet1.getRange(ii,jj+6).setValue(xx[jj]);//デバッグ用
        var m = xx[jj].match(/(.*)?<\/a>/gi);//最初の(.*)はスレッドタイトル、次の(.*)はリンクURL
        var d = new Date();
        sheet2.insertRowBefore(1);//新しく1行目を追加
        sheet2.getRange(1,1).setValue(d.toString());//日時
        sheet2.getRange(1,2).setValue(w);//キーワード
        sheet2.getRange(1,3).setValue(RegExp.$2);//スレッドタイトル
        var u = RegExp.$1;//URL
        u = u.substr(0,u.lastIndexOf("/")) + '/';//後ろの/以降を削除する
        sheet2.getRange(1,4).setValue(u);//URL
      }
    }
  }
  writelog(SHT_LOG_NAME, "スレッド一覧作成終了");

  ///////////////////////////////////////////////////////////////////////////////
  //スレッドの書き出し(100レコードまで)
  ///////////////////////////////////////////////////////////////////////////////
  writelog(SHT_LOG_NAME, "掲示板内容取り出し開始");

  var listmax = sheet2.getLastRow();
  for(var ii=0;ii    var w = sheet2.getRange(ii+1,2).getValue();//キーワード
    var t = sheet2.getRange(ii+1,3).getValue();//タイトル
    var u = sheet2.getRange(ii+1,4).getValue();//URL
    writelog(SHT_LOG_NAME, "スレッド読出("+(ii+1)+"/"+listmax+"):"+ w + ":" + t);
    var response;
    var resCode;
    var html;
    var errMsg='';
    try{
      response = UrlFetchApp.fetch(u,opt);
      resCode = response.getResponseCode();
      html = response.getContentText("Shift_JIS");
    }
    catch(e){
      resCode = 999;
      html = '';
      errMsg = ':' + e.message;
    }
    writelog(SHT_LOG_NAME, "RES:" + resCode + ":" + u + errMsg);
    var yy = html.match(/(\d{1,4})\s+:.*?:(\d{4}\/\d{2}\/\d{2}).*?(\d{2}:\d{2}:\d{2}).*?(.*)?/gi);

    if(yy){
      writelog(SHT_LOG_NAME, "全件:" + yy.length);
      for(var jj=0;jj        var m = yy[jj].match(/(\d{1,4})\s+:.*?:(\d{4}\/\d{2}\/\d{2}).*?(\d{2}:\d{2}:\d{2}).*?
(.*)?

/gi);//日付、時間、コンテンツを取得

        var d = new Date(RegExp.$2 + ' ' + RegExp.$3);
        //前日以降のデータのみ取得
        var td = new Date();//本日の日時
        td.setTime(td.getTime()-beferd*24*60*60*1000);//指定日前の日時

        if(d.getTime()>td.getTime()){
          sheet3.insertRowBefore(1);//新しく1行目を追加
          sheet3.getRange(1,1).setValue(w);
          sheet3.getRange(1,2).setValue(t);
          var n = RegExp.$1;
          sheet3.getRange(1,3).setValue(n);//番号
          sheet3.getRange(1,4).setValue(d);//日付
          var c = RegExp.$4;
          c = c.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi,"");//HTMLタグを置換
          c = c.replace(/>/gi,">");
          if(c.length>150){
            c = c.substring(0,150)+'…';
          }
          sheet3.getRange(1,5).setValue(c);//コンテンツ
          sheet3.getRange(1,6).setValue(u + n);//URL
        }
      }
    }
  }
  writelog(SHT_LOG_NAME, "掲示板内容取り出し終了");

  ///////////////////////////////////////////////////////////////////////////////
  //メール送信
  ///////////////////////////////////////////////////////////////////////////////
  writelog(SHT_LOG_NAME, "メール送信開始");
  var rngResults = sheet3.getRange(1, 1, sheet3.getLastRow(), 6);
  var aryResults = rngResults.getValues();
  if(aryResults){
    var kw="";
    var tmpKW="";
    var tt="";
    var tmpTT="";
    var c = "";
//昇順ならこれ
//    for(var ii=aryResults.length-1;ii>=0;ii--){
//降順ならこれ
    for(var ii=0;ii<=aryResults.length-1;ii++){
      tmpKW = aryResults[ii][0];//キーワード
      if(kw!=tmpKW){
        if(c!=""){
          //キーワードが変わったので今までの情報をメール送信する
          MailApp.sendEmail(TO_ADDRESS,"2ch検索結果 ["+kw+"] (直近 "+beferd+" 日)",c);
        }
        //新しく情報を作り始める
        c = "";
        c = c + "本メールは指定キーワード(" + tmpKW + ")が2chスレッドタイトルに含まれるもののうち、" + "\n"
        c = c + "直近 " + beferd + " 日以内の書き込みだけメール出力しています。" + "\n"
//        c = c +"\n■■■■■■■■■■■■■■■■■■■■■■■\n";
//        c = c + "検索キーワード:" + tmpKW + "\n";
//        c = c +"■■■■■■■■■■■■■■■■■■■■■■■\n";
        kw = tmpKW;
      }
      var tmpTT = aryResults[ii][1];//スレッドタイトル
      if(tt!=tmpTT){
        //スレッドタイトルが変わったのでセット内容を変える
        c = c +"\n--------------------------------------------\n";
        c = c + "スレッドタイトル:" + tmpTT + "\n";//スレッドタイトル
        var u = aryResults[ii][5];//URL(ただし、個別番号含む)
        u = u.substr(0,u.lastIndexOf("/")) + '/';//後ろの/以降を削除する
        c = c + u + "\n";
        c = c +"--------------------------------------------\n";
        tt = tmpTT;
      }
      var formd = Utilities.formatDate(new Date(aryResults[ii][3]), 'GMT+9', 'yyyy/MM/dd HH:mm')
//      c = c + "【"+aryResults[ii][2]+"】" + "\n";//番号
//      c = c + aryResults[ii][3];//日時
//      c = c + formd + " ";//日時
      c = c + "("+aryResults[ii][2]+")";//番号
      c = c + aryResults[ii][4]+ "\n";//書込み内容
    }
    if(c!=""){
      //キーワードが変わったので今までの情報をメール送信する
      MailApp.sendEmail(TO_ADDRESS,"2ch検索結果["+kw+"](直近"+beferd+"日)",c);
    }

  }
  writelog(SHT_LOG_NAME, "メール送信終了");

  writelog(SHT_LOG_NAME, "プログラム終了");
}

function writelog(sheetName, logline){
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet_log = ss.getSheetByName(sheetName);
  sheet_log.insertRowBefore(1);
  sheet_log.getRange(1,1).setValue(new Date());
  sheet_log.getRange(1,2).setValue(logline);
}