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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

POST

POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1116閲覧

POST送信で生成するサイトの次のページを取得したい

pia

総合スコア14

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

POST

POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/05/20 08:29

編集2021/05/21 13:05

前提・実現したいこと

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:の条件分岐部分をまとめて削除

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

おはようございます。

問題文読ませていただきました。

自分の方でスクレイピングを作成しましたので、以下の手順にてご確認いただけますか?

  1. こちらのページへアクセスする。
  2. 緑の「Code」ボタンをクリックする。
  3. 「Download ZIP」ボタンをクリックする。
  4. ダウンロードしたZipファイルをデスクトップへ移動する。
  5. Zipファイルをダブルクリックして展開する。
  6. ターミナルを開く。
  7. internal-medicine-scriping-master ディレクトリ内へ移動する。cdコマンドを利用する。
  8. pip install -r requirements.txtを実行する。(main.pyを実行する際に必要なパッケージをインストールする。)
  9. python main.pyを実行する。

もしかすると、selenium周りでエラーが出るかもです。

ご確認のほど、よろしくお願いいたします。????‍♂️

投稿2021/05/20 21:53

編集2021/05/21 07:42
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pia

2021/05/21 02:35

回答ありがとうございます。seleniumを使う方法も最初に少しだけ挑戦してみたのですが、getではなくpostでページを取得する方法がわからなかったので、諦めてしまっていました。今度はもっとよく調べてみます。
退会済みユーザー

退会済みユーザー

2021/05/21 07:44

お疲れ様です。回答の更新を行いました。 ご確認いただけますと幸いです。????‍♂️
pia

2021/05/21 13:15

わざわざ作っていただいてしまい申し訳ないです…。ありがとうございます。 気づくのが遅くて会社のwindows10で動かす時間がなかったので、自宅のMacで動作確認しようと思ったのですが、OSが古いせいかseleniumと関係ないところでエラーが出ていますので、週明けに会社で動かしてみます。 (実はMacにpythonが入っていることも今日初めて知りました…) seleniumでのテーブルの取得のやり方や、全ページの情報をまとめておいてcsvに書き込むやり方などとても勉強になりました。
退会済みユーザー

退会済みユーザー

2021/05/21 20:21

いえいえ! また困ったこととかありましたら、気軽にご相談ください〜!!
pia

2021/05/24 00:58

おはようございます。早速試したのですが、エラーが出るので調べてみたところwebdriver_managerというのが3.6以上でないと対応していないようでした。 ですので確認はできなかったのですが、テーブルのところ等を自分のコードに活かさせていただこうと思います。ありがとうございました!
guest

0

原因として考えたのが、
1.そもそも一度request.postしたページに再度request.postしても次のページは開けない

はい。これが原因です。

記載されているページで検索してみました。

診療科目「内科・小児科」の市区町村別の表をcsvで書き出すのが目的です。
http://medinf.mmic.or.jp/kensaku/kensaku_menu2.php?mode=consul

調べてみると、 25件以上のコンテナを得るには引数 "offset" へ、任意の値をセットしてから Webサーバーへ問い合わせ (submit) する仕様になっていますね

以下はボタンに ”次の25件を表示" と表示されているときのページの状態です。
引数 offset へ 値 "25" がセットされるようになっています。

<input type="hidden" name="offset" value="25">

イメージ説明

ならば1ページ目の情報を書き出したあとに、もう一度request.postでそのパラメータを送信すればどうかと思ったのですがうまく行かずエラーページが出てきてしまいます。

これは、この検索サイトが、ステートフルな通信になるように、つくられているからです。
よくある仕様で、セッション管理をしているので前のページの状態は、サーバー側で把握している状態になっていると思います。
なので、クライアント側のWebブラザーではURLは変わっていないけど、クライアントへ送られてくるレスポンスには(サーバー側が、現在のクライアント側を把握できているので)次のページが送られてきています。

投稿2021/05/20 08:56

編集2021/05/20 09:13
Yoshi88

総合スコア623

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pia

2021/05/21 02:29

回答ありがとうございます。サーバー側は「今は検索ページを表示しているところ」「今は1ページ目を開いているところ」みたいな情報を持っているけど、こちらからそれをどうこうすることは出来ないので、POSTだけ真似しても無駄という理解で合ってるでしょうか。 残念です…
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問