株価とスクレイピング③
目的:株探のWEBページを1ページずつ取得し、各データを取得する
目標
1 日経平均の銘柄・コード一覧から、株探のサイトを1企業ずつスクレイピング
2 データを整形
3 エクセルへ出力
株探のWEBページを1ページずつ取得し、各データを取得する
株探のサイトのURLは、ベースURL+証券コードの形になっているので、データが欲しい証券コードを準備し、1ページずつHTMLを取得しながら、データを抜き出していく
今回は日経平均の銘柄の各データを抽出して入力する
株探のサイトはスクレイピングについて明記されていない
みんかぶのサイトでも良かったが、更新が株探より遅かったので株探を利用した
プログラム実行順
1,必要なものをインポート
内容
import pandas as pd データフレーム(2次元配列)を扱うライブラリ
import urllib.request urllib.request.urlopen(URL)でURLにアクセス
from bs4 import BeautifulSoup
bs4ライブラリからBeautiful Soupをインポート
HTML文字列(ファイル)を解析することができる
urllib.request.urlopen(URL)でWebからHTMLファイルを取得して、それをBeautiful Soup 4(bs4)に渡すとBeautiful Soupオブジェクトを生成する
そのオブジェクトを検索したり抽出したりすることで、必要なデータを抜き出す(スクレイピング)ことができる
2,証券コード一覧のエクセルデータからコードだけを抜き出しリスト化
内容
pd.read_excel(r"エクセルのパス",sheet_name = 0, index_col = 0)
エクセルデータをそのままデータフレームに出力、sheet_name = 0で1枚目のシートを指定
index_col = 0で先頭行をインデックスに指定、指定しないと新しくインデックスが作成されてしまう
code = book_df["証券コード"].to_list()
コード一覧のリストを作成 .list()で、データフレームから証券コードの列を抜き出し、1列のリスト化する
base_url = "https://kabutan.jp/stock/?code="
https://kabutan.jp/stock/?code=4666 のように、4桁のコードを加えるだけで、その企業の株探のサイトURLになる
base_url + code でURLの完成になる
3,HTMLから欲しいデータを検索して取得する
内容と書式
WEBページ上で、右クリックするとページのソースを表示でHTMLデータを確認することができる
欲しいデータの空のリストを作成
for文で1企業ずつ各データを取得する
証券コードのリストからコードを抜き出し、str()で文字列変換してURLに、HTMLを取得して、欲しいデータを抜き出す
for i in code:
url = base_url + str(i)
data = urllib.request.urlopen(url)
soup = bs(data, 'html.parser')
最終的にデータフレームにしたいので、どれも1列のリスト化を目指している
最後に合わせてデータフレーム化 → エクセルに出力
今回はデータ数が同じことがはっきりしているので簡単に合わせられる
時価総額と株価をHTMLタグから取得する
株価と時価総額は、わかりやすいタグがついていたので、クラス指定で抽出
・時価総額
zika.append(soup.find("td",class_="v_zika2").text)
<td>タグがついていて、クラス属性"v_zika2"を指定、find().textでテキストのみを抽出する
リスト名.appendで、空のリストに取得した要素を加えていく
・株価
kabuka.append(soup.find("span",class_="kabuka").text)
同様に、<span>タグ、クラス指定で株価を取得、リスト化
出来高、PER、利回りの取得
株探のサイトは、テーブルで構成されているデータが多いので、HTMLからtableタグ指定、またはpandasのpandas.read_html()でテーブルをそのままデータフレームとして取得できる
内容
5番目のテーブルの1番目(1,1セル)に出来高のデータがあるので、まずは
table_5 = soup.find_all("table")[4]
で5番目のテーブルデータをtableタグから一括で抜き出す
[4]で5番目のテーブルを指定
table_5.find("tr").find("td")
で、最初の tr タグの、最初の td タグのデータを取得できる
~.textで文字列だけリスト化する
dekidaka.append(table_5.find("tr").find("td").text)
PERと利回りは、3つ目のテーブルにあるので、まずはテーブルデータを一括で取得
table = soup.find_all("table")[2] #3つ目のtableの取得
rows = table.find_all("tr")
col =[v.text for v in rows[1].find_all("td")]
#PBR、PER、利回り、信用倍率をリスト化している(1行だけ抜き出し)
ここでは、find_allはResultSetなので、find().find()のように、find_allを重ねて使えない → [1]でテーブルの2行目を指定してリストに変換して扱う
※rows は 2行4列のResultSet
下記参照
[v.text]で文字列だけリスト化されているので、
リストの中の1番目と3番目のデータを抽出してそのままリスト化
per.append(col[0])
rimawari.append(col[2])
売り上げと経常利益の取得
最新のデータのみを抽出したかったが、タグが同じで差別化する方法がわからなかったので、ここではpandasからそのままテーブルデータを取得した
データフレーム(エクセル)の3行目、2列目が売り上げ
3行目3列目が経常益なので、~.iloc[行,列]で欲しいデータのセルを指定してリスト化
インデックスは0から始まっている
内容
uriage.append(pd.read_html(url)[10].iloc[2,1])
keitune.append(pd.read_html(url)[10].iloc[2,2])
※HTMLタグから順番に指定していっても同じことができる
4,データフレームを作成し、元のエクセルへデータを重ねて出力
内容
各項目がすでにリスト化されていて、今回はデータ数が同じことがわかっているので、列名を指定しながらデータフレームにする
df = pd.DataFrame({"時価総額":zika,"株価":kabuka,"売上高":uriage,"経常利益":keitune,"RER":per,"利回り":rimawari,"出来高":dekidaka})
最初にエクセルから読み込んだ book_df と作成した df の2つのデータフレームをpd.concat([ , ], axis = ) で統合
ラベルも含めてデータ数が同じであることが前提
axis = 0 or 1 で行を追加するか列を追加するか指定
エクセルへ出力
dfs = pd.concat([book_df,df],axis = 1)
dfs.to_excel(r"エクセルのパス")
今回は作成しながら、トライ&エラーをするので新規シートへ出力した、pandas.ExcelWriterで、新規シートに保存することもできる
また、エクセルのパスを読み込んだものと同じにすると、データが上書きされる(※元データは消えることに注意)
コード全体
import pandas as pd
import urllib.request
from bs4 import BeautifulSoup as bs
#import requests
#import lxml.html
#import unicodedata
book_df = pd.read_excel(r"エクセルのパス",sheet_name = 0, index_col = 0)
code = book_df["証券コード"].to_list()
#ベースURL
base_url = "https://kabutan.jp/stock/?code="
#空のリストを作成
zika = []
kabuka = []
per = []
rimawari = []
uriage = []
keitune = []
dekidaka = []
#for文で1企業ずつ各データを取得する
for i in code:
url = base_url + str(i)
data = urllib.request.urlopen(url)
soup = bs(data, 'html.parser')
zika.append(soup.find("td",class_="v_zika2").text)
kabuka.append(soup.find("span",class_="kabuka").text)
table_5 = soup.find_all("table")[4]
dekidaka.append(table_5.find("tr").find("td").text)
table = soup.find_all("table")[2]
rows = table.find_all("tr")
col =[v.text for v in rows[1].find_all("td")]
per.append(col[0])
rimawari.append(col[2])
uriage.append(pd.read_html(url)[10].iloc[2,1])
keitune.append(pd.read_html(url)[10].iloc[2,2])
df = pd.DataFrame({"時価総額":zika,"株価":kabuka,"売上高":uriage,"経常利益":keitune,"RER":per,"利回り":rimawari,"出来高":dekidaka})
dfs = pd.concat([book_df,df],axis = 1)
dfs.to_excel(r"エクセルのパス")
今後の課題
・for文のリスト内包表記がよくわからない
・HTMLタグの<> A <> <> <>のAの場所のみの抽出がわからない
・どの方法で抽出するのがベスト(時間が早い)かわからない