9
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

郵便番号から住所を調べる

Last updated at Posted at 2016-12-06

今回はRubyで郵便番号から住所の都道府県、市、町名などを調べるスクリプトとRailsアプリを作成してみました。

概要

  1. データを手に入れる
  2. データを読み込む
  3. 住所を調べる
  4. Railsアプリに追加してみる
  5. まとめ

データを手に入れる

日本郵便のサイトで郵便番号のデータは無料でダウンロードできます

「読み仮名データの促音・拗音を小書きで表記するもの 全国一括」というファイルが必要なので、以下のリンクでダウンロードして展開してください。

http://www.post.japanpost.jp/zipcode/dl/kogaki/zip/ken_all.zip

データを読み込む

展開した「KEN_ALL.csv]は以下の形式です。3つ目の欄は郵便番号、7つ目は都道府県、8つ目は市など、9つ目は町名などです。それ以外は使いませんが、興味があれば日本郵便のサイトに説明があります

KEN_ALL.CSV
01101,"060  ","0600000","ホッカイドウ", "サッポロシチュウオウク", "イカニケイサイガナイバアイ", "北海道", "札幌市中央区", "以下に掲載がない場合",0,0,0,0,0,0
01101,"064  ","0640941","ホッカイドウ", "サッポロシチュウオウク", "アサヒガオカ", "北海道", "札幌市中央区", "旭ケ丘",0,0,1,0,0,0
01101,"060  ","0600041","ホッカイドウ", "サッポロシチュウオウク", "オオドオリヒガシ", "北海道", "札幌市中央区", "大通東 ",0,0,1,0,0,0
01101,"060  ","0600042","ホッカイドウ", "サッポロシチュウオウク", "オオドオリニシ(1-19チョウメ)", "北海道", "札幌市中央区", "大通西(1〜19丁目)",1,0,1,0,0,0
...

ファイルのエンコーディングはShift_JISなので、エディタはサポートしていない場合、文字化けになるかもしれません。

まず、新しいRubyのスクリプトを作成して、データを読み込みます。

post_code_index.rb
# encoding: utf-8
require 'csv'

class PostCodeIndex
  def initialize(file_name)
    @data = {}
 
    CSV.foreach(file, encoding: 'Shift_JIS:UTF-8') do |row|
      post_code, city, prefecture, street = row[2], row[6], row[7], row[8]
      @data[post_code] = {
        post_code: post_code,
        prefecture: prefecture,
        city: city,
        street: clean_street(street)
      }
    end
  end
  
  private
  
  def clean_street(input)
    return '' if input == '以下に掲載がない場合'
    input.sub(/(.*$/, '')
  end
end

RubyのCSVライブラリーはファイルの内容を読み込んで各行を配列にします。そして、encoding: 'Shift_JIS:UTF-8'」を与えると、データのエンコードイングはShift_JISからUTF-8になります。

CSVのブロックで使う欄をハッシュに与えます。郵便番号をキーにして作ったハッシュを@dataのハッシュに与えます。

郵便番号によっては町名がなくて「以下に掲載がない場合」が書いてある場合や、町名があっても「(1-19チョウメ)」のような括弧が付いている場合もあります。このような場合はユーザーに見せたくないテキストを消して町名を正規化するために、clean_streetのメソッドを作りました。

住所を調べる

読み込んだデータを使うために2つのメソッドを追加します。String#trについてわからない方はこちら

post_code_index.rb
# encoding: utf-8
require 'csv'

class PostCodeIndex
  # ...

  def lookup(post_code)
    @data[clean_post_code(post_code)]
  end
  
  private

  # ...

  def clean_post_code(input)
    input.to_s.tr('0-9', '0-9').gsub(/[^0-9]/, '')
  end
end

これで以下のように郵便番号から住所の都道府県、市、町名が調べる事ができます。

index = PostCodeIndex.new('KEN_ALL.csv')

# 記号があってもなくても一緒
index.lookup('140-0002') # => {:post_code=>"1400002", :prefecture=>"品川区", :city=>"東京都", :street=>"東品川"}

# 文字列値でも整数値でも構わない
index.lookup(4180112) # => {:post_code=>"4180112", :prefecture=>"富士宮市", :city=>"静岡県", :street=>"北山"}

# 半角と全角はどっちでも大丈夫
index.lookup('108ー0075') # => {:post_code=>"1080075", :prefecture=>"港区", :city=>"東京都", :street=>"港南"}

# もし与えられた郵便番号がデータにない場合、nilを戻す
index.lookup('000-0000') # => nil

もしユーザー入力中に郵便番号や住所の予測変換表示したい場合、もう一つのメソッドが必要です。

post_code_index.rb
  def suggest(input, top = 5)
    partial_code = clean_post_code(input)

    @data.select { |post_code| post_code.start_with?(partial_code) }.values.take(top)
  end

これで、郵便番号の一部を与えると、予測変換された住所が戻ります。

index = PostCodeIndex.new('KEN_ALL.csv')

index.suggest('140-0')
# => [
#   {:post_code=>"1400000", :prefecture=>"品川区", :city=>"東京都", :street=>""},
#   {:post_code=>"1400014", :prefecture=>"品川区", :city=>"東京都", :street=>"大井"},
#   {:post_code=>"1400012", :prefecture=>"品川区", :city=>"東京都", :street=>"勝島"},
#   {:post_code=>"1400001", :prefecture=>"品川区", :city=>"東京都", :street=>"北品川"},
#   {:post_code=>"1400015", :prefecture=>"品川区", :city=>"東京都", :street=>"西大井"}
# ]

# 提案はいくつほしいか指定できる
index.suggest('10800', 3)
# => [
#   {:post_code=>"1080022", :prefecture=>"港区", :city=>"東京都", :street=>"海岸"},
#   {:post_code=>"1080075", :prefecture=>"港区", :city=>"東京都", :street=>"港南"},
#   {:post_code=>"1080014", :prefecture=>"港区", :city=>"東京都", :street=>"芝"}
# ]

index.suggest('000-0000') # => []

Railsアプリに追加してみる

以上のコードはどんなアプリやフレームワークでも使えるけれども、Railsアプリに追加してみましょう。

app/services/post_code_index.rb
# encoding: utf-8

require 'csv'
require 'singleton'

class PostCodeIndex
  include Singleton

  def initialize
    file = File.join File.dirname(__FILE__), '..', '..', 'config', 'KEN_ALL.csv'
    # ...
  end
  # ...
end

私は風通にデータベース以外、JSON APIなどからデータを読み込むクラスをservicesに入れます。そして、このクラスを構築するのは結構時間かかるので、一回だけ構築するようにSingletonにしました。

config/initializers/post_code_index.rb
Rails.logger.info 'Initializing post code index...'
PostCodeIndex.instance

PostCodeIndexが最初に使われるとき、時間かからる構築しないように、initializerを追加しました。こうすると、アプリ起動するとき、構築されます。

app/controllers/example_controller.rb
class ExampleController < ActionController::Base
  layout 'application'

  def index
  end

  def lookup_address
    address = PostCodeIndex.instance.lookup(params[:post_code])
    render json: { data: address }
  end
end
config/routes.rb
Rails.application.routes.draw do
  get '/example', to: "example#index"
  get '/example/lookup_address', to: 'example#lookup_address'
end

lookup_addressはJSONを戻すので、AJAXで住所のデータが読み込めます。

app/views/example/index.html.erb
<script>
 $(document).ready(function () {
   $("#example_zip_code").change(function () {
     $.ajax({
       url: "/example/lookup_address",
       json: true,
       data: { post_code: $(this).val() }
     }).done(function (response) {
       $("#example_prefecture").val(response.data.prefecture);
       $("#example_city").val(response.data.city);
       $("#example_street").val(response.data.street);
     });
   });
 });
</script>

<%= form_for :example do |f| %>
  <%= f.text_field :zip_code %>
  <%= f.text_field :prefecture %>
  <%= f.text_field :city %>
  <%= f.text_field :street %>
<% end %>

追加した/example/lookup_addressのJSON APIはどんなJavaScriptのライブラリーやフレームワークでも使えるけれども、jQueryがRailsと付いているのでこの例はjQueryを使います。

lookup.gif

簡単ですが、完成です。

まとめ

他のウェブサイトでよくこの機能を見た事がありますが、思った以上に簡単に作れました。もちろん、郵便番号から住所を調べるライブリやJSON APIなどがあります。例えば、ajaxzip3。でも、自分で作くるのは一番柔軟で、自分のプロジェクトの条件と完全に合わせる事ができます。

9
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?