対象の要素を取得していく中で、構成要素を検証し「img要素が含まれていた場合には~」「含まれていない場合には~」と処理を分岐させるもできるしまた<img>要素自体をそのままスルーして文字列だけの取得も可能
python
1from bs4 import BeautifulSoup
2import requests
3
4url = 対象URL
5for num in range(30):
6 html = requests.get(url + str(num+1))
7 soup = BeautifulSoup(html.content, 'html.parser')
8
9
文中で<br>タグで改行されていたり\t
が使用されていたりなど非常に作りの悪いサイトの様で
取得したテキストを分割したり正規表現などで各データを抽出・整形する必要がありそうです
python
1import re
2
3郵便番号パターン = r'〒\d{3}-\d{4}'
4電話番号パターン = r'TEL : \d{2,4}?-\d{2,4}?-\d{3,4}'
5
6
7table = soup.find('table', id='kojin')
8# table内から全てのtr要素を取得
9tr_list = soup.find_all('tr')
10for tr in tr_list:
11 # table要素内にtacクラスの要素がある場合に実行(要は1番目のtr要素の除外)
12 if tr.find_all(class_='tac'):
13 # 大まかにtr要素内の全ての文字列を取得・分割しておく
14 td_list = [td.text for td in tr.find_all('td')]
15 # 氏名を漢字表記とルビ表記で分割
16 # 氏名欄にimg要素が含まれていた場合には名前の先頭に目印として○をつける
17 if tr.find('img'):
18 names = td_list[1].split('\n')
19 names[0] = '○' + names[0]
20 else:
21 names = td_list[1].split('\n')
22 # 正規表現を使用して郵便番号の取得
23 zipcode = re.search(郵便番号パターン, td_list[4]).group()
24 # 正規表現を使用して電話番号の取得、電話番号を載せていないケースもある為try文を使用する
25 try:
26 telephone = re.search(電話番号パターン, td_list[4]).group()
27 locate = td_list[4].replace('\t','').replace('\n', '').replace(zipcode, '').replace(telephone, '')
28 # 電話番号を掲載していなかった場合の処理
29 except:
30 telephone = 'NO TEL'
31 locate = td_list[4].replace('\t','').replace('\n', '').replace(zipcode, '')
32
33
以上で、異体字時による画像の挿入によるデータ抽出失敗の回避と
所在や氏名などのデータの抽出が行う事ができるかと思います。
後はデータフレームとしてまとめてあげれば正常に目的の動作が得られるのではないでしょうか。
上記コードは実際には検証しておらず、またあくまでも方法の提示・解説であり
コピペすればそのままコードが動くというものではございませんのでご参考までにご活用ください。
追記
以下が正しいコードの実行方法となります。
python
1from bs4 import BeautifulSoup
2import requests
3import re
4
5郵便番号パターン = r'〒\d{3}-\d{4}'
6電話番号パターン = r'TEL : \d{2,4}?-\d{2,4}?-\d{3,4}'
7
8url = r'http://kensaku.shiho-shoshi.or.jp/search/member.php?search_code=01&search_name=&search_address=&x=140&y=16&pageID='
9for num in range(30):
10 html = requests.get(url + str(num+1))
11 soup = BeautifulSoup(html.content, 'html.parser')
12
13 table = soup.find('table', id='kojin')
14 tr_list = soup.find_all('tr')
15 for tr in tr_list:
16 if tr.find_all(class_='tac'):
17 td_list = [td.text for td in tr.find_all('td')]
18 if tr.find('img'):
19 names = td_list[1].split('\n')
20 names[0] = '○' + names[0]
21 else:
22 names = td_list[1].split('\n')
23 zipcode = re.search(郵便番号パターン, td_list[4]).group()
24 try:
25 telephone = re.search(電話番号パターン, td_list[4]).group()
26 locate = td_list[4].replace('\t','').replace('\n', '').replace(zipcode, '').replace(telephone, '')
27 except:
28 telephone = 'NO TEL'
29 locate = td_list[4].replace('\t','').replace('\n', '').replace(zipcode, '')
これで期待のデータの抽出できコード自体は正しく動くでしょうが、しかしこれではまだ多少の問題点が残ります。
例えば今回の例であればURLでは26ページ目までしか存在しませんが、それ以降のページも取得しようとコードが動いてしまったり、短時間での過度なリクエストを送ってしまうコードになってしまっており、相手方のサーバーへの負担をかけてしまう事になります。
その為、for文ではなくWhile文を使用し、次のページが存在する場合にのみスクレイピング+データの抽出を続行する形に改めて修正致しました。
python
1import re
2import time
3from urllib.parse import urljoin
4from bs4 import BeautifulSoup
5import requests
6
7
8# 予め正規表現でそれぞれのパターンを準備しておく
9郵便番号パターン = r'〒\d{3}-\d{4}'
10電話番号パターン = r'TEL : \d{2,4}?-\d{2,4}?-\d{3,4}'
11# 今回のケースの場合56行目で相対パスとして取得される為、予めここでもウェブサイトURL部分と分割しておく
12url = 'http://kensaku.shiho-shoshi.or.jp'
13href = '/search/member.php?search_code=01&search_name=&search_address=&x=140&y=16&pageID=1'
14
15while True:
16 html = requests.get(urljoin(url, href)) # urljoinでURLの結合
17 soup = BeautifulSoup(html.content, 'html.parser')
18 # table要素を指定
19 table = soup.find('table', id='kojin')
20 # table内から全てのtr要素を取得
21 tr_list = soup.find_all('tr')
22 for tr in tr_list:
23 # table要素内にtacクラスの要素がある場合に実行(要は1番目のtr要素の除外)
24 if tr.find_all(class_='tac'):
25 # 大まかにtr要素内の全ての文字列を取得・分割しておく
26 td_list = [td.text for td in tr.find_all('td')]
27 # 氏名を漢字表記とルビ表記で分割
28 # 氏名欄にimg要素が含まれていた場合には名前の先頭に目印として○をつける
29 if tr.find('img'):
30 names = td_list[1].split('\n')
31 names[0] = '○' + names[0]
32 else:
33 names = td_list[1].split('\n')
34 # 正規表現のパターンが一致した場合に郵便番号の取得
35 zipcode = re.search(郵便番号パターン, td_list[4]).group()
36 # 正規表現のパターンが一致した場合に電話番号の取得
37 # (電話番号を載せていないケースもある為try文を使用する)
38 try:
39 telephone = re.search(電話番号パターン, td_list[4]).group()
40 locate = td_list[4].replace('\t','').replace('\n', '').replace(zipcode, '').replace(telephone, '')
41 # 電話番号を掲載していなかった場合の処理
42 except:
43 telephone = 'NO TEL'
44 locate = td_list[4].replace('\t','').replace('\n', '').replace(zipcode, '')
45
46 print('電話番号:', telephone)
47 print('郵便番号:', zipcode)
48 print('住所:', locate)
49 print('氏名:', names)
50 print('='*20)
51 print('='*50)
52 # ページ内に[次のページ]を表す[>]が存在するかを判定
53 # 存在した場合Trueが返ってくる為、if文が実行される(hrefの値が更新され次のページにループする)
54 if soup.find(class_='pagebottom').find_all('a', attrs={'title':'next page'}):
55 # 取得したhrefは相対パスである為、16行目でウェブサイトURLと結合される
56 href = soup.find(class_='pagebottom').find('a', attrs={'title':'next page'}).get('href')
57 # 次のページが存在しなかった場合にはループが終了
58 else:
59 break
60 # 短時間に連続してリクエストを送る行為はマナー違反且つサーバーへの負荷となる為
61 # 標準ライブラリよりtimeモジュールを使用して1秒間隔でリクエストを送る様に調整
62 time.sleep(1)
63print('Done!')
データフレーム部に関しては引き続きご自身にてコーディングを行ってください。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/10/13 14:53
2020/10/13 15:41 編集
2020/10/15 03:05
2020/10/15 04:28
2020/10/15 04:35
2020/10/15 05:37