PythonでWebスクレイピング②(実際に株サイトをスクレイピングする)


0.はじめに

pythonを投資活用に使う目的で調べたまとめ記事の第2弾の続き記事です。
今回は実際にスクレイピングを試してみます

*前回記事
PythonでWebスクレイピング①(スクレイピング事前知識)

1.前回のおさらい&スクレイピング対象の確認

前回の記事で唯一「robots.txt: Allow:/ 」だった株式投資メモを対象に色々試していく。

#まずはreppyで確認(前回のおさらい)
from reppy.robots import Robots

robots = Robots.fetch('https://kabuoji3.com/robots.txt')
print(robots.allowed('https://kabuoji3.com/', '*'))
実行結果
False

↑でFalseとなってしまうが、これは(reppyで取得するために)株式投資メモのrobots.txtの書き方に問題がある為である。
通常Allow:の後にスペースが入るのが普通なのだが、このサイトにはついていない。
よって前回記事で紹介したreppyではうまく取得できなかった模様。NGの場合は自分でrobots.txtを確認しにいくことが大事ですね。

今のHPのRobots.txt

本来こうあるべきRobots.txt

なお、今回は例として下図の赤枠部分を取得したいケースをとりあげる。(URL:https://kabuoji3.com/stock/)

2.該当URLのHTMLを検証して、取得したい部分を探しておく

ここで一旦pythonから離れる。今回の目標は「上場全銘柄の最新株価情報を取得すること」なのだが、何も考えずにURL情報を取得しても同じページ内に存在する不要な情報も取得してしまう(例えばhelpページへのリンクやタイトル情報等)ので、「必要な部分」がHTMLのどこに記述してあるか?を理解しておく必要がある。

するとHTMLの知識が必要・・また言語の勉強・・?となるわけだが、最低限だけ知っておけば問題ない。
しかもGoogle Chromeの場合は「検証」という機能があるので、それも不要である程度対応できる。
対象のURLで左クリックすると、↓のようなコマンドがでてくるはずである。


ページのソースを表示:HTMLをそのまま表示する
検証:ページの記述がHTMLのどこを指すのか?がすぐにわかる(それだけではないが、この記事ではメイン)

対象ページ「https://kabuoji3.com/stock/」 の上で検証を表示させてみる

右に検証Windowが出てくる。
検証WindowではHTMLの記述が書かれているが、試しにマウスカーソルを<header id="header" class="">の部分に
合わせてみると、上図のようにURLの上の方が青くハイライトされていることがわかる。
これはつまり、URLの上の方(タイトルとか)がこの部分に書かれていることを指す。

今回は株価データの表部分が取得したい部分なので、それを検証で出てきたHTMLの中から探していけばいい。


探していくと、table class="stock_table"という部分がそうっぽいことがわかる。
で、その中にtheadtbodyというのがあって、それぞれがこの表の「ヘッダー」と「各行の株価データ」であることがさらに検証で表示させていくとわかる。
ちなみに、表要素をHTMLではtableと呼ぶので、それで探してもいいかもしれない

3.上場全銘柄の最新株価情報をrequestsコマンドで取得

ここからは2で調べた情報も踏まえてpythonで取得していく。
前回も少し触れたが、BeautifulSoupをインストールしておくこと(pip install beautifulsoup4)。

事前に確認くんで自分のユーザーエージェント(UA)情報を確認するのが必要になる。
「現在のブラウザー」に記載してある部分がUA情報なので、下記コードを自分の環境に応じて書き直すこと。
※「現在のブラウザー」に出てくる「表示サイズ:~~」という記述は不要なのでコピペしないこと

import requests
from bs4 import BeautifulSoup
import pandas as pd

#スクレイピング対象のURLを入力
url = 'https://kabuoji3.com/stock/'

#確認くんで調べた自分のユーザーエージェント(現在のブラウザー)をコピペ ※環境に応じて書き直す
headers = {"User-Agent": "Mozilla/*** Chrome/*** Safari/***"}

#Requests ライブラリを使用してWebサイトから情報(HTML)を取得する。
response = requests.get(url, headers = headers)

print(response)
実行結果
<Response [200]>

すると3桁のHTTPステータスコードが返ってくる。200(リクエスト成功)が返ってくればOK。
もし403(認証拒否)や404(Not Found※URL不正)が返ってきたら失敗しているので、もう一度記述を見直す。

参考:HTTPステータスコードWiki

4.requestsコマンドで取得した中身を解析し、必要な部分を取り出す

次にBeautiifulSoupで取得したHTMLの中身を解析して、必要部分をタグ指定して取り出す

#取得したHTMLからBeautifulSoupオブジェクト作成
soup = BeautifulSoup(response.content, "html.parser")

"""
まずは株価表のヘッダー部分を取得する。
2でHTMLの中でヘッダー部分に「tr」というタグが付いているのはわかっているので、それを探す。
やり方はいくつかあるが、要は<thead>タグの中にある<tr>を全部抜き出せばヘッダー全部が取得できる。
"""
#まずはtheadをfindメソッドでコマンドで検索し、その中のtrをfind_allメソッドですべて抽出
tag_thead_tr = soup.find('thead').find_all('tr')

print(tag_thead_tr)
実行結果
[<tr>
<th>コード・名称</th>
<th>市場</th>
<th>始値</th>
<th>高値</th>
<th>安値</th>
<th>終値</th>
</tr>]
#同様に株価部分を取得していく。tbodyタグの内にあるtrタグでひとまとまりになっているのはすでにわかっている
tag_tbody_tr = soup.find('tbody').find_all('tr')

#数が多いので、0番目だけ表示
print(tag_tbody_tr[0])
実行結果
<tr data-href="https://kabuoji3.com/stock/1305/">
<td><a href="https://kabuoji3.com/stock/1305/">1305 ダイワ 上場投信-トピックス</a></td>
<td>東証ETF</td>
<td>1883</td>
<td>1888</td>
<td>1878</td>
<td>1884</td>
</tr>

これでうまく取得できていることがわかる。

5.取得した情報をpandasで表示させる

pythonで表を扱いやすいpandasでまとめてみる。

import pandas as pd

#取得したヘッダー部をさらにthで検索してテキスト化する ※[0]としているのは、find_allを重ねることが出来ないため。
head = [h.text for h in tag_thead_th[0].find_all('th')] 

#株価部分を当てはめていく
data = []
for i in range(len(tag_tbody_tr)):
    #各カラムのデータはtdタグに格納されているので取り出す
    data.append([d.text for d in tag_tbody_tr[i].find_all('td')])
    df = pd.DataFrame(data, columns = head)

#データフレーム最初2行だけ表示
df.head(2)

表示結果

コード・名称 市場 始値 高値 安値 終値
0 1305 ダイワ 上場投信-トピックス 東証ETF 1883 1888 1878 1884
1 1306 (NEXT FUNDS)TOPIX連動型上場投信 東証ETF 1861 1867 1856 1863

これでpythonで扱いやすい形に変形ができた。あとは煮るなり焼くなり好きに加工して使えばいいし、
pandasのto_csvメソッドでcsvに保存もできたりする。

6.最後に

記事自体は長くなっているが、コード自体も短く取得できていることがわかる。
要するに、HTMLのどこのタグに取得したい情報があるか?を探すだけでいいのである。
もちろんこれで取得できないケースも多く存在する(javascript等絡んでいる場合)が、それはその時に応じて身に着ければいいと思う。
次回は第3回として、1回目と2回目を組み合わせてデータベースにスクレイピングで取得した株価を格納する記事を予定です

7.(おまけ)簡単なHTMLの補足

一応 https://kabuoji3.com/stock/ のHTMLを簡単に解説する。
検証でHTMLを表示させて、body・・の部分を折りたたむと下図のようになる。

つまり簡単にすると⇓のようになっている。

<!-- 最初にHTML宣言を行う。lang="ja"で日本語を扱うよ~という意味-->
<html class=・・> 

<!-- ここはブラウザには表示されないhead部分。文字コードやページ検索時に検索結果として表示される部分などが記述-->
<head>...</head>
<!-- ここがHTML本体のbody部分。divタグでページ全体をグループ分けしつつ作られる-->
<body class=・・・>...</body>

<!-- HTML記述終了-->
</html>

ページ内HTMLのざっくりとしたbodyの構成を下に書いておく。
階層に分けてインデントを入れている。実際にページを見たり検証をクリックしながら確認するとわかりやすい。数が多くぐちゃぐちゃしてるように見えるが、確認したい箇所のdiv idにはダブりがないので着目していくだけである。

<!-- ▼bodyの部分だけ抜粋-->
<body>
    <div id="wrapper">
        <!-- ▼ヘッダー(見出し)部分-->
        <header id="header">...</header>
        <!-- ▼グローバルナビ(MENUを押すと出てくる)部分-->
        <div id="gNav_wrap">...</div>
        <!-- ▼ページメイン部分-->
        <div id="contents_wrap">
            <!-- ▼メイン部分-->
            <div id="container_in">
                <!-- ▼メイン部分-->
                <div id="main">
             <!-- ▼ここからの下層重要な箇所だけ。それ以外は省略-->
             <div class="data_contents">
             <!-- ▼株価のテーブル(表)-->
                        <table class="stock_table">
                 <!-- ▼表のヘッダー(カラム)-->
                            <thead>
                                <!-- ▼カラム-->
                                <tr>...</tr>
               </thead>
                 <!-- ▼表の中身データ部分-->
                            <tbody>
                                <!-- ▼各行毎に株価データ-->
                                <tr>...</tr>
                            </tbody>
                        </table>
                    </div>
                </div>
                <!-- ▼Data Menu部分-->
                <div id="side">...</div>                
            </div>
            <!-- ▼HOME,PAGETOPのリンクがあるナビ部分-->
            <div id="gNav_wrap">...</div>
            <!-- ▼フッター(一番下に配置される情報がまとまった)部分-->
            <div id="gNav_wrap">...</div>
        </div>
    <!-- ▼スクリプト部分。javascriptや外部スクリプトを読み込んだりする場合に使用する-->
    <script>...</script>
</body>