PythonTech Blogプログラミング 

株価ランキングのクローリング

こんにちは。データ事業部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は、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。