# PythonによるWebサイトの自動検索-2
# 国土交通省の「ネガティブ情報等検索サイト」から、一定期間に行政処分を受けた建設業者の情報を収集し、Excelに保存する
# 必要なモジュールを呼び出す
import requests
from bs4 import BeautifulSoup
from datetime import datetime, date, time , timedelta
from dateutil.relativedelta import relativedelta
import os
import pandas as pd
from openpyxl import load_workbook
# 検索結果から情報を抽出したときに格納するリスト
rs_url = [] # ネガティブ情報の「処分詳細URL」
rs_syobun = [] # ネガティブ情報の「処分の内容(詳細)」
rs_riyuu = [] # ネガティブ情報の「処分の原因となった事実」⇒「処分理由」と呼ぶ
# 検索結果を出力するディレクトリ「dir」を指定する
dir = r'C:\Users\......\......\......\......\......'
# 「ネガティブ情報」を検索する検索開始月と検索期間(月数)を設定する
kaisi = '0' # 検索開始月
while len(kaisi) != 6:
kaisi = input('「ネガティブ情報」を検索する開始月を数字6桁で入力してください yyyymm 形式 :')
kikan = 0 # 検索期間(月数)
while kikan<1 or kikan>6:
kikan = int(input('「ネガティブ情報」を検索する期間(月数)を1~6の範囲で入力してください :'))
# 検索に利用する様々な日付を作成する
fy = kaisi[0:4] # 検索開始年
fm = str(int(kaisi[4:6])) # 検索開始月
fmz = fm.zfill(2) # 検索開始月が1桁の場合は前ゼロを付けて2桁に揃える
ft = date(int(fy),int(fm),1) # 検索開始年月(1日)
tt = ft + relativedelta(months=kikan-1) # (kikan-1)月後の年月(1日)
ty = str(tt.year) # 検索終了年
tm = str(tt.month) # 検索終了月
tmz = tm.zfill(2) # 検索終了月が1桁の場合は前ゼロを付けて2桁に揃える
# 「ネガティブ情報等検索サイト」のドメインのURL「url0」を指定する
url0 = 'https://www.mlit.go.jp/nega-inf/cgi-bin/'
# 「ネガティブ情報等検索サイト」の検索条件を入れたURL「url」を生成する
url = (url0 + 'search.cgi?'
+'jigyoubunya=kensetugyousya' # 建設業者
+'&'+'EID=search'
+'&'+'start_year='+fy+'&'+'start_month='+fm # 検索開始年月
+'&'+'end_year='+ty+'&'+'end_month='+tm # 検索終了年月
+'&'+'disposal_name1='+'&'+'disposal_name2='
+'&'+'reason_con=1'
+'&'+'reason1='+'&'+'reason2='+'&'+'reason3='
+'&'+'shobun='
+'&'+'address='
+'&'+'agency='
)
# 「url」の検索リクエストを送り、検索結果のうち総件数を「r_ken」に格納し、数値としての総件数を「n」とする
r = requests.get(url)
r.raise_for_status()
soup = BeautifulSoup(r.content,'html.parser')
r_ken = soup.find_all(class_='title')
n = int(r_ken[0].text.replace('検索結果:','').replace('件',''))
# 検索結果は、表形式で表示されるので、データフレーム形式で収集して「df」にまとめていく
flag = '0'
p = 0 # 検索結果のページ番号
# 1ページ10件ずつ表示されるので、総件数がn件になるまで「url」にページ番号を付け10件ずつ検索結果を呼び出して収集する
while flag != '1':
p += 1 # ページ番号「p」を1つ増やす
urls = url+'&'+'page='+str(p) # 「url」にページ番号を付けた「urls」を生成する
dfm = pd.read_html(urls,header=0,index_col=None) # 表形式の検索結果を「dfm」に格納する
if p == 1:
df = dfm[0] # 1ページ目(最初の10件)は「dfm」をそのまま「df」とする
else:
df = pd.concat([df,dfm[0]]) # 2ページ目(次の10件)以降は「dfm」を「df」に追記する
# 「urls」の検索リクエストを送り、検索結果のうち「処分詳細」欄にリンクされているURLの情報を10件まとめて「rs_urls」に格納する
r = requests.get(urls)
r.raise_for_status()
soup = BeautifulSoup(r.content,'html.parser')
rs_urls = soup.find_all(class_='overview')
# 「rs_urls」に含まれるURL情報を1件ずつ取り出し、ドメインを付加して「処分詳細URL」の「rsg_url」として生成し、「rs_url」にリスト化する
for i in range(len(rs_urls)):
rsg_url = url0 + rs_urls[i].get('href') # ドメイン「url0」に属性名「href」で取得したディレクトリを付加
rs_url += [rsg_url] # 「処分詳細URL」のリスト
# 「rsg_url」により表示される詳細情報は表形式だが、データフレームとしては適さない形なので、
# 「rsg_url」の検索リクエストを送り、検索結果のうち「処分詳細」の内容を1件分まとめて「rs_texts」として取得し、
# そのうちテキスト部分を「rs_text」として取得する
rs = requests.get(rsg_url)
rs.raise_for_status()
soup = BeautifulSoup(rs.content,'html.parser')
rs_texts = soup.find_all(class_='overview__list')
rs_text = rs_texts[0].text
# 「rs_text」のうち「処分の内容(詳細)」を切り分けて「rs_syobun」としてリスト化する
t1 = rs_text.find('処分の内容(詳細)')+len('処分の内容(詳細)') # 「処分の内容(詳細)」開始位置
t2 = rs_text.find('処分の原因となった事実') # 「処分の内容(詳細)」終了位置の1つ先
rs_syobun += [rs_text[t1:t2]] # 「処分の内容(詳細)」のリスト
# 「rs_text」のうち「処分の原因となった事実(=処分理由)」を切り分けて「rs_riyuu」としてリスト化する
t3 = t2+len('処分の原因となった事実') # 「処分の原因となった事実」開始位置
t4 = rs_text.find('その他参考となる事項') # 「処分の原因となった事実」終了位置の1つ先
rs_riyuu += [rs_text[t3:t4]] # 「処分理由」のリスト
# 「df」の行数が総件数「n」になった段階で while~文を脱する
if df.shape[0] >= n:
flag = '1'
# 「df」から「処分詳細」の列を削除する(リンク情報を取り出した後は有益な情報がないため)
df = df.drop('処分詳細',axis=1)
# 「df」に新しい列として「処分の内容(詳細)」「処分理由」「詳細URL」を設け、収集してリスト化した各データを追加する
df.insert(5,'処分の内容(詳細)',rs_syobun,True)
df.insert(6,'処分理由',rs_riyuu,True)
df.insert(7,'詳細URL',rs_url,True)
# 「df」をExcelファイルとして出力する
nam = '国交省ネガティブ情報'+fy+fmz+'~'+ty+tmz+'.xlsx'
path = os.path.join(dir,nam)
df.to_excel(path,sheet_name='建設業者',index=False)
# Excelシートの列幅を修正する
col = [['A',20], # 列番号(アルファベット)と列幅の組合せをリスト化
['B',12],
['C',14],
['D',14],
['E',10],
['F',20],
['G',20],
['H',20]
]
wb = load_workbook(path)
ws = wb.active
for i in range(len(col)):
ws.column_dimensions[col[i][0]].width = col[i][1]
wb.save(path)
# 実行が成功したら結果を画面表示する
print('パス '+path+' でファイルを保存しました')