←Pythonでスクレイピング(メモ6:クロールが出来るまで)
こんばんは。
Pythonでスクレイピング奮闘メモです。
今回は、前回完成したコードを使っていたところ、古いページはソースが変わっていてスクレイピング出来なかったので、その対応をまとめたものです。
<この記事の目次>
・ソースの違いにより問題発生・・・古いページはソースが全然違った
・タグの属性指定方法・・・タグ内のalign=”center”属性指定の書き方
・CSSとXPathの対応・・・参考コードがXPathだったのでCSSではどう書くのか
・Google ChromeでCSSセレクターを生成・・・色々調べたら辿り着いた
※動作環境
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ページ以降中心)
以下試行錯誤メモ。
ソースの違いにより問題発生
実際にどの程度違うか比較します。
2018年のソース(2018年1月6日開催 日刊スポーツ賞中山金杯:http://www.jra.go.jp/datafile/seiseki/replay/2018/001.html)
<tr class="oddRow"> <td class="chakuCol"> 1</td> <td class="wakuCol"><img src="/JRADB/img/keiba/waku3.gif" alt="枠3赤" class="iconWakuImage"></td> <td class="umabanCol"> 6</td> <td class="umameiCol">セダブリランテス</td> <td class="seireiCol">牡4</td> <td class="hutanCol">55.0</td> <td class="kigouCol"></td> <td class="jocCol"> 戸崎圭太</td> <td class="timeCol">1:59.8</td> <td class="chakusaCol"></td> <td class="suiteiCol">35.0</td> <td class="bataiCol">522</td> <td class="zougenCol">+8</td> <td class="choukyoCol"> 手塚貴久</td> <td class="ninkiCol">1</td> </tr>
2017年のソース(2017年1月5日開催 日刊スポーツ賞中山金杯:http://www.jra.go.jp/datafile/seiseki/replay/2017/001.html)
<tr bgcolor="#FFFFFF"> <td align="center" nowrap class="gray12"> 1</td> <td align="center"><img src="/JRADB/img/keiba/waku3.gif" alt="3" width="16" height="22"></td> <td align="center" class="gray12"> 3</td> <td align="left" class="gray12"> ツクバアズマオー</td> <td align="center" class="gray12">牡6</td> <td align="center" class="gray12">56.5</td> <td width="11" align="center" class="gray12"></td> <td width="75" align="left" class="gray12"> 吉田豊</td> <td align="center" class="gray12">2:00.6</td> <td align="left" class="gray12"> </td> <td align="center" class="gray12">35.6</td> <td width="39" align="center" class="gray12">480</td> <td width="40" align="center" class="gray12"> 0</td> <td align="left" class="gray13"> 尾形充弘</td> <td align="center" nowrap class="gray12">1</td> </tr>
見比べてみると、各<tr>タグに着順で情報が入っているのは変わらないです。
問題はその情報を入れている<td>タグが違うこと。
2018年はclassが違うことで欲しい情報のタグ指定が単純でした(どれも’td.<class名>::text’で良かった)。
ところが2017年のclassはほぼ”gray12″なので、タグ指定を「’td.gray12::text’」とすると余計な情報もスクレイプしてしまいます。
例えばデバッグしてみると・・・
In [3]: keiba = response.css('td.gray12::text').extract() In [4]: print(keiba) ['サラ系4歳以上', '2000m', '芝・右,・・・(不要な情報が続く)・・・, '1'(欲しい情報),・・・(不要な情報が続く)・・・, '6'(欲しい情報),・・・(以後同様)]
これでは駄目ですね。
ただ<td>タグ内にalign=”center“という情報があるので、それを追加すればもう少しマシになりそうなので、指定方法を調べました。
タグの属性指定方法
ということで、「scrapy td align=”center”」でグーグル検索。
すると英語ですがトップに出てきたのがこのページ:https://stackoverflow.com/questions/16440619/scrapy-getting-only-td-elements-with-align-right
そこに載っていたソースの太字部分が解決策そのものでした笑
hxs.select('//tbody//td[@ALIGN="RIGHT"]//text()').extract()
だがしかし!これはxpathを使っています。
当面自分はCSSセレクターのみでやっていきたいので、CSSとXPathの対応を調べました(後述)。
<td align=”center” nowrap class=”gray12“>を指定するコード。
XPath:.//td[@align="center"][@class="gray12"]/text() CSS:td[align="center"].gray12::text
これを使ってデバッグすると結果大分減りました(せっかくなのでXPath使ってみました)。
In [5]: keiba = response.xpath('//td[@align="center"][@class="gray12"]/text()').extract() In [6]: print(keiba) [・・・(不要な情報8つ)・・・, '1'(欲しい情報), ・・・(不要な情報8つ)・・・, '6'(欲しい情報), ・・・(以後同様)・・・, '13'(欲しい情報)]
ここからかなり苦戦しました。
欲しい情報の更に詳しいソースを見る方法なども調べると出てきたので試しましたが、上手くいきませんでした(後述)。
なので諦めて別の解決策を探りました。
詳しくは次回のメモとします。→次回「Pythonでスクレイピング(メモ8:for文を使う)」
ヒントは不要な情報と欲しい情報が一定の個数で繰り返されている点です。
CSSとXPathの対応
「scrapy cssセレクタ」でグーグル検索。
するとトップで出てきたのがこのページ:https://python.civic-apps.com/scrapy-xpath-css/
そこに対応表がありました。解決!笑
参考文献にもいくつかありました。
XPathの方が細かい指定ができるようですが、今回のようにほとんどタグの形式が同じ場合は意味ないのでしょうか。
全然使いこなせていません笑
Google ChromeでCSSセレクターを生成
いろいろ調べてみると、このページに行き着きました:https://qiita.com/Azunyan1111/items/b161b998790b1db2ff7a
Google ChromeがCSSセレクターを生成してくれるとのこと。
例えば件の箇所のCSSセレクターを生成すると以下が得られる。
body > table > tbody > tr:nth-child(301) > td.line-content
それを使ってみる。
In [24]: ninki = response.css('body > table > tbody > tr:nth-child(301) > td.line-content::text').extract() In [25]: print(ninki) []
・・・
全然駄目でした。笑
BeautifulSoupとかいうhtmlを解析するやつを使おうとしましたがよく分からず・・・
もう嫌になってきたので先述したように別の解決策を探ることにしたのでした。
ということで次回はその解決に向けた奮闘メモになります。