こんばんは。
Pythonでスクレイピングするまでの試行錯誤メモです。
今回は、あるページ内の全てのリンク先でスクレイピングを行う「クロール」奮闘メモです。
<この記事の目次>
・クロール実行コードの書き方・・・コードの書き方が分からない
・詰まったらprint関数で挙動確認・・・実際にスクレイプで得た値を見てみると良い
・リンク先URLを正規の形で渡す・・・URLはhttp(s):〜の形でないと駄目みたい
・値をcsvで出力・・・説明不要
・出力したcsvのExcelでの開き方・・・普通に開くと文字化けする・同一セル内に値が詰められている
・指定のリンク先がない場合の条件分岐・・・リンク先が無くてErrorが返ることも
※動作環境
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ページ以降中心)
以下試行錯誤メモ。
クロール実行コードの書き方
さっぱり分からないのでGoogleで「scrapy クロール」検索。
いろいろ見ましたが
https://note.nkmk.me/python-scrapy-tutorial/
が一番参考になりました。
その中でクロールに関するコードを一部抽出すると以下。
def parse(self, response): for文 ・・・(略) yield scrapy.Request(名前.css(htmlタグ).extract(), callback=self.parse_detail, meta={'item': item}) def parse_detail(self, response): リンク先でのスクレイプコード yield item
同ページにも説明があり何となくは分かりますが、meta={‘item’ : item}の部分はよく分かりません笑
とりあえずそのまま使わせていただきます。
リンク先でのコードは前回用意できたので、リンク先を含むタグを指定するだけでできそうです。
ソースを確認。
<td class="result"><a href="/datafile/seiseki/replay/2018/001.html" class="btn-def btn-xs"><i class="fa fa-chevron-circle-right" aria-hidden="true"></i>レース結果</a></td>
<td class=”result“>の<a href>にあります。
であればリンク先の部分は以下で良さそう。
yield scrapy.Request(keiba.css('td.result a::attr(href)').extract(),
だがしかし!実行すると以下のエラーが・・・
TypeError: Request url must be str or unicode, got list:
URLが文字列ではなくリストで渡っているらしい。
なぜなのか、調べど調べど原因が分からず・・・
詰まったらprint関数で挙動確認
ある友人のアドバイスでprint関数を使って実際に何を渡しているのか確認することに。
デバッグモード(scrapy shell)にて。
In [1]: page = response.css('td.result a::attr(href)').extract() In [2]: print(page)
リストの中にリンク先が多数入っているのが返ってくる。
なるほど確かにリストを渡していたわけか。
とりあえず.extract_first()にしてみる。
In [3]: page = response.css('td.result a::attr(href)').extract_first() In [4]: print(page) /datafile/seiseki/replay/2018/001.html
リストでは無くなったからとりあえずコードを書き直してクロール実行すると次なるErrorが。
TypeError:Request url must be str or unicode, got NoneType:
始めはリンク先がない(時がある)からErrorが出てると考えてましたが違ったみたいです。
先の結果にhttp://www.jra.go.jpがついてないのが原因でした。
リンク先URLを正規の形で渡す
ソースで得られる文字列に上記のhttp://www.jra.go.jpを追加するには、response.urljoinを使えば良いらしい。
ってことでデバッグしてみると・・・
In [5]: page = response.urljoin(response.css('td.result a::attr(href)').extract_first()) In [6]: print(page) http://www.jra.go.jp/datafile/seiseki/replay/2018/001.html
ついにこれでクロールコードも完成か・・・
コードを書き換えて実行すると・・・
Errorが出ないで各ページでスクレイピングしている!!
量が多いので時間はかかるがついに成功!!
値をcsvで出力
ようやくコードも出来た所で、得た値をcsvファイルで出力していきます。
scrapy crawl <.pyのファイル名> -o <出力する際のファイルの名前>.csv
はい参考文献通りこれで終わり。
出力したcsvのExcelでの開き方
ところがExcelで開くと文字化けする。ググる。
参考:https://global-wing.com/activity/csv_character_code.html
(Excelにて) データ > 外部データの取り込み > テキスト ファイルのインポート
その後、Unicode(UTF−8)を選べば文字化けも解消(プレビューで確認できる)。
その他は必要に応じて設定すればOK。
指定のリンク先がない場合の条件分岐
結局関係無かったみたいだがおまけ。
リンク先が取得出来たときのみクロールさせるためには。
参考:https://stackoverflow.com/questions/40105949/scrapy-request-url-must-be-str-or-unicode-got-nonetype
当然if文を使えばいい。存在する時のみurljoinを実行する。
page = keiba.css('td.result a::attr(href)').extract_first() if page: page = response.urljoin(page) yield scrapy.Request(page, callback=self.parse_detail, meta={'item': item})
「if page:」と書くだけ。
ようやくクロール出来たので、次は2017年を実行だ!
ということで、start_urlsの2018を2017に変えて実行するだけ。楽ちん。
ところが!
一部しか出力されず・・・挙動見るとクロールは出来ている。
そこで一部ページを確認すると・・・ソースが最近のページと全然違う!!
なんかalignとかいろいろ細かいタグになっている・・・
次回は色々なcssクラスタの書き方についてまとめます。