郵便番号一括追跡プログラムをGoogleAppsScriptで一から作った全行程まとめ(前編)

スポンサーリンク

序章

なんで作ろうと思ったの?

ちょっと仕事で郵送されたものについてちゃんと届いているか知る必要がでてきました。
でも、伝票番号の検索って基本的に一個一個やっていたので数百件や数千件って規模になると億劫になりますよね?
そこでどんな方法があるのか?とやるんだったら自分で作ってみたいよね!ってことでリサーチすることに。

リサーチしよう!

ネットで色々調べたところ以下のようなことがわかった

  • シェアウェアのツールがあるっぽい(エクセルベース
  • 色々書いてあるけど情報が断片的
  • シェアウェアのソフト

素晴らしくよくできていて、希望通りの機能を満たすソフトだった。
これを使えばよいのだが、如何せんシェアなので制限があった・・・・
検索数の制限と使用回数の制限だった・・・・

「これでは、数千件の案件に対応するためには、有料版を買うしか・・・・」と思いましたが良い機会なので自分でこのシェアツールを参考にして作ってみることにしました。

第1章「どうやって作ろうかな?」

最初に考えたことは、「どうやって作ろうかな?」です。

プログラム経験は、まだそんなに無いですので得意な言語で超大作を・・・というわけにはいきません。
多分、エクセルやアクセスをつかったVBAかそれに近いもので作るのがいいのかなぁといった具合です。

次に、簡単な設計を考えました。

/*簡易設計図/

unnamed

イメージとしては、検索する対象の文字列を取得して問い合わせた上で結果をゲットしてもとのシートに入力するかんじです。

要件は以下のようになります(普通は逆だと思いますが)

Aランク:実現したい
  1. 伝票番号を検索して追跡結果を取得&表示できる
  2. 一括で処理が可能である(最低でも数千件には対応できる)
Bランク:できれば実現したい
  1. パソコンがついている必要がない
Cランク:好み
  1. WEBで状況などが確認できる

最後に開発環境について考えました。

VBAとGoogleAppsScriptで悩みました。

シェアのツールは、エクセルベースなのでVBAアプリケーションでした。とっちでつくろうかなぁ〜と思っておりましたがBランクの条件の「パソコンがついている必要がない」を考えた場合、サーバサイドであるGoogleAppsScriptのほうが良いという結論がでたので、今回はこっちで作ってみることにしました!

第2章「簡単なものから作ってみる」

基本的にネットで「これを改造すれば動くかも?」というソースを見つけてきて改造します。

まず、伝票番号追跡について調査します。

伝票番号追跡について

前もって知っていたこと

  1. 日本郵便のサイトで追跡できる
  2. 基本的に一件ずつ検索する(連番とかもある)

調べて分かったこと

  1. APIで追跡詳細のアドレスを調べてくれるサイトがある
  2. JSONやXMLなどの形で返してくれるものはない
  3. 追跡詳細のページは、一定の規則でアドレスが振られている(重要)

なんとなく伝票追跡についてわかってきました。
最初は、JSONなどの形で返してくれる本家の機能があれば、それを利用して構築すればよいと思ってました。
東電の電気利用率などが有名かな?
公開されているデータを取得してゴニョゴニョするのが楽だと思います。
しかし、郵便番号の場合はこれがなかった・・・・

しかたないので、別の方法を模索していきます。

また色々ネットで検索して、「これでいってみようかな?」と思ったのがこれです。

伝票番号詳細のページに接続して、HTMLを返却させそのHTMLの中から必要な情報を抜き出す。

色々調べて、参考にしたソースがこちら

<引用@http://qiita.com/kazuhine/items/23248b7290d8140f53fc>

function myFunction() {
  var response = UrlFetchApp.fetch("http://qiita.com");

  var myRegexp = /<title>([\s\S]*?)<\/title>/i;
  var match = myRegexp.exec(response.getContentText());
  var title = match[1];

  title = title.replace(/(^\s+)|(\s+$)/g, "");
  Logger.log(title);
}

</引用>

どうやら「WEBスクレイピング」という技術らしいです。
これを参考にして、伝票番号から、追跡結果を取得するプログラムを作成します。

まず参考にするソースを理解することに努めます。

概要 対象のWEBサイトのHTML(テキスト)を正規表現でフィルタリングしてほしい情報をゲットする!

ソースを理解する

var response = UrlFetchApp.fetch("http://qiita.com");

この部分は、UrlFetchApp.fetch(“サイトのアドレス”)で対象のHTMLを返却する関数。
「UrlFetchApp.fetch」自体がGASの関数なので検索すると確度の高い情報に出会える。

var myRegexp = /<title>([\s\S]*?)<\/title>/i;

この部分は、正規表現。これが曲者。難しかった。
基本的な検索は「javascript 正規表現」で情報がでてくる。(GASはJS準拠なので)
調べながら参考ソースの正規表現が何を使っているのかを理解しました。

var match = myRegexp.exec(response.getContentText());

この部分は、実際にHTMLテキストに対して上の正規表現でフィルタリングした結果を代入してるだけ。
かとおもいきや、execの他にもmatchがあって使い分けが・・・となって迷った。

title = title.replace(/(^\s+)|(\s+$)/g, "");

無駄な空白とかを詰めてる

ふむふむ・・・・こんな感じのソースになっているのか・・・・

次は、自分のやりたいことに摺合せていきます。

ネットで検索して得た情報として、「追跡結果のURLは一定のルールで生成されている」ことがわかってます。
これを利用して、今回スクレイピングで情報をゲットしようと試みました。

追跡結果のページは、


http://tracking.post.japanpost.jp/service/singleSearch.do?searchKind=S003&locale=ja&SVID=023&reqCodeNo1="伝票番号”

となっているようです。
これをUrlFetchApp.fetchでゲットします。

次に、追跡結果のページのソースを見て「どの部分が必要で、どうやって正規表現で引っ掛けるか?」を考えます。
今回必要な部分は、「届いたか届いてないか?いつ届いたか?」だったのでその部分を見ていきます。

特定できないと思うので、例をそのまま掲載しますが、以下の部分です。


<tr>

<td rowspan="2" class="w_120">2015/07/02 03:39</td>

<td rowspan="2" class="w_150">通過</td>

<td rowspan="2" class="w_180"></td>

<td class="w_105">松戸南郵便局</td>

<td rowspan="2" class="w_105">千葉県</td>

                  </tr>

<tr>

<td class="w_105">270-2299</td>

                  </tr>

<tr>

<td rowspan="2" class="w_120">2015/07/02 04:51</td>

<td rowspan="2" class="w_150">到着</td>

<td rowspan="2" class="w_180"></td>

<td class="w_105">船橋郵便局</td>

<td rowspan="2" class="w_105">千葉県</td>

                  </tr>

<tr>

<td class="w_105">273-8799</td>

                  </tr>

<tr>

<td rowspan="2" class="w_120">2015/07/02 10:35</td>

<td rowspan="2" class="w_150">お届け先にお届け済み</td>

<td rowspan="2" class="w_180"></td>

<td class="w_105">船橋郵便局</td>

<td rowspan="2" class="w_105">千葉県</td>

                  </tr>

<tr>

<td class="w_105">273-8799</td>

                  </tr>

            </table>

この部分の、最終的に届いた「お届け先にお届け済み」というステータスとその上の時間が必要な情報となります。


<tr>

<td rowspan="2" class="w_120">2015/07/02 10:35</td>

<td rowspan="2" class="w_150">お届け先にお届け済み</td>

<td rowspan="2" class="w_180"></td>

<td class="w_105">船橋郵便局</td>

<td rowspan="2" class="w_105">千葉県</td>

</tr>

<tr>

<td class="w_105">273-8799</td>

</tr>

↑この部分です。
HTMLソースを眺めると、この部分は形式も固定されているようです。(伝票番号が変わっても)
でも、見てわかるとおり複数回出てきます。
ですので、複数回ヒットしながら最後の一つを返すような仕組みを作ればよいと思いました。

ヒットするような正規表現を詰めていきます。
何事も小さくスタートです。
手始めに、 <td class=”w_105″>松戸南郵便局</td>を正規表現にすると、これだけヒットします。
「松戸南郵便局」の部分が変わってもこの条件をヒットするように「(.*)」としました。

<td class=”w_105″>(.*)</td>で正規表現すると


<td class="w_105">松戸南郵便局</td>

※1回目

<td class="w_105">船橋郵便局</td>

※2回目

とちゃんと2か所ヒットします。ちなみに「.*」だけでも該当行はヒットしますが最終的にタグの部分ではなく「松戸南郵便局」などの
中身だけ欲しいので中身だけ配列に格納するように「()」を追加してます。

こんな感じで、じわじわとヒットする範囲を広げていきました。

その結果、ちょっと長いですが以下の正規表現で欲しい場所をヒットすることができました。

/
<td rowspan="2" class="w_120">(.*)<\/td>\s*
<td rowspan="2" class="w_150">(.*)<\/td>\s*
<td rowspan="2" class="w_180"><\/td>\s*
<td class="w_105">(.*)<\/td>\s*
<td rowspan="2" class="w_105">(.*)<\/td>\s*<\/tr>\s*
<tr>\s*
<td class="w_105">(...-....)<\/td>/g

情報が必要な場所には「()」を付けて配列で情報をゲットしてます。

次は、「複数回ヒットするけど、最後のひとつを抜く」ことをかんがえました。
こっちについては、あんまり捻りはないですが「ループにかけて最後を確認する」という方法でやりました。

.execを使って正規表現する場合、リファレンスを見るに一回一回正規表現で検索しているようで複数該当するものがあった場合は、「最初にヒットするやつ→次にヒットするやつ」といった具合に変化していきます。
そして、順番に検索していって検索するものが無くなった状態になったら「null」を返す仕様のようです。

ですので、順番でループしていってnullを返す一個手前のものが「もとめている情報」となると!

これを関数にまとめて、引数として「伝票番号」返り値として「求めている情報」で実際に実装したのがこれです↓

/***
 *機能:伝票番号から検索した詳細情報から最終の情報を配列で返す
 *引数:TRACKINGID(伝票番号)
 *返り値:最終詳細の「日付」「ステータス」「郵便局」「都道府県」「郵便番号」
 ***/
function CallTrackingDetails(TRACKINGID) {

  //取得先のURLからHTMLと検索条件となる正規表現を定義
  var response = UrlFetchApp.fetch("http://tracking.post.japanpost.jp/service/singleSearch.do?searchKind=S003&locale=ja&SVID=023&reqCodeNo1=" + TRACKINGID);
  var myRegexp = /
<td rowspan="2" class="w_120">(.*)<\/td>\s*
<td rowspan="2" class="w_150">(.*)<\/td>\s*
<td rowspan="2" class="w_180"><\/td>\s*
<td class="w_105">(.*)<\/td>\s*
<td rowspan="2" class="w_105">(.*)<\/td>\s*<\/tr>\s*
<tr>\s*
<td class="w_105">(...-....)<\/td>/g;

  var TrackingDetails = new Array(); //詳細情報の集まり
  var detail;//詳細情報
  var flag = true;//ループフラグ nullだとfalse
  var i = 0;//ループカウンタ

  while(flag)
  {
    detail = myRegexp.exec(response.getContentText());

    if(detail == null){
      flag = false;
      }
    else{
      TrackingDetails[i] = [detail[1],detail[2],detail[3],detail[4],detail[5]];
      i++;
    }
  }

  var lastTrackingDetail;//最後の詳細情報
  lastTrackingDetail = TrackingDetails[i-1];
  //Logger.log(lastTrackingDetail);

  return lastTrackingDetail;

}

この関数を利用することによって、伝票番号を入力すると「最後の追跡情報」が返ってくる仕組みができました。

後編へ続く

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

コメント

  1. […] 郵便番号一括追跡プログラムをGoogleAppsScriptで一から作った全行程まとめ(前&# […]

  2. やまだ より:

    ちょっと待った!例がおかしいwww
    五香からくぬぎ山のどこかの人ですか

    • TK より:

      すいませんw
      バレないと思いましたが、近くに住んでらっしゃる方には、かなりピンポイントの地名だったようですね・・・
      ちょっと、関東からの発送でして・・・
      反省します。

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>