🏤

UWSCRで疑似APIっぽく日本郵便の追跡ページをJSON形式で取得する

に公開

UWSCRを使って、日本郵便の追跡ページから配達状況および履歴情報を取得し、JSON形式で出力する処理を作成しました。

はじめに

従来のUWSCでは、JSONの取り扱いやHTMLのパース処理はほぼ不可能でした。
たとえばHTMLから特定の要素を取得するといった処理を行うには、正規表現でごり押しするか、外部ツールに頼る必要がありました。

しかし、UWSCRでは ParseHTML() によってHTMLをDOMとして解析することも可能になっています。
また、UObject を使ったオブジェクト記法が導入されており、JSON形式のデータを構造的に扱うことができます。

これにより、APIが用意されていないWebサービスからも、HTMLベースで必要な情報を安全に抽出し、構造化して扱えるようになりました。
本記事ではその一例として、日本郵便の追跡ページをパースしてAPIのような形でデータを扱う処理を紹介します。

処理の目的

  • 日本郵便の追跡ページにアクセスし、配達状況と履歴を自動で取得したい
  • 情報をJSON形式に整形して、後続処理や通知に使いたい
  • 公式APIが存在しないサービスを、UWSCRの機能で補完したい

処理の流れ

  1. webrequest() でHTMLを取得し、ParseHTML() に渡してDOM化します
  2. 「配達状況詳細」テーブルから、項目と値のペアを取得します
  3. 「履歴情報」テーブルから、複数行に分かれた配送履歴を抽出します
  4. 抽出結果をJSON構造にまとめて返します

使用例

shippingNumber = "123456789123"
print JapanPostTrackingAPI(shippingNumber)

function JapanPostTrackingAPI(shippingNumber)
    //JSONフォーマット
    json = @{
    "statusCode": "",
    "status": "",
    "message": "",
    "data":{
            "trackingNumber": "",
            "packageType": "",
            "services": "",
            "size": "",
            "deliveryDate": "",
            "timeSlot": "",
            "deliveryOffice": "",
            "multiPackageCount": "",
            "trackingHistory":[]}
            }@
    //trackingHistory内(履歴データ1件分)のフォーマット
    trackingJson = @{
            "date": null,
            "status": null,
            "detail": null,
            "office": null,
            "region": null,
            "zipcode": null
    }@

    //日本郵便のトラッキングページを取得し、HTMLをパース
    url     = "http://tracking.post.japanpost.jp/service/singleSearch.do?searchKind=S002&reqCodeNo1=" + shippingNumber + "&locale=jp"
    request = webrequest(url)
    root    = ParseHTML(request)

    //「配達状況詳細」テーブルの各項目(th)とその値(td)を取得して、対応するJSONキーにマッピング
    th = root.first('table[summary="配達状況詳細"]').find('th')
    td = root.first('table[summary="配達状況詳細"]').find('td')
    fieldNames = ["お問い合わせ番号", "商品種別", "付加サービス", "サイズ", "お届け指定日", "指定時間帯", "配達予定局", "複数個口数"]
    jsonKeys   = ["trackingNumber", "packageType", "services", "size", "deliveryDate", "timeSlot", "deliveryOffice", "multiPackageCount"]

    for i = 0 to length(th.outerHTML) - 1
        label = th[i].textContent
        for n = 0 to length(fieldNames) - 1
            if label = fieldNames[n] then
                json.data[jsonKeys[n]] = td[i].textContent
                break
            endif
        next
    next

    //「履歴情報」テーブルの各行から、配送履歴を抽出
    tr = root.first('table[summary="履歴情報"]').find('tr')
    th = root.first('table[summary="履歴情報"]').find('th')
    historyLabels = ["状態発生日", "配送履歴", "詳細", "取扱局", "県名等", "郵便番号"]
    historyKeys   = ["date", "status", "detail", "office", "region", "zipcode"]

    for i = 0 to length(tr.outerHTML) - 1 step 2
        //取得したいtrが2行に分かれているため強制的にマージ→パースしてコレクション化する
        td = ParseHTML('<table>' + tr[i].innerHTML + tr[i + 1].innerHTML + '</table>').find('td')
        if length(td.outerHTML) > 0 then
            for n = 0 to length(th.outerHTML) - 1
                label = th[n].textContent
                for m = 0 to length(historyLabels) - 1
                    if label = historyLabels[m] then
                        trackingJson[historyKeys[m]] = td[n].textContent
                        break
                    endif
                next
            next
        endif
        //履歴情報データの追加
        if trackingJson.date <> null then
            json.data.trackingHistory += trackingJson
        endif
        //履歴情報データJSONの初期化
        for key in historyKeys
            trackingJson[key] = null
        next
    next 

    //照会結果テーブルが存在する場合はエラーとみなして、ステータスとメッセージを設定
    if length(root.find('table[summary="照会結果"]').outerHTML) > 0 then
        json.statusCode = "400"
        json.status = "error"
        json.message = root.first('table[summary="照会結果"]').first("font").text
        json.data = null
    else
        json.statusCode = "200"
        json.status = "success"
        json.message = "お問い合わせ情報を取得しました"
    endif

    result = ToJson(json, True)
fend

検証環境:UWSCR(x64) Ver. 1.1.2

コードのポイント

JSON構造の初期化

返却用のJSONは、ステータス情報と配達データで構成されています。
履歴情報(trackingHistory)は配列であり、1件分のテンプレート(trackingJson)を使い回して追加していきます。

なお、テンプレートは参照型のため、都度 null 初期化を行わないと全件が同じ内容になる恐れがあります。

HTMLのパース処理について

UWSCRの ParseHTML() を使用すると、HTMLをDOMオブジェクトとして解析できます。
ただし、.findで得られるのは NodeCollection 型であり、length() がそのまま使えないため、ループ回数を取得するには .outerHTML など配列を返すプロパティを経由する必要があります。

length(th.outerHTML)

また、履歴情報の <tr> が2行に分かれているため、1件分の情報を取得するにはHTMLを結合してから再度 ParseHTML() に渡す必要があります。
UWSCRではHTML文字列の再構築と再パースが比較的簡単に行えるため、以下のような処理で対応しています。

td = ParseHTML('<table>' + tr[i].innerHTML + tr[i + 1].innerHTML + '</table>').find('td')

このように、一度文字列として <tr> 2行分を結合し、仮の <table> タグで囲んだ上で再度 ParseHTML() に渡すことで、
2行分を1セットの td 要素として取得できるようにしています。

UWSCRでは、こうした再パース処理が比較的容易に行えるため、複雑なテーブル構造にも対応しやすい特徴があります。

エラー判定の扱い

「お問い合わせ番号が見つかりません」や「情報が登録されていません」といったケースでは、通常の配達状況テーブルではなく、 別の「照会結果」テーブルが表示される構造になっています。

UWSCRではこのテーブルの存在を root.find('table[summary="照会結果"]') で検出し、 存在している場合は "statusCode": "400""status": "error" としてエラー扱いとしています。

また、エラーメッセージ自体も画面内の <font> 要素から取得し、"message" に格納しています。

所感

UWSCRはGUI自動化に強いツールですが、構文や機能の進化によりJSONやHTMLの構造的な扱いも可能になりました。
これにより、APIが用意されていないWebサービスに対しても、情報取得〜整形〜通知の一連の自動化を完結させることができます。

本記事で紹介した処理は、あくまで一例ですが、構造が安定しているページであれば同様の手法で応用が可能です。

おわりに

UWSCRでは、Webページから必要な情報を「画面を操作せずに取得する」ことが可能です。
従来のUWSCとは異なり、HTMLの要素抽出やJSON形式での整形など、スクリプト単体で完結できる場面が増えています。

GUI自動化以外にも、「外部サービスの情報を取得して処理する」といった用途にUWSCRを活用するきっかけになれば幸いです。

Discussion