前提・実現したいこと
python2.7での以下のサイトのスクレイピングに挑戦しています。
診療科目「内科・小児科」の市区町村別の表をcsvで書き出すのが目的です。
http://medinf.mmic.or.jp/kensaku/kensaku_menu2.php?mode=consul
発生している問題・エラーメッセージ
上のURLに、チェックの入っている市区町村のinputタグのnameとvalueを辞書型にしてrequest.postで送信すると検索結果のページになりました。
情報が少ない市区町村のデータは一応書き出すことには成功したのですが、25件を超える市区町村だと1ページ目の情報しか書き出されません。
GoogleChromeの「検証」で「次の25件」をクリック後のページを調べてみると、また別のパラメータがPOSTで送られているようです。
ならば1ページ目の情報を書き出したあとに、もう一度request.postでそのパラメータを送信すればどうかと思ったのですがうまく行かずエラーページが出てきてしまいます。
該当のソースコード
python
1#coding:cp932 2 3import requests 4from bs4 import BeautifulSoup 5from ast import literal_eval 6import csv 7import codecs 8import re 9from time import sleep 10 11#グローバル変数 12#サイトURL 13url="http://medinf.mmic.or.jp/kensaku/kensaku_kekka2.php?mode=consul" 14 15#抽出する市区町村をグローバル変数に 16chiiki="青葉区" 17 18#保存場所 19fpath='デスクトップのパスを入れています' 20 21def main(): 22 23 #市区町村コードげと 24 c=area_select(chiiki) 25 26 data={ 27 'chkbox1[0]': '1', #内科 28 'chkbox1[6]': '9', #小児科 29 } 30 31 data.update(c) 32 response=requests.post(url,data) 33 34 soup=BeautifulSoup(response.text,"html.parser") 35 nextflg=soup.find_all('input') 36 37 table2csv(soup) #ここまでは一応成功します 38 39 for s in nextflg: 40 if 'all_rows' in str(s): 41 n=soup.find('input',attrs={'name':'all_rows'})['value'] 42#まずはパラメータをそのまま入れたのですが失敗します 43 nextgo={'mode':'page','offset':'25','all_rows':'196', 44 'coment':'診療科目から探す', 45 'precondition':'内科、小児科 & '+chiiki, 46 'repair_mode':'consul'} 47 nextres=requests.post(url,nextgo) 48 soup=BeautifulSoup(nextres.text,"html.parser") 49 print(soup.prettify('shift-jis'))#エラーページが出ます 50 table2csv(soup) 51 52def table2csv(soup): 53 #4つ目のテーブルに処理を行う 54 table=soup.findAll("table",{"class":""})[3] 55 #print (table) 56 rows=table.findAll("tr") 57 58 with open(fpath+"/"+chiiki+".csv","wt") as file: 59 writer=csv.writer(file) 60 for row in rows: 61 csvrow=[] 62 63 for cell in row.findAll(['td','script','th']): 64 65 if "script" in cell.name: 66 ad=address_get((cell).prettify('shift-jis')) 67 csvrow.append(ad) 68 else: 69 csvrow.append(cell.get_text().encode('cp932')) 70 writer.writerow(csvrow) 71 72 #変なコンマ等が入っていて直せなかったので置換しています 73 with open(fpath+"/"+chiiki+".csv") as file: 74 data_lines=file.read() 75 76 data_lines = data_lines.replace('\n', '') 77 data_lines = data_lines.replace(',"",', ',') 78 79 with open(fpath+"/"+chiiki+".csv","w") as f: 80 f.write(data_lines) 81 82def area_select(chiiki): 83 areas={ 84 "青葉区":["chkbox100[0]",'1'], 85#省略 86#1ページのみで成功した町 87 "松島町":["chkbox100[10]",'11'], 88 } 89 90 #地域にチェック入れて返す 91 idx=dict(zip(areas[chiiki][0::2],areas[chiiki][1::2])) 92 return idx 93 94def address_get(str): 95 address_code='$adrsname=\".*\"' 96 adrs=re.search(address_code,str) 97 adrs=adrs.group().replace('$adrsname="','') 98 return adrs 99 100if __name__ == '__main__': 101 main()
試したこと
原因として考えたのが、
1.そもそも一度request.postしたページに再度request.postしても次のページは開けない
2.'precondition':'内科、小児科 & '+chiiki,の文字コードがサイトと合ってなくて文字化けしたパラメータが渡されている
3.その他
1.についてはできそうだと思ったのですが確信が持てず
2.文字コードをshift-jisにしてから送信してはと思ったのですが方法がわかりませんでした
エラーページが出てきてしまうので、python上のエラーとしてはtable=soup.findAll("table",{"class":""})[3]の部分でout of rangeが出るくらいで原因の特定が出来ず困っています…
補足情報(FW/ツールのバージョンなど)
python 2.7
windows10
######回答をいただいた後にseleniumを使って作った修正版
KUROROさんに作っていただいたものとは別に自分でも作っていたので、対比として載せておきます。作っていただいたものと並べて見ることで、「こんな方法があるのか」「こう書けばすっきりするのか」ととても勉強になりましたので、自分と同じくらいのレベルであがいている人の役には立つのではないかと考えました。
python
1#coding:cp932 2 3import time 4from selenium import webdriver 5import chromedriver_binary 6from bs4 import BeautifulSoup 7import csv 8import codecs 9import os 10 11#グローバル変数 12#サイトURL 13url='http://medinf.mmic.or.jp/' #トップページ 14 15#抽出する市区町村 16chiiki="青葉区" 17 18#保存場所 19#fpath='デスクトップパス' 20fpath=os.path.expanduser('~/Desktop') 21 22def main(): 23## try: 24 #市区町村コードげと 25 c=area_select(chiiki) 26 27 driver=webdriver.Chrome() 28 driver.get(url) 29 time.sleep(1) 30 31#この辺はdriver.find〜by_なんとか().clickと書いて良いのを知りませんでした… 32 #検索ページに移動 33 kensakuP=driver.find_element_by_link_text(u"診療科目と所在地") 34 kensakuP.click() 35 36 #内科 37 naika=driver.find_element_by_name("chkbox1[0]") 38 naika.click() 39 40 #小児科 41 shounika=driver.find_element_by_name("chkbox1[6]") 42 shounika.click() 43 44 #地域 45 area=driver.find_element_by_name(c) 46 area.click() 47 48 #検索ボタン 49 kensakuB=driver.find_element_by_xpath("/html/body/div/table[2]/tbody/tr[8]/td/input[1]") 50 kensakuB.click() 51 52 #次の25件が出なくなるまで繰り返し 53 while True: 54#driver.find_elements(By.TAG_NAME, "tbody")で取得すればよいのがわからなかったので前回のを使っています 55#そのせいで各ページの表の見出しが繰り返し取得されてしまっています(要修正部分) 56 source=driver.page_source 57 soup=BeautifulSoup(source,"html.parser") 58 59 table2csv(soup) 60 61 #次へトライ 62 try: 63 nextgo=driver.find_element_by_xpath("/html/body/div/table[3]/tbody/tr/td[3]/input[1]") 64 nextgo.click() 65 time.sleep(3) 66 except Exception: 67 break 68 finally: 69 driver.quit 70 71 #直せないから置換する 72 with open(fpath+"/"+chiiki+".csv") as file: 73 data_lines=file.read() 74 75 data_lines = data_lines.replace('\n', '') 76 data_lines = data_lines.replace('",,', '",') 77 78 with open(fpath+"/"+chiiki+".csv","w") as f: 79 f.write(data_lines) 80def area_select(chiiki): 81 areas={ 82#値をリストではなく文字列に変更 83 "青葉区":"chkbox100[0]", 84#省略 85 } 86 #地域コード返す 87 idx=areas[chiiki] 88 return idx 89 90def table2csv(soup): 91 #テーブル 92 table=soup.findAll("table",{"class":""})[3]#.prettify('shift-jis') 93#省略 94#if "script" in cell.name:の条件分岐部分をまとめて削除
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/05/21 02:35
退会済みユーザー
2021/05/21 07:44
2021/05/21 13:15
退会済みユーザー
2021/05/21 20:21
2021/05/24 00:58