株価ランキングのクローリング
こんにちは。データ事業部2部の安部健太郎です。本記事では、クローリングについて解説したいと思います。クローリングとは、データ分析をする上で必要なデータを、様々なWEBサイトから自動で収集する技術のことです。WEBからの情報取得は、以下の2つに分類されます。
・ APIを使い、必要な情報だけを取得するタイプ
・ 人間と同じように、WEBサイトを実際に見に行き、必要な情報を取得するタイプ
本記事では、後者のAPIが提供されていないWEBサイトの情報取得について解説していきます。静的なサイトと動的なサイトではクローサーの作成の仕方、難易度が変わってきますが、今回は静的なサイトのクローラーの作成の方法を説明していきます。
今回は、Pythonを使ってヤフーファイナンスから株価情報を取得し、最終的にcsvファイルとして出力することを目標にします。
実際の作業の手順としては、以下の通りに行っていきます。
1, WEBサイトからソースコードを取得する
2,ソースコードをパースして、必要な情報を抜き出す
3,データフレームに情報を格納し、csvファイルに出力する
それでは順にやっていきましょう。
WEBサイトからソースコードを取得する
まずソースコードの取得ですが、これは`requests`と呼ばれるライブラリを使い、以下のように書くことで取得できます。
import requests url = 'https://info.finance.yahoo.co.jp/ranking/' html = requests.get(url)
ここで`html`にはレスポンスコードやヘッダー情報などが含まれていますが、ソースコードを抜き出す場合は以下のように書くことで取得できます。
html.content
しかし、ここに含まれている情報は、何の意味も持たないただの文字の羅列になっています。htmlタグなどに意味を持たせ、抽出を容易にするために、次に説明するパースを行います。
ソースコードをパースして、必要な情報を抜き出す
`html.content`に入っている情報をパースすることで、文字の羅列を、htmlとして解釈することが可能になります。
今回は、`BeautifulSoup`というライブラリを用いてパースしたいと思います。
以下のように記述することで、`soup`という変数に、パースされたソースコードが格納されます。
from bs4 import BeautifulSoup soup = BeautifulSoup(html.content, 'html.parser')
ここから、一つ一つの情報の抽出に移ります。いきなり全部取るのではなく、まずは1行目のデータだけ抜き出してみたいと思います。1行目が正しく抜き出せれば、後はfor文を回していけば全て取得できるというのはなんとなくイメージが湧くかと思います。
ブラウザからソースコードを確認してみると、各行の株価の情報は`tr`タグでの中の、`rankingTabledata yjM`というクラウ名で書かれているということがわかります。これら情報を抜き出すためには、以下のように書きます。
itemlist = soup.find_all('tr', class_= 'rankingTabledata yjM')
これで`itemlist`に全ての株価情報がリスト型として格納されました。確認のため、`len(itemlist)` と書いて実行してみると、株価の表の行数である50という数値が返ってきたと思います。今は`itemlist`には全行分のデータが格納されているため、まずは1行目だけを以下のようにして抜き出します。
item = itemlist[0]
`item`の中身は以下のようになっており、確かに1行目のデータが抜き出せたことが確認できます。
<tr class="rankingTabledata yjM"><td class="txtcenter">1</td><td class="txtcenter"><a href="https://stocks.finance.yahoo.co.jp/stocks/detail/?code=9399.t">9399</a></td><td class="txtcenter yjSt">東 証2部外国</td><td class="normal yjSt">ビート・ホールディングス・リミテッド</td><td class="txtcenter grey yjSt">15:00</td><td class="txtright bold">203</td><td class="txtright bgyellow02"><span class="greenFin">+26.09</span>%</td><td class="txtright bgyellow03"><span class="greenFin">+42</span></td><td class="txtright">741,912</td><td class="txtcenter yjSt"><a href="https://textream.yahoo.co.jp/rd/finance/9399">掲示板</a></td></tr>
順位、コード、コードのリンク先のURLは以下のようにして抜き出せます。別の要素が、同じタグ、同じクラス名で書かれている場合は、何番目を取り出すかを後ろに記載します。
junni = item.find_all('td', class_='txtcenter')[0].text code = item.find_all('td', class_='txtcenter')[1].find('a').text code_url= item.find_all('td', class_='txtcenter')[1].find('a')['href']
市場のように、タグとクラスで指定をすると要素が一つに決まる場合には、以下のように書くことができます。
shijou = item.find('td', class_='txtcenter yjSt').text
ここまでできたら、1行目の要素を全て抜き出し、dataframeに入れてみましょう。
データフレームに情報を格納し、csvファイルに出力する
dataframeの作成と同時にデータを挿入してもいいのですが、この後にfor文を回すことを考え、先に空のdataframeを作成し、そこにデータを追加していく形とします。上で書いたコードもまとめて書いてみます。
# 空のデータフレームの作成 import pandas as pd columns = ['順位', 'コード', 'コードURL', '市場', '名称', '取引値時間', '取引値', '前日比割合', '前日比', '出来高', '掲示板URL'] df = pd.DataFrame(columns=columns) # 1行目だけ抜き出す item = itemlist[0] # 1行目の各要素を、一旦変数に格納。 junni = item.find_all('td', class_='txtcenter')[0].text code = item.find_all('td', class_='txtcenter')[1].find('a').text code_url= item.find_all('td', class_='txtcenter')[1].find('a')['href'] shijou = item.find('td', class_='txtcenter yjSt').text meishou = item.find('td', class_='normal yjSt').text torihikichi_jikan = item.find('td', class_='txtcenter grey yjSt').text torihikichi = item.find('td', class_='txtright bold').text zenjitsuhi_wariai = item.find('td', class_='txtright bgyellow02').text zenjitsuhi = item.find('td', class_='txtright bgyellow03').text dekidaka = item.find('td', class_='txtright').text keijiban_url = item.find_all('td', class_='txtcenter yjSt')[1].find('a')['href'] # 新しいdataframeを作成し、各要素をdataframeの各行に格納。 append_list = [junni, code, code_url, shijou, meishou, torihikichi_jikan, torihikichi, zenjitsuhi_wariai, zenjitsuhi, dekidaka, keijiban_url] df_next = pd.DataFrame([append_list], columns=columns) # 最初に作成した空のデータフレームと、データを入れたdataframeを結合する。 df = df.append(df_next)
ここまで書けたら、dfの中身を見てみましょう。各要素が正しくdfに格納されていると思います。
あとは`itemlist`後ろの数値を変数に置き換え、for文を回していけば完成です。上のコードとほとんど同じですが、完成版は以下のようになります。
# 空のデータフレームの作成 columns = ['順位', 'コード', 'コードURL', '市場', '名称', '取引値時間', '取引値', '前日比割合', '前日比', '出来高', '掲示板URL'] df = pd.DataFrame(columns=columns) # 1行目から最後の行まで順にデータを取得して、データフレームに格納していく for item in itemlist: junni = item.find_all('td', class_='txtcenter')[0].text code = item.find_all('td', class_='txtcenter')[1].find('a').text code_url= item.find_all('td', class_='txtcenter')[1].find('a')['href'] shijou = item.find('td', class_='txtcenter yjSt').text meishou = item.find('td', class_='normal yjSt').text torihikichi_jikan = item.find('td', class_='txtcenter grey yjSt').text torihikichi = item.find('td', class_='txtright bold').text zenjitsuhi_wariai = item.find('td', class_='txtright bgyellow02').text zenjitsuhi = item.find('td', class_='txtright bgyellow03').text dekidaka = item.find('td', class_='txtright').text keijiban_url = item.find_all('td', class_='txtcenter yjSt')[1].find('a')['href'] append_list = [junni, code, code_url, shijou, meishou, torihikichi_jikan, torihikichi, zenjitsuhi_wariai, zenjitsuhi, dekidaka, keijiban_url] df_next = pd.DataFrame( [append_list], columns=columns ) df = df.append(df_next)
このコードが実行できたら、dfの中身を見てみてください。どうでしょうか?50行のデータが正しく格納されたでしょうか?
正しくできていれば、あとは以下のようにcsvで出力するだけです。文字コードは各自の環境に合わせて変更してください。
df.to_csv('finance.csv', encoding="SHIFT-JIS", index = False)
まとめ
`requests`と`BeautifulSoup`を使うことで、静的なページのクローリングができました。
複数ページをクローリングする場合は、URLを変更しながらクロールするだけです。ただしその時は、WEBサイトに負荷をかけすぎないように注意しましょう。
DATUM STUDIOは、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。
Contact
Explore Jobs
関連記事