長文になったのでまとめを最初に書いときます
以下、試行の変遷記録(笑)
最初に言っておくと、Python3しか書けないのでPython3で書きました。
連番をurlに埋め込んで使うのは良いと思いますが、ページが変なページで取得したものがNoneだったらエラーになってしまいます。
なのでifで回避すれば動くと思います。
以下のコードは取得のみなのでフォーマットを整えてcsv出力はしてません。
python
1# -*- coding:utf-8 -*-
2
3from urllib.request import urlopen
4import time
5from bs4 import BeautifulSoup
6
7url = 'http://db.netkeiba.com/horse/201310069{0}/'
8horse_name_list = []
9
10for i in range(1, 99999):
11 soup = BeautifulSoup(urlopen(url.format(i)), "lxml")
12 time.sleep(5)
13 div = soup.find('div', class_='horse_title')
14
15 # divがNoneTypeObjectだったらcontinue
16 if div != None:
17 horse_name = div.h1.text
18 if horse_name != None:
19 horse_name_list.append(horse_name)
20 else:
21 continue
22 else:
23 print("取得できないから飛ばすよ")
24 continue
25
26print(horse_name_list)
27
求めているものと違ったら申し訳ありません。
追記:
2.7で質問にあるコードでいろいろやってみたのですが、なんかxrangeで返るイテレータをforで回してurlに入れ込んでもurlが変わらないみたいです。
ずっとhttp://db.netkeiba.com/horse/2013100691/
のままなので同じものしか取得できませんでした。
2.7の仕様はよく分かってませんので、きっとなにかがPython3と違うのでしょう。
print i
をするとちゃんと順番にたされた数字が返ってきてるのですが・・・
※このへんは変数urlを上書きしてるのが原因でした。一番下で言及します
なのでいろいろやってこうなりました。
python
1# -*- coding:utf-8 -*-
2
3import urllib2
4import codecs
5import time
6from bs4 import BeautifulSoup
7
8f = codecs.open('hiyoko.csv', 'w', 'utf-8')
9horse_name = ""
10
11for i in xrange(1, 10):
12 url = 'http://db.netkeiba.com/horse/201310069%d/' % i
13 soup = BeautifulSoup(urllib2.urlopen(url).read(),"lxml")
14 time.sleep(5)
15 horse_name_tag = soup.find('div',{'class':'horse_title'})
16 if horse_name_tag != None:
17 horse_name_tag.find('h1')
18 horse_name = "".join([x for x in horse_name_tag.text if not x == u'\xa0' and not x == u'\n'])
19 print horse_name.strip()
20 cols = [horse_name]
21 f.write(",".join(cols) + "\n")
22 else:
23 continue
24
25f.close()
追記:
質問者様のコードでは
python
1 horse_name_tag=soup.find('div',{'class':'horse_title'}).find(('div',{'class':'horse_title'}).find('h1'))
の部分で、find関数にタプルを渡してしまっています。(丸括弧が多い)
この部分ですね。
python
1soup.find('div',{'class':'horse_title'}).find( ('div',{'class':'horse_title'}).find('h1') )
このまま実行すると
AttributeError: 'tuple' object has no attribute 'find'
というエラーになります。なので、
python
1soup.find('div',{'class':'horse_title'}).find('div',{'class':'horse_title'}).find('h1')
これでOK。(違う理由でこれだとエラーになります。以下参照)
おそらくただのタイプミスだとは思いますが一応。
上記のままだとエラーになる理由がこれ。
python
1horse_name_tag=soup.find('div',{'class':'horse_title'}).find('div',{'class':'horse_title'}).find('h1')
ここでclass="horse_title"
のdiv
の下でまたclass="horse_title"
のdiv
を探しちゃってるのでNoneになります。
サイト内で確認したらclass="horse_title"
のdiv
の直下にh1
があるので
python
1horse_name_tag=soup.find('div',{'class':'horse_title'}).find('h1')
これで取得できます。
あとはページが変だったときにNoneが返るのをifで分岐してエラー回避して・・・
python
1# -*- coding:utf-8 -*-
2
3import urllib2
4import codecs
5import time
6from bs4 import BeautifulSoup
7
8f = codecs.open('hiyoko.csv', 'w', 'utf-8')
9horse_name = ""
10start_url = 'http://db.netkeiba.com/horse/201310069{0}/'
11
12for i in xrange(1, 10):
13 url = start_url.format(i)
14 soup = BeautifulSoup(urllib2.urlopen(url).read(), "lxml")
15 time.sleep(1)
16 horse_name_tag = soup.find('div', {'class': 'horse_title'})
17
18 if horse_name_tag != None:
19
20 if horse_name_tag.find('h1') != None:
21 horse_name = horse_name_tag.find('h1').text
22 horse_name = "".join(
23 [x for x in horse_name_tag.text if not x == u'\xa0' and not x == u'\n'])
24 print horse_name.strip()
25 cols = [horse_name]
26 f.write(",".join(cols) + "\n")
27
28 else:
29 continue
30
31f.close()
これで動くはずです。
最初コード書いたときに、最初に定義したurlという変数をfor内で上書きしちゃってました。
format
関数は文字列内に{0}
などがなくてもエラー吐かないんですね・・・
確か同スコープ内なので、最初のurlはstart_urlという変数にいれて、for内でフォーマットして使うものはurlとしました。
最後にもう一度まとめ
あとはxrange(1, 100000)
にすれば2013100000から2013199999までの情報がとれます。
sleep(1)
だとちょっと不安なのでもっと増やしたほうが個人的にはいいですが・・・
import random
をしてtime.sleep(random.uniform(5, 10))
とかにすると5sec - 10secの間でランダムな秒数待てます。
ただこれだけ待ってると99999件取得した場合に尋常じゃない時間がかかります。
普通1秒に1アクセスでも問題にはならないようですが、岡崎市図書館事件のように、相手側のサーバーでの通信方式に不備があると障害を引き起こすこともあります。
一番は相手側にスクレイピングの許可をもらえることですね。
長文で申し訳ないです・・・
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/05/08 04:22 編集
2017/05/08 04:30
2017/05/08 10:01