PythonでYahoo!ファイナンスをWebスクレイピングして自分好みにデータ加工する

IT

株式投資を実践していく上で、ヤフーファイナンスの情報はとても有用で利用しない手はありませんが、自分好みの情報を得ようと思うと、どうしても痒いところに手が届きません。

例えば僕の場合、上場数年以内の企業でサステナブルな事業を行っている企業に投資をしたいと思っています。

この場合、ヤフーファイナンスのランキング機能を利用すれば、上場数年以内の企業を新しい順に割り出すことが可能です。

しかしサステナブルかどうかを判断するために、特色や詳細の事業内容、業績といった情報にアクセスするには、どうしてもひと手間を要します。

この課題を解決するために、ヤフーファイナンスの情報を整理し、自分好みの情報にまとめるシステムをPythonのスクレイピングで実現しました。

本記事では上場数年以内の企業の特色を一覧化し、詳細情報に素早くアクセス可能なシステムの構築方法について説明します。

システムイメージ

システムイメージは下記のとおりです。

・定期的にヤフーファイナンスからWebスクレイピングによりデータ取得し、JSONデータにまとめます。

・HTMLファイル(JavascriptとCSS)でJSONファイルを読み込んで自分好みに整形し、Webサーバに読み込ませ、ブラウザにより表示できるようにします。

インフラ部分は前回の記事で作成したDockerに乗せます。

定期実行については、前回Docker上に構築したCron専用コンテナを利用します。

今回作成する新規コンテナを「成長株コンテナ」とすると、イメージとしては下記のようになります。

作成方法

まず、前回同様、ソースコードの入れ物となる7つの空ファイルを作成します。vscodeを開き、下記のようなフォルダ階層で作成します。

僕の環境では下記フォルダ構成で利用します。

E:.
└─corder
     ├─etc
     └─projects
         └─YoungStock
             ├─docker-compose.yml
             ├─Dockerfile
              └─src
                   ├─bottlerun.py
                   ├─youngstock.py
                   ├─youngstock.sh
                   ├─dynamic
                   |  └─youngstock.json
                   └─views
                       └─youngstock.html

下記のような感じになります。

続いて、ファイルの中身を記載していきます。

■docker-compose.yml

Dockerfileを構成し、コンテナを立ち上げるための設定です。ポート部分は環境に合わせて変更します。

version: '3'
services:
maccounter:
restart: always
build: .
container_name: 'youngstock'
working_dir: '/root/src/'
command: python3 bottlerun.py
tty: true
ports:
- '8134:8134'
volumes:
- /E/coder/projects/YoungStock/src:/root/src

■Dockerfile

コンテナイメージの定義です。Python3のコンテナをベースとして、必要なパッケージをpipインストールしています。

FROM python:3
USER root
RUN apt-get update
RUN apt-get -y install locales && \
localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN pip3 install bottle
RUN pip3 install requests
RUN pip3 install beautifulsoup4
RUN pip3 install mojimoji
view raw Dockerfile hosted with ❤ by GitHub

■bottlerun.py

単純なWebサーバです。簡単のため、Bottleフレームワークを利用しています。ポート部分は環境に合わせて変更します。

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from socketserver import ThreadingMixIn
from wsgiref.simple_server import WSGIServer
from bottle import HTTPResponse, route, run, template, request, response, view, default_app, static_file, redirect
class ThreadedWSGIServer(ThreadingMixIn, WSGIServer):
"""マルチスレッド化した WSGIServer"""
pass
# メイン
@route('/')
@view('youngstock')
def youngstock():
pass
# 動的ファイル
@route('/dynamic/<filename:path>')
def dynamic(filename):
return static_file(filename, root="dynamic")
if __name__ == '__main__':
server = simple_server.make_server('', 8134, default_app(), server_class=ThreadedWSGIServer)
server.serve_forever()
view raw bottlerun.py hosted with ❤ by GitHub

■youngstock.py

Webスクレピングの実装部分です。

基本ライブラリや細かい処理の解説は割愛しますが、大まかな流れとしては下記のようになっています。

① ヤフーファイナンスの上場年月日ランキング表を1ページから取得する

② 会社名、ヤフーファイナンスの会社個別URLをハッシュマップに代入する

③ 会社個別URLから特色を取得し、ハッシュマップに代入する

④ 「会社名 HP」のGoogleWeb検索結果から、会社HPのURLを取得し、ハッシュマップに代入する

⑤ ①に戻り15ページまで繰り返す

⑥ ハッシュマップをJSONファイルに書き出す

# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import json
import mojimoji
import re
import datetime
def makejson(page_num = 1):
# JSON
'''
{
"date": "7/13 15:00",
"data": [
{
"name": "会社名",
"hp_url": "会社のURL",
"yf_url": "会社のヤフーファイナンスURL"
"feature": "特色"
},
{},
...,
{}
]
}
'''
ret = {}
ret["date"] = datetime.datetime.now().strftime("%m/%d %H:%M")
ret["data"] = []
# 指定ページまで繰り返す
for page in range(1,page_num+1):
# ヤフーファイナンスの設立年月日ランキングのページを取得
urlstr = 'https://finance.yahoo.co.jp/stocks/ranking/listingDate?market=all&term=daily&page=' + str(page)
html_text = requests.get(urlstr).text
soup = BeautifulSoup(html_text, 'html.parser')
# テーブルを取得
for selecttable in soup.find_all("table"):
if "順位" in selecttable.text:
table = selecttable
break
# アンカーリストを取得
for ancs in table.find_all("a"):
# 掲示板のアンカーを排除
if "掲示板" not in ancs:
#------------------------------
# 各会社の基本情報を取得
#------------------------------
company = {}
company["name"] = mojimoji.zen_to_han(ancs.text, kana=False).replace("&","&")
company["yf_url"] = ancs.attrs["href"]
#------------------------------
# 特色を取得
#------------------------------
company_code = re.sub(".*/", "", company["yf_url"])
urlstr2 = "https://finance.yahoo.co.jp/quote/" + company_code + "/profile"
html_text2 = requests.get(urlstr2).text
soup2 = BeautifulSoup(html_text2, 'html.parser')
# 特色を取得
company["feature"] = ""
for selecttable in soup2.find_all("table"):
if "特色" in selecttable.text or "概要" in selecttable.text:
for selecttr in selecttable.find_all("tr"):
if "特色" in selecttr.text or "概要" in selecttr.text:
company["feature"] = selecttr.find_all("td")[0].text.replace("【特色】", "")
break
break
# 特色が未反映の会社は除外する
if company["feature"] == "---":
continue
#------------------------------
# 会社URLを取得
#------------------------------
urlstr3 = "https://www.google.com/search?q=" + company["name"] + "+hp"
html_text3 = requests.get(urlstr3).text
soup3 = BeautifulSoup(html_text3, 'html.parser')
# 会社URLを取得
company["hp_url"] = ""
for selectdiv in soup3.select("div > a"):
href = selectdiv.attrs["href"]
if href.startswith("/url?q=") and "google" not in href and "wikipedia" not in href:
tmpstr = href.replace("/url?q=", "")
tmpstr = re.sub("&sa=U.*", "", tmpstr)
company["hp_url"] = tmpstr
break
#------------------------------
# 標準出力
#------------------------------
print(company["name"])
print(company["hp_url"])
print(company["yf_url"])
print(company["feature"])
print("----------------------")
ret["data"].append(company)
# JSON出力
f = open("dynamic/youngstock.json", "w", encoding='utf-8')
f.write(json.dumps(ret, ensure_ascii=False))
f.close()
if __name__ == "__main__":
# JSONファイル作成
makejson(page_num=15)
view raw youngstock.py hosted with ❤ by GitHub

■youngstock.sh

Cron定義記載のためのyoungstock.pyを起動するシェルです。

#!/bin/bash
proc="python3"
rundir="/root/src/"
op="youngstock.py"
cd $rundir
$proc $op
exit $RETVAL
view raw youngstock.sh hosted with ❤ by GitHub

■youngstock.json

自動生成されるファイルです。空ファイルのままで問題ありません。

■youngstock.html

データを見やすく表示するための肝の部分となります。

会社名、特色を一覧表示し、会社名をタップ or クリックすると、会社ホームページおよびヤフーファイナンスのリンクがポップアップで表示されるようにしました。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<title>上場数年以内の会社一覧</title>
<style type="text/css">
table {
border-collapse: collapse;
width:100%;
font-size : 11px;
}
th {
border-bottom: #e3e3e3 1px dotted;
text-align: left;
padding: 10px;
font-weight: normal;
width: 34%;
}
td {
border-bottom: #e3e3e3 1px dotted;
text-align: left;
padding: 5px;
width: 34%;
}
td.s {
border-bottom: #e3e3e3 1px dotted;
text-align: left;
padding: 2px;
width: 66%;
}
tr:hover {
background: #3D80DF;
color: #FFFFFF;
}
span {
font-size : 11px;
}
.modal {
display: block;
width: 600px;
max-width: 100%;
height: 200px;
position: fixed;
z-index: 100;
left: 50%;
top: 70%;
transform: translate(-50%, -10%);
/* margin: -200px 0 0 -200px; */
background: white;
box-shadow: 0 0 60px 10px rgba(0, 0, 0, 0.9);
}
.closed {
display: none;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 50;
background: rgba(0, 0, 0, 0.6);
}
.modal-guts {
position: absolute;
top: 20px;
left: 80px;
width: 100%;
overflow: auto;
padding: 30px 50px 50px 50px;
}
.modal .close-button {
position: absolute;
z-index: 1;
top: 10px;
right: 20px;
border: 0;
background: black;
color: white;
padding: 5px 10px;
font-size: 1.3rem;
}
</style>
</head>
<body>
<span id="date"></span>
<table id="asset">
<tr>
<th>会社名</th>
<td>特色</td>
</tr>
</table>
<div class="modal-overlay closed" id="modal-overlay"></div>
<div class="modal closed" id="modal">
<button class="close-button" id="close-button">閉じる</button>
<div class="modal-guts">
<h2 id="hp"><a id="hp_url" href="" target="_blank">会社ホームページ</a></h4>
<h2 id="yf"><a id="yf_url" href="" target="_blank">ヤフーファイナンス</a></h4>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script type="text/javascript">
ads = "";
$(function(){
$.get("/dynamic/youngstock.json", null, function(data){
//JSONのパース不要
//var obj = JSON.parse(data);
/////////////////
//データ表示
/////////////////
ads = data.data
ads.forEach((ad,i,ads) => {
var addtag = "<tr><td id='" + i + "' class='ob'>" + ad.name + "</td><td class='s'>" + ad.feature + "</td></tr>";
$('#asset').append(addtag);
});
/////////////////
//取得日時
/////////////////
$('#date').html(data.date);
});
});
// ポップアップを開く
$(document).on("click", ".ob", function(){
$("#modal").toggleClass("closed");
$("#modal-overlay").toggleClass("closed");
var id = Number($(this).attr('id'))
$("#hp_url").attr("href", ads[id].hp_url);
$("#yf_url").attr("href", ads[id].yf_url);
});
// ポップアップを閉じる
$(document).on("click", "#close-button", function(){
$("#modal").toggleClass("closed");
$("#modal-overlay").toggleClass("closed");
});
</script>
</body>
</html>
view raw youngstock.html hosted with ❤ by GitHub

次に、Cron設定を施します。Cron専用コンテナのcrontabに下記を追記します。

18 16 * * 5 docker exec -i youngstock /root/src/youngstock.sh
view raw crontab hosted with ❤ by GitHub

毎週金曜16:18に実行する例です。曜日や時間は適宜変更してください。

今回の内容(企業の上場頻度)であれば、高頻度の更新は必要ないので、週次で実行することにします。なるべく低頻度にして、ヤフーのサーバーに負担をかけないよう気をつけます。

Cron専用コンテナのcrontabを変更した際には、下記コマンドで反映してください。

cd Cron

sudo docker-compose down

sudo docker-compose build

sudo docker-compose up -d

最後に、成長株コンテナを起動して作成完了です。

cd YoungStock

sudo docker-compose up -d

システム利用方法

Cron設定時刻の経過後に、スマホのブラウザを開き、下記URLに接続します。

http://[サーバのIPアドレス]:[ポート]/

上場数年以内の会社一覧が、整形された形で表示されたら成功です。

会社名をタップすると、ポップアップが表示され、会社ホームページのリンクとヤフーファイナンスのリンクが表示されます。

スマホを片手で操作できるよう、下の方にポップアップし、リンクを右側に寄せてます。

これによりスピーディーに株式投資の情報を得られるようになりました。

まとめ

本記事では、PythonによるWebスクレイピングを利用して取得したデータを元に、個人利用Webサイトの作成方法を説明しました。

よく利用するWebサイトに改善案がある場合は、今回の方法を応用することで便利な個人利用Webサイトを作成可能と思いますので、試してみてはいかがでしょうか。

コメント

タイトルとURLをコピーしました