Railsで日本郵便の追跡状況を取得してみる

こんにちは!kossyです!




さて、今回はnet::httpとnokogiriを使って日本郵便の追跡情報を取得してみたので、ブログに残してみたいと思います。




コード説明


lib/japan_post_client.rb

# 使うライブラリをrequireしておく
require 'net/https'
require 'nokogiri'


class JapanPostClient

# JapanPostClient.instance.get_details(request_number) みたいに呼び出すことを想定

  # インスタンスが作成された時に呼び出す
  def initialize
    # 日本郵便の追跡情報のURLをparseする
    @uri = URI.parse 'http://tracking.post.japanpost.jp/services/sp/srv/search/'
    # Net::HTTPのインスタンスにURLのホストとポートを渡したものを作成
    @http = Net::HTTP.new @uri.host, @uri.port
  end

  # 追跡情報を取得するためのメソッド、引数には追跡番号を渡す
  def get_details(request_number)
    # 追跡ページに向けたGetリクエストを作成
    req = Net::HTTP::Get.new @uri.path + "?requestNo1=#{request_number}&search=開始&locale=ja"
    # Getリクエストを叩く
    res = @http.request req
    # 通信結果をNokogiriでparseしたものを変数に格納
    doc = Nokogiri::HTML.parse res.body

    # ここからはスーパー力技です、、、マサカリ待ってます

    # 日付を取得する
    dates = doc.xpath('//dl[@class="tracking_form"]/dd[position()!=0]').map { |el| el.text.match(/[0-9]?[0-9][0-9]?[0-9]日\ \d\d:\d\d/) }

    # 日付が一つでもあればmapする
    if dates.present?
      i = 0
      details = dates.map do |date|
        i += 1

        _date = date.to_s.scan(/[0-9]?[0-9][0-9]?[0-9]/).join
        time = date.to_s.scan(/\d\d:\d\d/).join
        status = doc.xpath("//dl[@class='tracking_form']/dt[position()=#{i}]").first.text.presence
        place = doc.xpath("//dl[@class='tracking_form']/dd[position()=#{i}]").first.text.presence&.gsub(" ", "")&.scan(/.*郵便局/).first || doc.xpath("//dl[@class='tracking_form']/dd[position()=#{i}]").first.text.presence&.scan(/[A-Z]*/).join.gsub("", "")

        if _date.present? && time.present?
          datetime = Time.parse("#{Date.today.year}/#{_date} #{time}")
          if datetime > Time.now
            datetime = datetime.ago(1.year)
          end
        end

        { datetime: datetime, description: status, detail: place }
      end

    else
      details = [{}, {datetime: nil, description: '伝票番号誤り', detail: nil}]
    end

    return {status: details[-1][:description], details: details}
  end
end

get_detailsの返り値はこんな感じになるかと思います。

{:status_str=>"お届け済み",
 :infos=>
  [{:datetime=>2019-09-25 13:00:00 +0900,
    :description=>"引受",
    :detail=>"新宿郵便局"},
   {:datetime=>2019-09-26 19:00:00 +0900,
    :description=>"国際交換局に到着",
    :detail=>"東京国際郵便局"},
   {:datetime=>2019-09-26 18:00:00 +0900,
    :description=>"国際交換局から発送",
    :detail=>"東京国際郵便局"},
   {:datetime=>2019-09-28 10:00:00 +0900,
    :description=>"国際交換局に到着",
    :detail=>"CHINA"},
   {:datetime=>2019-09-30 09:00:00 +0900,
    :description=>"国際交換局から発送",
    :detail=>"CHINA"},
   {:datetime=>2019-09-30 14:00:00 +0900,
    :description=>"お届け済み",
    :detail=>"CHINA"}]}

この返り値をDBに保存するなりHTMLに書き出すなり、
あとは好きに加工してみてください。