→Pythonでスクレイピング(メモ10:n番目指定・思わぬ落とし穴)
こんばんは。
Pythonでスクレイピング奮闘メモです。
今回は、余白や改行などにより/nや/rなどが出力される事案に対してどう対応したかをまとめました。
<この記事の目次>
・新たな目標と新たな問題・・・余計な文字列(\nなど)や情報を取り除きたい
・.replace()で文字列を置換・・・文字列を空白に置換することで不要な文字を取り除く
・リストから新たなリストを作成・・・リストから文字列を抽出して新たなリストを作る
・遡るとまたソースが違う・・・ソースが違うのでその都度勉強させてくれるJRAに感謝
※動作環境
macOS High Sierra 10.13.6
プロセッサ 2.7 GHz Intel Core i5
メモリ 8 GB 1867 MHz DDR3
Python 3.6.0 :: Anaconda custom (x86_64)
コマンド実行は「ターミナル」、.py編集は「Xcode」を使用
※参考文献「Python エンジニア ファーストブック」(142ページ以降中心)
以下試行錯誤メモ。
新たな目標と新たな問題
前回ひとつの案件が解決して、ようやく次に進めます。
今回は以下のソースからオッズのみをスクレイプするのが目標です。(元のページ:2017年1月5日開催 日刊スポーツ賞中山金杯:http://www.jra.go.jp/datafile/seiseki/replay/2017/001.html)
<table width="753" border="0" cellpadding="3" cellspacing="1" bgcolor="#BABABA"> <tr class="gray12"> <td bgcolor="#EEEDCE" valign="top">単勝</td> ・・・ <td align="right" bgcolor="#FFFFFF" valign="top"> 280円 </td> ・・・ <td bgcolor="#EEEDCE" valign="top">馬連</td> ・・・ <td align="right" bgcolor="#FFFFFF" valign="top"> 1,760円 </td> ・・・(以後同様) </tr> </table>
オッズ情報を包含しているタグは、よりタグに関する情報が多い<table width=”753” border=”0” cellpadding=”3” cellspacing=”1” bgcolor=”#BABABA“>を使えば他とも被らない。
alignなどの情報が複数の場合は下記のように[ ]を続ければOK。
table[width="753"][border="0"][cellpadding="3"][cellspacing="1"][bgcolor="#BABABA"]
そしてそのタグ内の<td align=”right“>タグを指定すれば良さそう。
まずはデバッグにて。
In [1]: keiba = response.css('table[width="753"][border="0"][cellpadding="3"][cellspacing="1"][bgcolor="#BABABA"]') In [2]: odd = keiba.css('td[align="right"]::text').extract() In [3]: print(odd) ['\n\n280円\n', '\n\n1番人気\n', '\n\n1,760円\n', '\n\n7番人気\n' , ・・・ , '\xa0', '\xa0', '\xa0']
余計な情報は多いものの、思惑通りの結果。
・・・と言いたいところだが、\nや\xa0が気になる。
一旦気にせずクロールしてcsvファイルにて出力すると・・・\nのせいで改行されてしまうので使えない。
それともう一つ。やはり余計なものはいらない。
ただし前回と違うのは、必要な情報が規則正しく循環しているわけではない!
例えばワイドは、オッズ1→オッズ2→オッズ3→人気1→人気2→人気3の順。
そのため前回の策も使えず・・・ぐぬぬ。
とりあえず参考文献に書いてある対処法、.re()を使ってみる。
keiba.css('td[align="right"]::text').re(r'(.+)\n')
これで\nが取り除けるらしいので実際にやってみる。
In [4]: print(keiba.css('td[align="right"]::text').re(r'(.+)\n')) ['280円', '1番人気', '1,760円', '7番人気', '2,690円', '10番人気', '220円', '3番人気', '1,030円', '12番人気', '2,360円', '5番人気', '10,430円', '24番人気', '1,930円', '7番人気']
確かに取り除くことは出来た!これでいける!だがしかし!これはぬか喜びだった。
よく見たら情報が取り除かれすぎている。
例えばワイドの情報はオッズ3・人気3のみになっている。
他にも調べて色々試したが何をやっても上手く行かず途方に暮れる。
そこで以前アドバイスをもらった友人(メモ6:クロールが出来るまで)にもう一度アドバイスを求めました。
すると友人から「置換だ!」の一言が笑
ということで、「Python 置換」で検索。
.replace()で文字列を置換
まずは色々なページを見て「置換」という意味を把握。
なるほど、ある文字列の中の指定の文字を別の文字に変えることか。
つまり今回の場合は、不要なもの(’\n’など)を空白(”)に変えれば解決するということ。
「置換」のような専門用語的なものを知らなくて調べようがなくなる自分には目からウロコのアドバイスでした。
この場をお借りして友人に感謝。ありがとう!
では早速.replace()を使ってみる。
In [6]: odd = keiba.css('td[align="right"]::text').extract() In [7]: print(odd.replace('\n','')) --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-7-1a9f07d0a128> in <module>() ----> 1 print(odd.replace('\n','')) AttributeError: 'list' object has no attribute 'replace'
ところがどっこい、Errorを弾く。
多分listに.replace()は使えないよ、ってことでしょう。
ならこれならどうだ!
In [8]: odd = keiba.css('td[align="right"]::text').replace('\n','').extract() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-8-46611f9d458f> in <module>() ----> 1 odd = keiba.css('td[align="right"]::text').replace('\n','').extract() AttributeError: 'SelectorList' object has no attribute 'replace'
同じようなErrorを弾く。
ならリスト内の文字列に対して置換を実行したいと考え、「Python リスト 置換」で検索。
リストから新たなリストを作成
複数ページは見たが、https://note.nkmk.me/python-list-str-select-replace/が欲しい情報に一番近かった。
リストから新たなリストを作成できるみたいなので、試してみた。
In [10]: print([s.replace('\n', '') for s in odd]) ['280円', '1番人気', '1,760円', '7番人気', '2,690円', '10番人気', '120円', '260円', '220円', '1番人気', '5番人気', '3番人気', '550円', '370円', '1,030円', '5番人気', '2番人気', '12番人気', '2,360円', '5番人気', '10,430円', '24番人気', '1,930円', '7番人気', '\xa0', '\xa0', '\xa0']
完璧すぎた笑
しかもこれ、if文も使えるとのこと。
なら「円」を含むものだけで新たなリストも作成できるぞ。
In [11]: print([s.replace('\n', '') for s in odd if '円' in s]) ['280円', '1,760円', '2,690円', '120円', '260円', '220円', '550円', '370円', '1,030円', '2,360円', '10,430円', '1,930円']
ここまで見事に、一度に全てが解決できるとは・・・ありがたや。
遡るとまたソースが違う
以上ですべてが出揃い、ようやく2017年以前のページにも対応できる!
・・・いや待てよ、と。
変更があったのは本当に2017〜2018にかけてだけか?
念の為遡って調べてみると・・・
重賞一覧ページが2015〜2016にかけて変わっている(泣)
さらに結果ページは2006〜2007にかけて変わっている(泣)
ちくしょう!
次回はその対応メモに確定しました笑→「Pythonでスクレイピング(メモ10:n番目指定・思わぬ落とし穴)」
どんだけ勉強させてくれるんだJRAよ笑
以下おまけ。
(おまけ)Excel上で値をまとめて100で割る
スクレイピングで得られたオッズは円単位なので、値をまとめて100で割って倍率にしたかったため調べたらこのページ(https://incloop.com/%E3%82%A8%E3%82%AF%E3%82%BB%E3%83%AB%E5%80%A4%E3%82%92%E4%B8%80%E6%8B%AC%E3%81%97%E3%81%A61000%E3%81%A7%E5%89%B2%E3%82%8B/#1000)がヒット。
Excelの「形式を選択して貼り付け」って便利ですね。