Pythonでスクレイピング(メモ5:クロール下準備)

←前回「Pythonでスクレイピング(メモ4:スクレイプ試行)」

こんばんは。

Pythonでスクレイピングするまでの試行錯誤メモです。

今回は、あるページ内の全てのリンク先でスクレイピングを行う「クロール」奮闘メモです。

想定した通りに実行するようコードを書くのに大変苦労しました。

当初はクロールまでのメモのつもりが、下準備だけで大量になってしまいました笑

※動作環境

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ページ以降中心)

※参考URL

https://note.nkmk.me/python-scrapy-tutorial/

以下試行錯誤メモ。

リンク先でスクレイピング(クロール)させるコードを書く前に、そのリンク先でのスクレイピングコードをまずは確定させます。

つまりリンク先でのソースからhtmlのタグを指定します。

試行ページはhttp://www.jra.go.jp/datafile/seiseki/replay/2018/jyusyo.htmlのリンク先の一つであるhttp://www.jra.go.jp/datafile/seiseki/replay/2018/001.htmlでやってみました。

出走馬の単勝人気と最終オッズを知りたいので、そこに該当するソースを探す。

単勝人気に関する情報があるソース箇所は以下。

<table class="mainList">
・・・
 <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="ninkiCol">1</td>
 </tr>

 <tr class="evenRow">
  <td class="chakuCol"> 2</td>
・・・(先と同様の情報)
  <td class="ninkiCol">2</td>
 </tr>

・・・(3着以降の情報)

</table>

とりあえず<td class=”ninkiCol“>に単勝人気がある。

(単勝人気だけなら)それを含むタグは<table class=”mainList“>で良いですかね。

最終オッズに関する情報があるソース箇所は以下。

<div class="haraimodoshiListTitle">払戻金</div>
<div class="haraimodoshiOutDiv">
 <div class="haraimodoshiCellDiv">
  <table class="haraimodoshiList">
   <tr>
    <th scope="row">単勝</th>
    <td class="umabanCol singleCell">6</td>
    <td class="haraimodoshiCol singleCell">250円</td>
    <td class="ninkiCol singleCell">1番人気</td>
   </tr>

   <tr>
    <th rowspan="3" scope="row">複勝</th>
    <td class="umabanCol compositeCellTop">6</td>
    <td class="haraimodoshiCol compositeCellTop">130円</td>
    <td class="ninkiCol compositeCellTop">1番人気</td>
   </tr>

・・・(他のオッズ情報)

  </table>
 </div>
</div>

オッズは<td class=”haraimodoshiCol compositeCellTop“>の中にある。

(オッズだけなら)それを含むタグは<div class=”haraimodoshiOutDiv“>で良さそうです。

ただ人気もオッズも両方同時に欲しい。

人気の方は厳密には<tr class=”oddRow“>ですが、<tr>で一度試してみました。

お試しはscrapy shell <URL>でしたね!→メモ4:スクレイピング試行

In [1]: keiba = response.css('tr')
In [2]: ninki = keiba.css('td.ninkiCol::text').extract_first()
In [3]: odds = keiba.css('td.haraimodoshiCol::text').extract_first()
In [4]: print([ninki, odds])
['1', '250円']

あれ?駄目だったはずなんだが・・・

実はこのメモは事後で書いているので、記憶をたどってメモしてるんですが当時は出来なかったような・・・

.extract_first()のfirstは始めのやつのみなので、取り払って改めてやってみる。

In [5]: ninki = keiba.css('td.ninkiCol::text').extract()
In [6]: odds = keiba.css('td.haraimodoshiCol::text').extract()
In [7]: print([ninki, odds])
[['1', '2', '10', '4', '3', '9', '6', '14', '15', '5', '12', '7', '13', '8', '11', '17', '16',
 '1番人気', '1番人気', '2番人気', '10番人気', '1番人気', '1番人気', '1番人気', '13番人気', '19番人気', '1番人気', '13番人気', '38番人気'],
 ['250円', '130円', '170円', '540円', '540円', '680円', '310円', '1,380円', '1,750円', '1,100円', '5,270円', '18,170円']]

余計な情報があるけどまあいけるな・・・記憶違いだったか?

念の為以下のようなコードを書いた.pyでやってみる。

# -*- coding: utf-8 -*-
import scrapy

class Keiba2Spider(scrapy.Spider):
    name = 'keiba2'
    allowed_domains = ['jra.go.jp']
    start_urls = ['http://www.jra.go.jp/datafile/seiseki/replay/2018/001.html']

    def parse(self, response):
        for keiba2 in response.css('tr'):
            ninki = keiba2.css('td.ninkiCol::text').extract()
            odds = keiba2.css('td.haraimodoshiCol::text').extract()
        print([ninki, odds])

これを実行した結果・・・

[['38番人気'], ['18,170円']]

そうだ!確か最後しか得られなくて困ってたんだった!

ただ今更気づいてしまったんだが、この要因はprint([ninki, odds])の位置ではないのか?

printがfor文の中にないからでは?

ということで先程のコードのprintをtabキーで修正して再度実行した結果・・・

[[], []]
[[], []]
[[], []]
[[], []]
[['1'], []]
[['2'], []]
[['10'], []]
[['4'], []]
[['3'], []]
[['9'], []]
[['6'], []]
[['14'], []]
[['15'], []]
[['5'], []]
[['12'], []]
[['7'], []]
[['13'], []]
[['8'], []]
[['11'], []]
[['17'], []]
[['16'], []]
[[], []]
[[], []]
[[], []]
[[], []]
[[], []]
[[], []]
[['1番人気'], ['250円']]
[['1番人気'], ['130円']]
[['2番人気'], ['170円']]
[['10番人気'], ['540円']]
[['1番人気'], ['540円']]
[['1番人気'], ['680円']]
[['1番人気'], ['310円']]
[['13番人気'], ['1,380円']]
[['19番人気'], ['1,750円']]
[['1番人気'], ['1,100円']]
[['13番人気'], ['5,270円']]
[['38番人気'], ['18,170円']]

いらない情報も多いけどいけた笑

ただいらない情報は無駄なので改善しましょう。

要は欲しい場所意外にも<tr>タグがあるわけで、タグの指定の仕方によってはそもそもfor文でループする必要も無いんじゃないかと。

ならもっと大きく包含するタグ、<body>にしてはどうでしょう。

ってことで改善したコードが以下。

import scrapy
from keiba_scraper.items2 import KeibaScraperItem

class Keiba2Spider(scrapy.Spider):
    name = 'keiba2'
    allowed_domains = ['jra.go.jp']
    start_urls = ['http://www.jra.go.jp/datafile/seiseki/replay/2018/001.html']

    def parse(self, response):
        keiba2 = response.css('body')
        item = KeibaScraperItem()
        item['ninki'] = keiba2.css('td.ninkiCol::text').extract()
        item['odds'] = keiba2.css('td.haraimodoshiCol::text').extract()
        yield item

実行した結果・・・

{'ninki': ['1',
           '2',
           '10',
           '4',
           '3',
           '9',
           '6',
           '14',
           '15',
           '5',
           '12',
           '7',
           '13',
           '8',
           '11',
           '17',
           '16',
           '1番人気',
           '1番人気',
           '2番人気',
           '10番人気',
           '1番人気',
           '1番人気',
           '1番人気',
           '13番人気',
           '19番人気',
           '1番人気',
           '13番人気',
           '38番人気'],
 'odds': ['250円',
          '130円',
          '170円',
          '540円',
          '540円',
          '680円',
          '310円',
          '1,380円',
          '1,750円',
          '1,100円',
          '5,270円',
          '18,170円']}

いけましたね。

さらに「〇〇番人気」はオッズの人気順なんですが要らないので更に改善を。

さすがに<body>タグは大きく取りすぎているのと、単勝人気とオッズを別々に分ければどうか。

先程のソースも踏まえて改善したコード(一部略)が以下。

(前は先と同様)
    def parse(self, response):
        keiba = response.css('table.mainList')
        keiba2 = response.css('div.haraimodoshiOutDiv')
        item = KeibaScraperItem()
        item['ninki'] = keiba.css('td.ninkiCol::text').extract()
        item['odds'] = keiba2.css('td.haraimodoshiCol::text').extract()
        yield item

実行した結果・・・成功!!

先ほどの結果から「〇〇番人気」だけ取り除けました。

これでようやく、リンク先でのスクレイピングコードが完成しました。

次回は(本来は今回メモする予定だった)このコードを各リンク先で実行する、つまりクロールするようコードを作っていきます。

→次回「Pythonでスクレイピング(メモ6:クロールが出来るまで)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする