Webスクレイピングで営業リストを作成する(1)

2021年2月4日

動画はこちら

あるサイトから、企業・店舗などの情報を収集して、営業リストを作成します。一般にWebスクレイピングと呼ばれる方法です。
実際の個人や企業データを表示するのは問題があるので、ケーススタディとして、全国の裁判所情報を収集することにします。
各地の裁判所の所在地・電話番号等一覧

作業手順

  1. サイト利用規約の確認
  2. 操作手順、取得項目の確定
  3. XPath取得
  4. スクリプト作成
  5. デバッグ、エラー対応
  6. データ補正、スクリプトの改良
  7. 評価、考察

利用規約の確認

対象となるサイトが

  • スクレイピング(ロボットによる自動取得)禁止でないこと
  • 情報の転用を禁止していないこと

を確認します。

Webスクレイピングを禁止していない場合でも、あまりに高速かつ集中的に大量データを収集すると、サーバーに負荷がかかり、相手方に迷惑がかかります。サーバーの過負荷で、サービスの遅延や停止が発生すると、最悪の場合、訴えられないとも限りませんので、常識的な範囲で利用するようにします。(人間の速度の数倍程度なら問題ないでしょう)

規約の確認

このサイトについて | 裁判所

スクレイピングやクローラーによる自動取得を禁止していません。
掲載情報についても、著作権の範囲内で利用とのことです。

操作手順、取得項目

リンクをたどっていくと次のようになります。
A.全国の裁判所一覧 ⇒ B.地域の裁判所一覧 ⇒ C.住所・電話番号

A.全国画面

札幌高等裁判所内の所在地・電話番号等一覧
高等裁判所管内の所在地・電話番号等一覧

操作内容

各欄のリンク「所在地・代表電話」をクリックします。

B.地域画面

管内の裁判所所在地一覧
管内の裁判所所在地一覧

操作内容

各欄の左側のリンクをクリックします。

C.詳細画面

函館地方・家庭・簡易裁判所の所在地
裁判所の所在地

操作内容(取得項目)

裁判所名、郵便番号、所在地、電話番号を取得します。
上記の例では郵便番号が3個、所在地にアクセス方法が含まれていますが、いったんこのままで取得します。

XPath取得

各画面のXPathをXPath取得ツールで取得します。

A.全国画面

XPath取得(画面A)

取得されたXPath

札幌高等裁判所管内
札幌高等裁判所 所在地・代表電話 //div[4]/dl[1]//a[1]
札幌地方裁判所/札幌地裁管内の簡易裁判所 所在地・代表電話 //div[4]/dl[2]//a[1]
札幌家庭裁判所 所在地・代表電話 //div[4]/dl[3]//a[1]
函館地方裁判所/函館地裁管内の簡易裁判所 所在地・代表電話 //div[4]/dl[4]//a[1]
函館家庭裁判所 所在地・代表電話 //div[4]/dl[5]//a[1]
旭川地方裁判所/旭川地裁管内の簡易裁判所 所在地・代表電話 //div[4]/dl[6]//a[1]
旭川家庭裁判所 所在地・代表電話 //div[4]/dl[7]//a[1]
釧路地方裁判所/釧路地裁管内の簡易裁判所 所在地・代表電話 //div[4]/dl[8]//a[1]
釧路家庭裁判所 所在地・代表電話 //div[4]/dl[9]//a[1]
仙台高等裁判所管内
仙台高等裁判所 所在地・代表電話 //div[7]/dl[1]//a[1]
仙台地方裁判所/宮城県内の簡易裁判所 所在地・代表電話 //div[7]/dl[2]//a[1]

表は、TABLEタグではなく、divとdlタグで記述されているようです。
そして各表は、札幌div[4]、仙台div[7]、東京div[10]のように、3つ飛ばしで記述されています。

しかし、これですとプログラムで扱う時に、表ごとの変数(dvの序数)と、各裁判所ごとの変数(dlの序数)が必要になり、不便です。そもそも、出力リストは高等裁判所の管区ごとに分ける必要はありません。

ということで、表構造を無視して、リンクだけを指定するようにします。リンクの文言は「所在地・電話番号」で統一されていますので、<a>タグの文言が「所在地・電話番号」である要素を指定すればよいことになります。

XPathでテキストを指定する記述方法

試しに、左端の裁判所名のXPathを取得してみます。

XPath取得(テキスト欄)

XPathは //dt[contains(text(),'札幌高等裁判所')] となりました。
このことから、リンク要素のXPathは //a[contains(text(),’所在地・代表電話’)] となることが分かります。

XPathの検証

実際に上記のXPathでリンク要素を取得できるかを、XPath検証ツールでチェックします。

XPath検証(所在地・電話番号)

【同一要素が111個あります】と表示されました。
文言が「所在地・代表電話」の<a>タグを一律に取得するので、重複が発生していますが、動作としては正常です。しかし実際のシナリオでは、その中から1個を特定して操作することになります。
生成コード欄に
driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]')[0]
とありますように、スクリプトを作成する時は、使用するメソッドを[find_elements]にした上で、末尾に序数を付けて操作します。
なお、内側の「’(シングルクォーテーション)」はエスケープ処理「\(円マーク)」が必要です。

補足

今回は要素をXPathで取得していますが、Seleniumにはリンクの文言で要素を指定する方法もあります。
XPath検証ツールで、リンク文言(所在地・代表電話)を入力してボタンを押せば、要素を取得するコードが出力されます。

要素をリンクテキストで取得
要素をリンクテキストで取得

やはり要素が111個取得され、生成コードは
driver.find_elements_by_partial_link_text('所在地・代表電話')[0]
となります。

B.地域画面

XPath取得

XPath取得ツールでXPathを取得します。

XPath取得(画面B)
裁判所名 XPath
函館地方裁判所・函館家庭裁判所・函館簡易裁判所 //div//tr[3]/td[1]/a
函館地方裁判所江差支部・函館家庭裁判所江差支部・江差簡易裁判所 //div//tr[4]//a
函館家庭裁判所松前出張所・松前簡易裁判所 //div//tr[5]//a
函館家庭裁判所八雲出張所・八雲簡易裁判所 //div//tr[6]//a
函館家庭裁判所寿都出張所・寿都簡易裁判所 //div//tr[7]//a

一番上だけが、tdタグを省略できていないのが気になります。

XPath取得(画面B)
リンク(ダイヤルイン番号)

表の同じ行に、もうひとつ<a>タグ(ダイヤルイン番号)があるのが原因です。
tdタグを省略してしまうと、住所のリンクと、ダイヤルインのリンクを区別できなくなります。ダイヤルインが表示されるケースは他にもあるでしょうから、tdは省略できません。
よって、XPathは
//div//tr[序数]/td[1]/a
となり、序数は3始まりで、1ずつ加算することになります。

XPath検証

念のため、tdを付け加えて要素が取得できることを、XPath検証ツールで確認します。

XPath検証(画面B)

C.詳細画面

XPath取得

XPath取得ツールでXPathを取得します。

XPath取得(画面C)
項目 XPath
裁判所名 //div[@id="VcArea-MainColum"]/div/h3
郵便番号 //div[@id="VcArea-MainColum"]/div/dl[1]/dd
所在地 //div[@id="VcArea-MainColum"]/div/dl[2]/dd
電話番号 //div[@id="VcArea-MainColum"]/div/dl[3]/dd

使用するXPath

画面 項目 XPath 操作 序数
全国 リンク //a[contains(text(),’所在地・代表電話’)][序数] クリック 0始まり
地域 リンク //div//tr[序数]/td[1]/a クリック 3始まり
詳細 名称 //div[@id="VcArea-MainColum"]/div/h3 テキスト取得  
//div[@id="VcArea-MainColum"]/div/dl[1]/dd テキスト取得  
住所 //div[@id="VcArea-MainColum"]/div/dl[2]/dd テキスト取得  
TEL //div[@id="VcArea-MainColum"]/div/dl[3]/dd テキスト取得  

操作コード

上記のXPathから要素を操作するコードを確定します。

画面 項目 操作コード 変数
全国 リンク driver.find_elements_by_xpath(('//a[contains(text(),\'所在地・代表電話\')]')[i].click() 0始まり
地域 リンク driver.find_element_by_xpath('//div//tr[j]/td[1]/a').click() 3始まり
詳細 名称 driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/h3').text  
driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text  
住所 driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text  
TEL driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text  

全国画面では、「所在地・代表電話」の<a>タグ(111個)を取得し、0番目から順番にクリックします。
よって、find_elementsで複数要素を取得し、取得した要素を順番に処理します。

一方、地域画面では、//div//tr[j]/td[1]/aでXPathを指定します。この場合、取得される要素はj番目の1個だけです。よってfind_elementで1個の要素を取得・操作します。

find_elementとfind_elementsの違い

4.Seleniumスクリプト作成

実行環境の準備

  1. Python実行環境を準備します。
    Pythonのインストール
  2. Seleniumの 実行環境を準備します。
    Seleniumのインストール
  3. Excelを操作するためのPythonライブラリを導入します。
    xlwingsのインストール
  4. 使用するブラウザのドライバを導入します。
    Google Chrome
    Firefox
    Microsoft Edge
    Internet Explorer

スクリプト構成

Seleniumスクリプトのプログラム構成(フローチャート)を考えます。

  1. 利用するライブラリを使う準備(import)
  2. ブラウザを操作するWebドライバの起動
  3. 出力Excelシートを開く
  4. ブラウザ操作
    1. 全国の裁判所一覧ページに移動
    2. 地域一覧ページに遷移
    3. 詳細画面に遷移
    4. 出力項目をExcelに出力
  5. Excelシートの保存

1~3はブラウザ操作を行うための前準備、5は後処理ですので、1回だけ実行します。
4は画面操作なので、裁判所の件数分、繰り返しの処理となります。

1.ライブラリのインポート

Pythonライブラリ(モジュール)を使用するために、前もって使用するライブラリを import文で読み込みます。

#Seleniumライブラリ
from selenium import webdriver
#Excel操作ライブラリ
import xlwings as xw

2.Webドライバの起動

操作するブラウザのWebドライバを起動します。

#Chrome
driver_path = (Webドライバの配置先)
driver = webdriver.Chrome(executable_path=driver_path)
#Firefox
driver_path = (Webドライバの配置先)
driver = webdriver.Firefox(executable_path=driver_path)
#Microsoft Edge
driver_path = (Webドライバの配置先)
driver = webdriver.Edge(executable_path=driver_path)
#Intenet Explorer
driver_path = (Webドライバの配置先)
driver = webdriver.Ie(executable_path=driver_path)

3.Excelシートを開く

Excelブックを開き、シートの1行目にヘッダ情報を出力します。

# Excelオープン
book_path = (Excelブックの配置先)
wb1 = xw.Book(book_path) 
ws1 = wb1.sheets[0]    #シート1枚目
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'

5.Excelシートの保存

Excelブックを保存して閉じます。

#Excel保存
wb1.save(book_path)
wb1.close()

4.ブラウザ操作

  1. 全国の裁判所一覧ページに移動
  2. 地域一覧ページに遷移
  3. 詳細画面に遷移
  4. 項目をExcelに出力

4-1.全国の裁判所一覧ページに移動

ブラウザに「所在地一覧」のURL(https://www.courts.go.jp/courthouse/map_tel/index.html)を入力します。

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')

4-2.地域一覧ページに遷移

一覧画面(全国)

【所在地・代表電話】をクリックします。

driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]')[i].click()

変数「i」は0始まりで、1ずつ加算されます。

4-3.詳細画面に遷移

一覧画面(地域)

各裁判所へのリンクをクリックします。

driver.find_element_by_xpath('//div//tr[j]/td[1]/a').click()

このままですと、「j」は変数ではなく「j」という固定文字として、解釈されますので

driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a').click()

のように、シングルクォーテーションの外に出します。
変数「j」は3始まりで1ずつ加算されます。

4-4.項目をExcelに出力

函館地方・家庭・簡易裁判所の所在地
裁判所の所在地

名称、郵便番号、所在地、電話番号をExcelのk行目に出力します。

#Excel出力
ws1.cells(k, 1).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/h3').text
ws1.cells(k, 2).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
ws1.cells(k, 3).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
ws1.cells(k, 4).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text

Excelシートの1行目がヘッダーなので、変数「k」は2始まりで、1ずつ加算されます。

ブラウザ操作の繰り返し

これらの操作をループで繰り返します。操作内容は

  1. 全国一覧画面で「所在地・代表電話」のリンクをクリックして、地域一覧画面に遷移
    変数:i、0始まり、1ずつ加算
  2. 地域一覧画面で各裁判所のリンクをクリックして詳細画面に遷移
    変数:j、3始まり、1ずつ加算
  3. 詳細画面の内容をExcelに記載
    変数:k、2始まり、1ずつ加算

でした。

これらのことから、全国のi件目について、地域をj件処理することになります。
そしてkは、i,jと関係なくシートに記載するたびに1ずつ増やします。

全国 地域 裁判所 i(全国) j(地域) k(Excel)
札幌 札幌 支部1 0

3

2
  札幌 支部2 0 4 3
  旭川 支部1 0 5 4
  旭川 支部2 0 6 5
仙台 青森 支部1 1 3 6
  青森 支部2 1 4 7
  岩手 支部1 1 5 8
  岩手 支部2 1 6 9
  秋田 支部1 1 7 10

ですから、ループはiの中に、jのループが入る二重ループになります。

変数(i)

iは全国画面で「所在地・代表電話」のリンクがある間、繰り返します。
「所在地・代表電話」のリンクを取得するコードは

driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]')

で、リンク要素を111個取得できました。そして、1つ1つのリンクは

driver.find_elements_by_xpath(('//a[contains(text(),\'所在地・代表電話\')]')[i].click()

で操作しました。

よって、iが取る値は、0~リンク要素の個数 – 1(=110)となります。(0始まりなので、ラストは110)
要素の個数は len()関数を用いて

len(driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]'))

で取得できるので、iのループは

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]'))
#count_iは111となる
for i in range(count_i) #0~110まで繰り返す driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html') driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]')[i].click()

となります。

変数(j)

jは地域画面で、表の行のリンク先がある間、繰り返します。要素を取得するコードは

driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')    #jは3始まり

でしたので、上記のコードで要素が存在する間、

driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a').click()

クリック処理を実施します。

要素が存在するかを判定するには

  1. 要素の個数が0でないことを判定
  2. 要素が存在しない場合、クリックすると例外が発生するので、例外をキャッチ

と二通りの方法があります。今回は1の方法を取ります。

要素の個数を知るには、変数iの時に使用したように、まずfind_elements関数で要素のリストを取得し、次にlen関数でリストの個数を得ます。

len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a'))    #要素の個数

これが0より大きければ、繰り返し処理を継続します。

j = 3
while len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
    driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a').click()
j += 1
変数(k)

kはExcelシートの行を管理します。2始まりでExcelに出力するたびに、1加算します。

#Excel出力 kは2始まり
ws1.cells(k, 1).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/h3').text
ws1.cells(k, 2).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
ws1.cells(k, 3).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
ws1.cells(k, 4).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
k += 1
ループ処理

上記のi、j、kの処理を合わせます。

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]'))
#count_iは111となる

for i in range(count_i) #0~110まで繰り返す
	driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
	driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]')[i].click()
	
	j = 3
	while len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
    	driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a').click()

		#Excel出力 kは2始まり
		ws1.cells(k, 1).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/h3').text
		ws1.cells(k, 2).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
		ws1.cells(k, 3).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
		ws1.cells(k, 4).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
		k += 1
                j += 1
修正点
  • Kに初期値(2)を与えます
  • while文の繰り返し処理では、毎回の処理開始時に、地域画面が表示されている必要があります。
    → while文の最後でブラウザの戻るボタンを押す処理を入れます。
k = 2
driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]'))
#count_iは111となる

for i in range(count_i) #0~110まで繰り返す
	driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
	driver.find_elements_by_xpath('//a[contains(text(),\'所在地・代表電話\')]')[i].click()
	j = 3
	while len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
    	driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a').click()
		#Excel出力
		ws1.cells(k, 1).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/h3').text
		ws1.cells(k, 2).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
		ws1.cells(k, 3).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
		ws1.cells(k, 4).value = driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
		k += 1
j += 1 driver.back() #ブラウザの戻る

完成スクリプト

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)
    j = 3
    while len(driver.find_elements_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a').click()
        #Excel出力
        ws1.cells(k, 1).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/h3').text
        ws1.cells(k, 2).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
        ws1.cells(k, 3).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
        ws1.cells(k, 4).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
        driver.back()   #ブラウザの戻る
        j += 1
        k += 1

#Excel保存
wb1.save(book_path)
wb1.close()

Chromeでは、事前にウィンドウの倍率を100%にしておかないと、リンクのクリックができません。

スクリプトの実行

Pythonで作成したSeleniumのスクリプトを実行します。

コマンドプロンプトから実行する場合
  • Windows10の場合
    「スタートメニュー」「 Windows システムツール」「コマンドプロンプト」
  • Windows8の場合
    「すべてのアプリ」 「 Windows システムツール」「コマンドプロンプト」
  • Windows7の場合
    「スタートメニュー」「すべてのプログラム」「アクセサリ」「コマンドプロンプト」

コマンドプロンプトの画面で

python 実行ファイル名
python listCourt1.py

プログラムが起動すると、ブラウザとExcelが開き、住所と電話番号を収集します。

スクリプト実行

なんと、一番最初の札幌高裁のデータが取得できていません。
その後、エラーが発生しました。

5.エラーの修正

5-1.タブの切り替え

コマンドプロンプト(例外発生)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//div[@id="VcArea-MainColum"]/div/h3"}

要素が存在しないようです。ブラウザの状態を見てみます。

エラー発生個所
別タブ表示

上部を見てもらえると分かりますように、八雲出張所だけ、なぜか別タブで開かれています。
実際に、地域一覧画面に戻って「八雲出張所」のリンクをクリックすると、新規タブが開きます。
本来は他と同じように、同一タブ内で画面が切り替わるのが正しいような気もしますが、依頼して修正してもらう訳にもいきません。ここがRPAのツライところです。

タブの切り替え

Seleniumはタブの切り替わりを自動では処理しませんので、明示的に処理対象のタブを切り替える必要があります。【driver.window_handles】と【driver.switch_to.window】を使用します。

Seleniumが起動したブラウザのウィンドウ(タブ)は、window_handlesというリスト(配列)で管理されています。

  • タブが1枚:window_handles[0]
  • タブが2枚:window_handles[0],window_handles[1]
  • タブが3枚:window_handles[0],window_handles[1],window_handles[2]

そして、タブを切り替えるときは、switch_to_window関数に切り替え先のハンドルを渡します。

driver.switch_to.window(window_handles[1])

ただし今回は、最終的に開いたタブを閉じて、元のタブに戻す必要があるので、現在のタブのハンドルを取得する【driver.current_window_handle】を用いて、

before_handle = driver.current_window_handle    #現在のタブのハンドルを保存
#タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
if len(driver.window_handles) > 1:
    driver.switch_to.window(window_handles[1])

    (処理終了後)
    driver.close()    #新規タブを閉じる
    driver.switch_to.window(before_handle)    #元のタブに戻す

とします。

5-2.札幌高裁のXPath

XPathの違い

出力シート

一番最初の札幌高裁のデータが取得できていません。XPathを調べてみます。

XPath取得(札幌高裁)
札幌高裁

ここで指定しているXpathは //div//tr[j]/td[1]/a (jは3始まり)でしたが、札幌高裁の場合は、jが1になるようです。このままでは、他のケースと共有できません。
<tr>タグは表の行を示しています。HTMLソースを見ると、以下のようになっていました。

札幌高裁(テーブルヘッダーはtheadタグ)
札幌高裁(ヘッダーは<thead>)
その他のケース(テーブルヘッダーはtrタグ)
その他のケース(ヘッダーは<tr>)

対応策

変数jは、表のヘッダー部(tr[1],tr[2])を除いて3から始まることにしていますが、ヘッダー部が<thead>タグの場合は、jを1から始めることにします。

<thead>部分のXPathは //table/thead ですので、XPath検証ツールで

  • 札幌高裁では要素が1個しかないこと
  • 通常のケースでは0個であること

を確認します。

XPath検証(札幌高裁)
札幌高裁
XPath検証(仙台高裁)
仙台高裁(NoSuchElementException発生)

修正コード

#表のヘッダーの相違でtrの序数が変わる
if len(driver.find_elements_by_xpath('//table/thead')) > 0:
    j = 1
else:
    j = 3

修正スクリプト

上記の修正コードを書き加えます。

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)

    #表のヘッダーの相違でtrの序数が変わる
    if len(driver.find_elements_by_xpath('//table/thead')) > 0:
        j = 1
    else:
        j = 3
    while len(driver.find_elements_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        before_handle = driver.current_window_handle  # 現在のタブのハンドルを保存
        driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a').click()
        # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
        if len(driver.window_handles) > 1:
            driver.switch_to.window(driver.window_handles[1])
        #Excel出力
        ws1.cells(k, 1).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/h3').text
        ws1.cells(k, 2).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
        ws1.cells(k, 3).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
        ws1.cells(k, 4).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
        #地域一覧画面に戻る
        if len(driver.window_handles) > 1:
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
        else:
            driver.back()   #ブラウザの戻る
        j += 1
        k += 1

#Excel保存
wb1.save(book_path)
wb1.close()

スクリプトの実行

コマンドプロンプトから実行します。

python listCourt2.py

札幌高裁のデータが取得できています。

修正スクリプトの実行

エラーが発生しました。

5-3.表のタイトルなし

コマンドプロンプト(例外発生)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//div[@id="VcArea-MainColum"]/div/h3"}

XPathが //div[@id="VcArea-MainColum"]/div/h3 の要素(裁判所名)が存在しないとのことです。ブラウザを見てみましょう。

エラー発生個所
表のタイトルなし

やはり、表の上部の裁判所名がありません。仕方がないので、左上のヘッダー部分から編集します。

ヘッダー部のXPathを、XPath取得ツールで取得します。

XPath 操作
// div[ @ id = “VcArea-Header"] / div / h2 テキスト取得

要素が存在するかの判定は、変数jのところで記載したように

  1. 要素の個数が0でないことを判定
  2. 要素が存在しない場合、クリックすると例外が発生するので、例外をキャッチ

の二通りがあります。
前回は1を適用したので、学習のためにも今回は2を採用します。

try:
    ws1.cells(k, 1).value = driver.find_element_by_xpath \
        ('//div[@id="VcArea-MainColum"]/div/h3').text
except:
    # 表上部の裁判所が存在しない場合はヘッダー部から編集
    ws1.cells(k, 1).value = driver.find_element_by_xpath \
        ('//div[@id="VcArea-Header"]/div/h2').text

修正スクリプト

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)
    #表のヘッダーの相違でtrの序数が変わる
    if len(driver.find_elements_by_xpath('//table/thead')) > 0:
        j = 1
    else:
        j = 3
    while len(driver.find_elements_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        before_handle = driver.current_window_handle  # 現在のタブのハンドルを保存
        driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a').click()
        # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
        if len(driver.window_handles) > 1:
            driver.switch_to.window(driver.window_handles[1])
        #Excel出力
        try:
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/h3').text
        except:
            # 表上部の裁判所が存在しない場合はヘッダー部から編集
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-Header"]/div/h2').text
        ws1.cells(k, 2).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
        ws1.cells(k, 3).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
        ws1.cells(k, 4).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
        #地域一覧画面に戻る
        if len(driver.window_handles) > 1:
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
        else:
            driver.back()   #ブラウザの戻る
        j += 1
        k += 1

#Excel保存
wb1.save(book_path)
wb1.close()

 

スクリプトの実行

修正したスクリプトをコマンドプロンプトから実行します。

python listCourt3.py

またエラーが発生しました。

5-4.知財高裁が別サイト

コマンドプロンプト(例外発生)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//div[@id="VcArea-MainColum"]/div/dl[3]/dd"}

ブラウザの状態を見てみます。

エラー発生個所
知的財産高等裁判所サイト

知財高裁だけ、各地の裁判所とは異なるページ内容になっています。

知財高裁へのリンク

リンク元で、「別サイトへジャンプします」となっています。

知財高裁の対応

1か所だけなので、自動操作はあきらめ、エラー扱いとしてシートに記載します。
しかしながら、知財高裁のリンクをクリックすると別タブが開いてしまうので、

  1. リンクをクリックしない
  2. 開いたタブを閉じる

どちらかの対応が必要です。

今回は2で行きます。

#知的高裁エラーハンドリング
if len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
    ws1.cells(k, 1).value = 'エラー'
    ws1.cells(k, 2).value = 'i=' + str(i)
    k += 1
    if len(driver.window_handles) > 1:
        before_handle = driver.current_window_handle  # 現在のタブのハンドルを保存
        driver.switch_to.window(driver.window_handles[1])
        driver.close()  # 新規タブを閉じる
        driver.switch_to.window(before_handle)  # 元のタブに戻す

なお、知財高裁の住所・電話番号は「東京高裁」経由で取得できます。

捕捉)重複データの扱い

収集したデータを見ますと、簡易裁判所と家庭裁判所を併設しているようなケースでは、簡易裁判所からのリンクと、家庭裁判所からのリンクで、データが重複して記載されています。

いったん、重複分もExcelに出力して、重複データはExcelで削除することにします。
プログラムを組むよりもExcelの標準機能を用いた方が早くて確実です。

修正スクリプト

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)
    #表のヘッダーの相違でtrの序数が変わる
    if len(driver.find_elements_by_xpath('//table/thead')) > 0:
        j = 1
    else:
        j = 3
    #知的高裁エラーハンドリング
    if len(driver.find_elements_by_xpath \
                       ('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
        ws1.cells(k, 1).value = 'エラー'
        ws1.cells(k, 2).value = 'i=' + str(i)
        k += 1
        if len(driver.window_handles) > 1:
            before_handle = driver.current_window_handle  # ハンドルを保存
            driver.switch_to.window(driver.window_handles[1])
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
    while len(driver.find_elements_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        before_handle = driver.current_window_handle  # 現在のタブのハンドルを保存
        driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a').click()
        # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
        if len(driver.window_handles) > 1:
            driver.switch_to.window(driver.window_handles[1])
        #Excel出力
        try:
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/h3').text
        except:
            # 表上部の裁判所が存在しない場合はヘッダー部から編集
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-Header"]/div/h2').text
        ws1.cells(k, 2).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
        ws1.cells(k, 3).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
        ws1.cells(k, 4).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
        #地域一覧画面に戻る
        if len(driver.window_handles) > 1:
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
        else:
            driver.back()   #ブラウザの戻る
        j += 1
        k += 1
#Excel保存
wb1.save(book_path)
wb1.close()

5-5.電話番号なし

スクリプトを実行すると、エラーが発生します。

コマンドプロンプト(例外発生)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//div[@id="VcArea-MainColum"]/div/dl[3]/dd"}
エラー発生個所

電話番号欄が存在しません。
地域画面にダイヤルイン一覧として、pdfファイルが掲載されています。
SeleniumでPDFから文字を取得するのは難しいので、手動で取得するしかないようです。

地域画面(横浜地裁)

コードの修正

電話番号の欄が存在する場合のみ、電話番号を取得します。

#電話番号が存在しないケースあり(横浜地方裁判所)
if len(driver.find_elements_by_xpath \
    ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd')) > 0: 
    ws1.cells(k, 4).value = driver.find_element_by_xpath \
        ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text

修正スクリプト

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)
    #表のヘッダーの相違でtrの序数が変わる
    if len(driver.find_elements_by_xpath('//table/thead')) > 0:
        j = 1
    else:
        j = 3
    #知的高裁エラーハンドリング
    if len(driver.find_elements_by_xpath \
                       ('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
        ws1.cells(k, 1).value = 'エラー'
        ws1.cells(k, 2).value = 'i=' + str(i)
        k += 1
        if len(driver.window_handles) > 1:
            before_handle = driver.current_window_handle  # ハンドルを保存
            driver.switch_to.window(driver.window_handles[1])
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
    while len(driver.find_elements_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        before_handle = driver.current_window_handle  # 現在のタブのハンドルを保存
        driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a').click()
        # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
        if len(driver.window_handles) > 1:
            driver.switch_to.window(driver.window_handles[1])
        #Excel出力
        try:
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/h3').text
        except:
            # 表上部の裁判所が存在しない場合はヘッダー部から編集
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-Header"]/div/h2').text
        ws1.cells(k, 2).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
        ws1.cells(k, 3).value = driver.find_element_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
        #電話番号が存在しないケースあり(横浜地方裁判所)
        if len(driver.find_elements_by_xpath \
            ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd')) > 0:
            ws1.cells(k, 4).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
        #地域一覧画面に戻る
        if len(driver.window_handles) > 1:
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
        else:
            driver.back()   #ブラウザの戻る
        j += 1
        k += 1
#Excel保存
wb1.save(book_path)
wb1.close()

5-6.PDF表示

スクリプトを実行すると、エラーが発生します。

コマンドプロンプト(例外発生)
pdfファイル
長崎地裁

長崎地裁はPDFが表示されます。地域画面の方には、住所・電話番号とも表示されています。

地域画面(長崎)

対応方法

地域画面でリンクをクリック後、pdfファイルの場合は、地域画面の情報でExcel出力します。
画面の項目を取得するコードは以下のようになります。

項目 XPath テキスト取得コード
裁判所名 //div//tr[j]/td[1]/a driver.find_element_by_xpath('//div//tr[j]/td[1]/a’).text
郵便番号 //div//tr[j]/td[2] driver.find_element_by_xpath('//div//tr[j]/td[2]’).text
住所 //div//tr[j]/td[3] driver.find_element_by_xpath('//div//tr[j]/td[3]’).text
電話番号 //div//tr[j]/td[5] driver.find_element_by_xpath('//div//tr[j]/td[5]’).text

pdfファイルかどうかは、リンク要素(裁判所名)のURLの末尾が「.pdf」かどうかで判断します。

#リンク先がpdfの場合は遷移無し
url = driver.find_element_by_xpath \
                  ('//div//tr[' + str(j) + ']/td[1]/a').get_attribute('href')    #リンク先URL取得
if url[len(url)-4:] == '.pdf':    #urlの末尾4文字が'.pdf'
    ws1.cells(k, 1).value = driver.find_element_by_xpath \
                  ('//div//tr[' + str(j) + ']/td[1]/a').text
    ws1.cells(k, 2).value = driver.find_element_by_xpath \
                  ('//div//tr[' + str(j) + ']/td[2]').text
    ws1.cells(k, 3).value = driver.find_element_by_xpath \
                  ('//div//tr[' + str(j) + ']/td[3]').text
    ws1.cells(k, 4).value = driver.find_element_by_xpath \
                  ('//div//tr[' + str(j) + ']/td[5]').text

修正スクリプト

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)
    #表のヘッダーの相違でtrの序数が変わる
    if len(driver.find_elements_by_xpath('//table/thead')) > 0:
        j = 1
    else:
        j = 3
    #知的高裁エラーハンドリング
    if len(driver.find_elements_by_xpath \
                       ('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
        ws1.cells(k, 1).value = 'エラー'
        ws1.cells(k, 2).value = 'i=' + str(i)
        k += 1
        if len(driver.window_handles) > 1:
            before_handle = driver.current_window_handle  # ハンドルを保存
            driver.switch_to.window(driver.window_handles[1])
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
    while len(driver.find_elements_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        #リンク先がpdfの場合は遷移無し
        url = driver.find_element_by_xpath \
                ('//div//tr[' + str(j) + ']/td[1]/a').get_attribute('href')
        if url[len(url)-4:] == '.pdf':  #urlの末尾4文字が'.pdf'
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a').text
            ws1.cells(k, 2).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[2]').text
            ws1.cells(k, 3).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[3]').text
            ws1.cells(k, 4).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[5]').text
        else:
            before_handle = driver.current_window_handle  # 現在のハンドルを保存
            driver.find_element_by_xpath \
                ('//div//tr[' + str(j) + ']/td[1]/a').click()
            # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
            if len(driver.window_handles) > 1:
                driver.switch_to.window(driver.window_handles[1])
            #Excel出力
            try:
                ws1.cells(k, 1).value = driver.find_element_by_xpath \
                    ('//div[@id="VcArea-MainColum"]/div/h3').text
            except:
                # 表上部の裁判所が存在しない場合はヘッダー部から編集
                ws1.cells(k, 1).value = driver.find_element_by_xpath \
                    ('//div[@id="VcArea-Header"]/div/h2').text
            ws1.cells(k, 2).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
            ws1.cells(k, 3).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
            #電話番号が存在しないケースあり(横浜地方裁判所)
            if len(driver.find_elements_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd')) > 0:
                ws1.cells(k, 4).value = driver.find_element_by_xpath \
                    ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
            #地域一覧画面に戻る
            if len(driver.window_handles) > 1:
                driver.close()  # 新規タブを閉じる
                driver.switch_to.window(before_handle)  # 元のタブに戻す
            else:
                driver.back()   #ブラウザの戻る
        j += 1
        k += 1
#Excel保存
wb1.save(book_path)
wb1.close()

スクリプトの実行

修正したスクリプトをコマンドプロンプトから実行します。

python listCourt6.py

ようやく例外が発生せずに、終了しました。

コマンドプロンプト(スクリプト終了)

5-7.リンクのXPathが異なる

エラー内容

保存されたExcelシートを確認すると、エラーが3か所あります。

エラー(i=23)
i=23

知財高裁はエラー扱いとしましたので、問題ありません。1行上に、正常データが取得できています。

エラー(i=78,79)
i=78,79

山口と鳥取の間でエラーが発生しています。

エラー(i=82,83)
i=82,83

鳥取と高松の間でも、エラーが発生しています。

知財高裁エラーのところで、エラー出力する対応のおかげで、エラー発生を知ることができました。

エラー箇所の特定

エラー箇所はどこかを確認します。

全国画面(広島高裁管区)

山口と鳥取の間は「岡山」、鳥取と高松の間は「松江」です。

エラー原因(岡山)

岡山の地域画面を見ると、表形式が他のケースと異なります。【地図】をクリックすると詳細画面に遷移します。

地域画面(岡山)
地域画面(岡山)

しかも、詳細画面のXPathも異なります。

詳細画面(岡山)
詳細画面(岡山)
画面 項目 XPath 備考
地域 【地図】 //div//tr[j]//a[1] jは3始まり
詳細 裁判所名 //div[@id="VcArea-MainColum"]/div//caption  
郵便番号 //div[@id="VcArea-MainColum"]/div//tr[1]/td  
所在地 //div[@id="VcArea-MainColum"]/div//tr[2]/td  
電話番号 //div[@id="VcArea-MainColum"]/div//tr[3]/td  

さらには、支部については別タブで表示されます。

詳細画面(岡山支部)

エラー原因(松江)

こちらも地域画面でのクリック項目が異なります。

地域画面(松江)
地域画面(松江)
画面 項目 XPath
地域 地図表示 //div//tr[3]/td[1]//a

詳細画面のXPathは他と同じですが、新規タブで表示されます。

詳細画面(松江)

対応方法

以上から全体として、地域画面での場合分けは以下のようになります。

地域 リンクのXPath 処理
知財高裁 リンク要素なし エラー
岡山 //div//tr[j]//a[1] 独自処理
松江 //div//tr[j]/td[1]//a 通常処理
その他 //div//tr[j]/td[1]/a
元のコード
#知的高裁エラーハンドリング
if len(driver.find_elements_by_xpath \
                   ('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
    ws1.cells(k, 1).value = 'エラー'
    ws1.cells(k, 2).value = 'i=' + str(i)
    k += 1
    if len(driver.window_handles) > 1:
        before_handle = driver.current_window_handle  # ハンドルを保存
        driver.switch_to.window(driver.window_handles[1])
        driver.close()  # 新規タブを閉じる
        driver.switch_to.window(before_handle)  # 元のタブに戻す
while len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
    #リンク先がpdfの場合は遷移無し
    url = driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a').get_attribute('href')
if url[len(url)-4:] == '.pdf':
    (処理)
else:
    before_handle = driver.current_window_handle  # 現在のハンドルを保存
    driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a').click()

これまでは、地域画面でのリンク要素 //div//tr[j]/td[1]/a をwhile文の判定条件にしていました。
しかし、通常ケース //div//tr[j]/td[1]/a 以外に、松江のリンク //div//tr[j]/td[1]//a も判定条件に加えなければなりません。

そこで、変数を用いて、リンク要素を先に変数に代入します。

link = None
#通常ケース
if len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
    link = driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')
#松江
elif len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]//a')) > 0:
    link = driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]//a')
#知財高裁はそのまま(link=None)
elif len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
    ws1.cells(k, 1).value = 'エラー'
    ws1.cells(k, 2).value = 'i=' + str(i)
    k += 1
    if len(driver.window_handles) > 1:
        before_handle = driver.current_window_handle  # ハンドルを保存
        driver.switch_to.window(driver.window_handles[1])
        driver.close()  # 新規タブを閉じる
        driver.switch_to.window(before_handle)  # 元のタブに戻す

while link is not None:    #通常ケース&松江
    #リンク先がpdfの場合は遷移無し
    url = link.get_attribute('href')
if url[len(url)-4:] == '.pdf':
    (処理)
else:
    before_handle = driver.current_window_handle  # 現在のハンドルを保存
    link.click()

while文の継続条件が「変数link がNoneでないこと」に変わりましたので、ループ処理の最後に、次のリンク要素を変数linkに代入する処理を追加します。

    while link is not None: #通常ケース&松江
        (ループ処理)
        j += 1
        k += 1
        #通常ケース
        if len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
            link = driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]/a')
        # 松江
        elif len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']/td[1]//a')) > 0:
            link = driver.find_element_by_xpath('//div//tr[' + str(j) + ']/td[1]//a')
        else:
            link = None
#Excel保存
wb1.save(book_path)
wb1.close()

個別に岡山の対応を追加します。

#岡山
if len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']//a[1]')) > 0:
    while len(driver.find_elements_by_xpath('//div//tr[' + str(j) + ']//a[1]')) > 0:
        before_handle = driver.current_window_handle  # 現在のハンドルを保存
        driver.find_element_by_xpath('//div//tr[' + str(j) + ']//a[1]').click()
        # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
        if len(driver.window_handles) > 1:
            driver.switch_to.window(driver.window_handles[1])
    	#詳細画面も独自形式
        ws1.cells(k, 1).value = \
        	driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div//caption').text
        ws1.cells(k, 2).value = \
        	driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div//tr[1]/td').text
        ws1.cells(k, 3).value = \
        	driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div//tr[2]/td').text
        ws1.cells(k, 4).value = \
        	driver.find_element_by_xpath('//div[@id="VcArea-MainColum"]/div//tr[3]/td').text
        #地域一覧画面に戻る
        if len(driver.window_handles) > 1:
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す
        else:
            driver.back()   #ブラウザの戻る
    j += 1
    k += 1

完成スクリプト

上記の対応を合わせます

# -*- coding:utf-8 -*-
from selenium import webdriver
import xlwings as xw

# Chromeドライバ起動
driver_path = r'D:\SeleniumCodeChecker\driver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)

# Excelオープン
book_path = r'D:\SeleniumCodeChecker\ListCourt\裁判所所在地.xlsx'
wb1 = xw.Book()
ws1 = wb1.sheets[0]
#列幅の設定
xw.Range('a:a').column_width = 25
xw.Range('b:b').column_width = 9
xw.Range('c:c').column_width = 50
xw.Range('d:d').column_width = 20
#ヘッダ出力
ws1.cells(1, 1).value = '裁判所'
ws1.cells(1, 2).value = '郵便番号'
ws1.cells(1, 3).value = '住所'
ws1.cells(1, 4).value = '電話番号'
k=2 #Excelシートの行

driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
count_i = len(driver.find_elements_by_xpath \
                  ('//a[contains(text(),\'所在地・代表電話\')]'))

for i in range(count_i):
    driver.get('https://www.courts.go.jp/courthouse/map_tel/index.html')
    driver.find_elements_by_xpath \
        ('//a[contains(text(),\'所在地・代表電話\')]')[i].click() #element(s)
    #表のヘッダーの相違でtrの序数が変わる
    if len(driver.find_elements_by_xpath('//table/thead')) > 0:
        j = 1
    else:
        j = 3
    link = None    #地域画面のリンク要素
    #通常ケース
    if len(driver.find_elements_by_xpath \
                       ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
        link = driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]/a')
    #松江
    elif len(driver.find_elements_by_xpath \
                         ('//div//tr[' + str(j) + ']/td[1]//a')) > 0:
        link = driver.find_element_by_xpath \
            ('//div//tr[' + str(j) + ']/td[1]//a')
    #岡山
    elif len(driver.find_elements_by_xpath \
                         ('//div//tr[' + str(j) + ']//a[1]')) > 0:
        while len(driver.find_elements_by_xpath \
                              ('//div//tr[' + str(j) + ']//a[1]')) > 0:
            before_handle = driver.current_window_handle  # 現在のハンドルを保存
            driver.find_element_by_xpath \
                ('//div//tr[' + str(j) + ']//a[1]').click()
            # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
            if len(driver.window_handles) > 1:
                driver.switch_to.window(driver.window_handles[1])
            # 詳細画面も独自形式
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div//caption').text
            ws1.cells(k, 2).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div//tr[1]/td').text
            ws1.cells(k, 3).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div//tr[2]/td').text
            ws1.cells(k, 4).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div//tr[3]/td').text
            #地域一覧画面に戻る
            if len(driver.window_handles) > 1:
                driver.close()  # 新規タブを閉じる
                driver.switch_to.window(before_handle)  # 元のタブに戻す
            else:
                driver.back()   #ブラウザの戻る
            j += 1
            k += 1
    #知財高裁
    elif len(driver.find_elements_by_xpath \
                       ('//div//tr[' + str(j) + ']/td[1]/a')) == 0:
        ws1.cells(k, 1).value = 'エラー'
        ws1.cells(k, 2).value = 'i=' + str(i)
        k += 1
        if len(driver.window_handles) > 1:
            before_handle = driver.current_window_handle  # ハンドルを保存
            driver.switch_to.window(driver.window_handles[1])
            driver.close()  # 新規タブを閉じる
            driver.switch_to.window(before_handle)  # 元のタブに戻す

    #通常ケース&松江
    while link is not None:
        #リンク先がpdfの場合は遷移無し
        url = link.get_attribute('href')
        if url[len(url)-4:] == '.pdf':  #urlの末尾4文字が'.pdf'
            ws1.cells(k, 1).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[1]/a').text
            ws1.cells(k, 2).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[2]').text
            ws1.cells(k, 3).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[3]').text
            ws1.cells(k, 4).value = driver.find_element_by_xpath \
                          ('//div//tr[' + str(j) + ']/td[5]').text
        else:
            before_handle = driver.current_window_handle  # 現在のハンドルを保存
            link.click()
            # タブが2枚になっていたら、新規タブが開いているので、タブを切り替える
            if len(driver.window_handles) > 1:
                driver.switch_to.window(driver.window_handles[1])
            #Excel出力
            try:
                ws1.cells(k, 1).value = driver.find_element_by_xpath \
                    ('//div[@id="VcArea-MainColum"]/div/h3').text
            except:
                # 表上部の裁判所が存在しない場合はヘッダー部から編集
                ws1.cells(k, 1).value = driver.find_element_by_xpath \
                    ('//div[@id="VcArea-Header"]/div/h2').text
            ws1.cells(k, 2).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[1]/dd').text
            ws1.cells(k, 3).value = driver.find_element_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[2]/dd').text
            #電話番号が存在しないケースあり(横浜地方裁判所)
            if len(driver.find_elements_by_xpath \
                ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd')) > 0:
                ws1.cells(k, 4).value = driver.find_element_by_xpath \
                    ('//div[@id="VcArea-MainColum"]/div/dl[3]/dd').text
            #地域一覧画面に戻る
            if len(driver.window_handles) > 1:
                driver.close()  # 新規タブを閉じる
                driver.switch_to.window(before_handle)  # 元のタブに戻す
            else:
                driver.back()   #ブラウザの戻る
        j += 1
        k += 1
        # while文の次の条件を設定
        #通常ケース
        if len(driver.find_elements_by_xpath \
                           ('//div//tr[' + str(j) + ']/td[1]/a')) > 0:
            link = driver.find_element_by_xpath \
                ('//div//tr[' + str(j) + ']/td[1]/a')
        # 松江
        elif len(driver.find_elements_by_xpath \
                             ('//div//tr[' + str(j) + ']/td[1]//a')) > 0:
            link = driver.find_element_by_xpath \
                ('//div//tr[' + str(j) + ']/td[1]//a')
        else:
            link = None
        ws1.cells(k, 1).select()  # 表示デモ用
#Excel保存
wb1.save(book_path)
wb1.close()

動画デモ用に、Excel列幅の設定(13~17行)、アクティブセルを画面内に表示する処理(145行)を追加しています。

動画

実行結果

終了画面

コマンドプロンプト(終了)
完了画面

結果ファイル

ここから、なるべく実用的なデータにしていきたいと思います。長くなりましたので次回にします。