質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

pythonの正規表現を使用して住所から都道府県などを抜き出したい。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 2,675

前提・実現したいこと

今私は、
pythonとSeleniumというモジュールを使用して、GoogleMapから自動的にビル情報を抜き出す
プログラムを作成しております。

今私が苦戦しております内容は、

'''
東京都渋谷区恵比寿西1丁目12−14エイビスビルB1
大阪府大阪市北区角田町5−1楽天地ビル
奈良県奈良市東向中町28 奈良近鉄ビル3F
福岡県福岡市中央区天神1丁目12−20
'''

GoogleMapから抜き出した上記のようなビルの住所から、

・都道府県
・市区町村
・町名
・番地
・ビル名

をPythonの正規表現を用いて抜き出すという作業です。Pythonの正規表現の使い方がまだ初心者レベルで、どうしてもわからないのでご協力いただけるとありがたいです。

ちなみに、
・「都道府県」は、「都道府県」の文字まで出力
・「市区町村」は、「奈良市東向中町」のように、「市」で区切らずに「区町村」で区切る
・「町名」は、「天神1丁目」のように、「区町村」の文字の後から「目」の文字まで
・「番地」は、「12−20」と「28」の2パターンがあります。
・正規表現で抽出した文字列は、住所の文から毎回消去してビル名は最後に残るので、「ビル名」の処理は必要ありません。

そして、抜き出したい要素が見つからない場合には、「null」で出力したいのですが、ここに関してはまだ実装内容を細かく考え切れていません。

プログラム全体のソースコード

### 質問サイト提出用のコード
## GoogleMapスクレイピング
from selenium import webdriver
import time
from bs4 import BeautifulSoup as BS
import re

# クロームのドライバーを用意
driver = webdriver.Chrome()

# クロームドライバーが「GoogleMap」urlを取得・開く
url = 'https://www.google.co.jp/maps/'
driver.get(url)

# time.sleepは、ロード中の処理でエラーが起こらないように、処理を待機させるために使用
time.sleep(3)

# 検索欄にキーワードを入力
keys = input('「郵便番号」の後にハイフンを入れて郵便番号を入力>')

# データ入力
building_search_id = driver.find_element_by_id("searchboxinput")
building_search_id.send_keys(keys) # サーチボックスに、keysにインプットされた要素を代入

time.sleep(3)

# クリック
search_button = driver.find_element_by_xpath("//*[@id='searchbox-searchbutton']")
search_button.click()

time.sleep(5)

# 「付近を検索」をクリック
near_search_button = driver.find_element_by_xpath("//*[@id='pane']/div/div[1]/div/div/div[5]/div[3]/div/button")
near_search_button.click()

time.sleep(5)

# 「付近のビル」と入力
building_search_id = driver.find_element_by_id("searchboxinput")
building_search_id.send_keys('付近のビル')

time.sleep(3)

# 「付近のビル」と入力し終わった後の検索蘭をクリック
search_button2 = driver.find_element_by_xpath("//*[@id='searchbox-searchbutton']")
search_button2.click()

time.sleep(6)

## リンクを順にクリックするループ処理
while True:
    try:
        ## リンク数解析
        link_list = []
        page_source = driver.page_source # htmk解析
        soup = BS(page_source, 'html.parser')
        link_text = soup.find(class_ = 'n7lv7yjyC35__left') # スクレイピング
        link_list.append(link_text.text.strip())

        # スクレイピングした文字を一個一個分解する処理(n_gramを使用)
        # 下のfor文をdefの中に収納する可能性あり
        def n_gram(target, n):
            result = []
            for i in range(0, len(target) - n + 1):
                result.append(target[i : i + n])
            return result

        for i in link_list:
            target = i

        result = n_gram(target, 1) # 1文字ごとに分解してリストに収納したリスト

        if result[0] == '1':
            link1 = int(result[0])
            link2_s = result[2:4]
            link2 = int("".join(link2_s))
        elif result[2] == '〜':
            link1_s = result[0:2]
            link1 = int("".join(link1_s))
            link2_s = result[3:5]
            link2 = int("".join(link2_s))
        else:
            pass

        ## スクレイピングに使用する空のリストをここに設置
        title_list = [] # スクレイピング要素がないリンクを飛ばすためのリスト
        address_list = []

        # 検索結果のxpathというものでループを回しています
        path_front = '//*[@id="pane"]/div/div[1]/div/div/div[4]/div[1]/div['
        count = 1
        path_end = ']'

        roop_counter = 1 # ループを強制終了させるためのカウンター

        while roop_counter <= link2 - link1 + 1: # リンクを順にクリックさせています
            path = path_front + str(count) + path_end
            l = driver.find_element_by_xpath(path)
            l.click()
            time.sleep(5)

            if count == 1 or 3: # HTML解析
                page_source = driver.page_source
                soup = BS(page_source, 'html.parser')

            # 例外ページをスキップ
            title = soup.find(class_ = 'GLOBAL__gm2-headline-5 section-hero-header-title-title')
            title_list.append(title.text.strip())
            title_list_del = title_list.pop()
            # 内部の例外処理
            if '-' not in title_list_del:
                pass
            else:
                count += 2
                roop_counter += 1
                back_button = driver.find_element_by_xpath('//*[@id="pane"]/div/div[1]/div/div/button/span')
                back_button.click() # 検索一覧に戻ります
                time.sleep(4)
                continue

            # スクレイピングの準備
            address = soup.find_all(class_ = 'section-info-text')

            # 郵便番号をスクレイピングする処理
            target = address[0].text.strip()
            address_result_old = n_gram(target, 1) # 分解した後のリスト
            address_result = [i for i in address_result_old if i != ' '] 
            postal_code_list = address_result[0:9]
            postal_code = "".join(postal_code_list) # 郵便番号が完成

            ## 正規表現を使用した住所解析
            scraping_list = address_result[9:] # 一文字ずつ分けた、正規表現用のリスト
            scraping_str = "".join(scraping_list) # 正規表現用のリストを文字列に

            ###############ここから質問箇所です######################
            # 「県」の抽出
            Ken_match = re.match('', scraping_str) # matchの部分は適当に変えてください
            Ken = Ken_match.group()
            scraping_list = [i for i in scraping_list if i == Ken]

            # 「市区町村」の抽出
            City_match = re.match('', scraping_str) # matchの部分は適当に変えてください
            City = City_match.group()
            scraping_list = [i for i in scraping_list if i == City]

            # 「町名」の抽出
            Town_match  = re.match('', scraping_str) # matchの部分は適当に変えください
            Town = Town_match.group()
            scraping_list = [i for i in scraping_list if i == Town]

            # 「番地」の抽出
            num_match = re.match('', scraping_str) # matchの部分は適当に変えてください
            num = num_match.group()
            scraping_list = [i for i in scraping_list if i == num]
            #################ここまでが質問箇所です#####################

            # 「ビル名」の抽出
            ## リストの残りを出力すれば完了

            # 出力
            print('--------------------------')
            print(scraping_str) # 正規表現の材料、あとで消す
            print(title.text.strip()) # タイトルの出力 最後は使わないかも
            print(postal_code) # 郵便番号の出力
            print(Ken) # 「県名」の出力
            print(City) # 「市区町村」の出力
            print(Town) # 「町名」の出力
            print(num) # 「番地」の出力

            back_button = driver.find_element_by_xpath('//*[@id="pane"]/div/div[1]/div/div/button/span')
            back_button.click() # 検索一覧に戻ります
            count += 2
            roop_counter += 1
            time.sleep(5)

        # 次のページへ移動します
        next_button = driver.find_element_by_xpath('//*[@id="n7lv7yjyC35__section-pagination-button-next"]')
        next_button.click()
        time.sleep(7)

    except:
        driver.close() # 最後にブラウザを閉じて終了

質問したい部分のソースコード

            # スクレイピングの準備
            address = soup.find_all(class_ = 'section-info-text') 

            # 郵便番号をスクレイピングする処理
            target = address[0].text.strip()
            address_result_old = n_gram(target, 1) # 分解した後のリスト
            address_result = [i for i in address_result_old if i != ' '] # 空白消す
            postal_code_list = address_result[0:9]
            postal_code = "".join(postal_code_list) # 郵便番号が完成

            ## 正規表現を使用した住所解析
            scraping_list = address_result[9:] # 一文字ずつ分けた、正規表現用のリスト

            scraping_str = "".join(scraping_list) # 正規表現用のリストを文字列に
            # 例(渋谷区恵比寿西1丁目12−14エイビスビルB1)

            ###############ここから質問箇所です######################
            # 「県」の抽出
            Ken_match = re.match('', scraping_str) # matchの部分は適当に変えてください
            Ken = Ken_match.group()
            scraping_list = [i for i in scraping_list if i == Ken]# マッチを消去

            # 「市区町村」の抽出
            City_match = re.match('', scraping_str) # matchの部分は適当に変えてください
            City = City_match.group()
            scraping_list = [i for i in scraping_list if i == City]

            # 「町名」の抽出
            Town_match  = re.match('', scraping_str) # matchの部分は適当に変えください
            Town = Town_match.group()
            scraping_list = [i for i in scraping_list if i == Town]

            # 「番地」の抽出
            num_match = re.match('', scraping_str) # matchの部分は適当に変えてください
            num = num_match.group()
            scraping_list = [i for i in scraping_list if i == num]
            #################ここまでが質問箇所です#####################

補足情報

Ken_match = re.match('', scraping_str)の、''の部分正規表現を考えていただきたいです。
re.matchの、matchは適宜適切なものに変えてください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+4

都道府県を分離、程度ならどうにかなりますが、その他に関してはあきらめたほうがよろしいかと思われます
程度問題となりますが、全く間違いなく分離、というのはムリかと。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/09 09:36

    やはりそうですよね。
    都道府県に関しては、精度を完璧にすることはできますが、他は完璧には難しそうですね。

    ご回答ありがとうございます。

    キャンセル

checkベストアンサー

+1

都道府県・市区町村(自治体で切る)ならリストを用意するなりイレギュラーなところだけピックアップして正規表現にかけるなりで対応できますが(参考)、それ以降の住所については「町名+番地」だけでなく、

  • 市町村の次がいきなり番地(長野県木曽郡王滝村3623番地 王滝村役場)
  • 無番地(東京都青ヶ島村無番地 青ヶ島村役場)
  • 京都市内の通り名住所(京都市中京区寺町通御池上る上本能寺前町488 京都市役所)
  • 国有林内の林班(和歌山県伊都郡高野町大字高野山国有林第9林班ノは 高野山駅)
  • 岩手県の地割(岩手県紫波郡矢巾町第13地割123 矢巾町役場)
  • 北海道の条丁目・線号(札幌市中央区北1条西2丁目 札幌市役所)
  • 軽井沢の別荘番号など、公式の住所が役に立たないので独自に振っているケース
  • 道路式住居表示(東根市神町営団大通り47号 東根市営団公民館)

などなど、場所によって異なると言っても過言ではありません。パターン化するには、並大抵でない労力が必要となります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/09 16:31

    わざわざ例外や参考サイトまで記載してくださり、本当にありがとうございます。
    maisumakun様の回答を見て、完璧なものではなく、ある程度の精度のものを作ることにいたしました。

    本当にありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る