株価とスクレイピング②
目的:webスクレイピングの活用とエクセルへ出力
目標
1 日経平均プロフィルというwebページから、日経平均の銘柄・コードを取得
2 データを整形
3 エクセルへ出力
webスクレイピングのやり方について
前回はpandas_datareaderという、いくつかのサイトからデータを抜き出す専用のモジュールを使用した。
これでは、抜き出すデータやWEBサイトが限定されるので、どのWEBサイトでも使えるように、次のような手順でスクレイピングを行う必要がある。
1,対象のWEBページのHTMLをデータコピーして取得(クローリング)
2,WEBページの解析(パース)、特定のデータの抽出
3,抽出したデータの整形や保存など
スクレイピングに使用されるライブラリ
Requests
WEBページを取得することができる
Beautiful Soup
取得したWEBページの情報を解析(パース、parse)することができる
Selenium
人がWEBページを閲覧しているように、ブラウザソフト(WEBドライバ)を使用して、情報を取得、解析することができる。動作が遅い
日経平均について
日本経済新聞社が選定した225社の株価を係数をかけて除数で割ったもの
日経平均株価 = (株価 × 株価換算係数)の225社の合計/除数
日経平均プロフィルは日経平均の公式サイト、データの二次利用を禁止しているので、スクレイピングは明記されていないが、グレーゾーン
個人利用はOK
プログラム実行順
1,必要なものをインポート
内容
import pandas as pd データフレーム(2次元配列)を扱うライブラリ
import urllib.request urllib.request.urlopen(URL)でURLにアクセス
※取得できる情報は色々あるみたいだが、HTTPのことがまだわからない
from bs4 import BeautifulSoup
bs4ライブラリからBeautiful SoupをインポートHTML文字列(ファイル)を解析することができる。
urllib.request.urlopen(URL)でWebからHTMLファイルを取得して、それをBeautiful Soup 4(bs4)に渡すとBeautiful Soupオブジェクトを生成する
そのオブジェクトを検索したり抽出したりすることで、必要なデータを抜き出す(スクレイピング)ことができる
2,日経平均のサイトからHTMLを取得
内容
日経平均プロフィルの225銘柄一覧ページのURLを指定
urllib.request.urlopen(url)で、指定したサイトのHTMLを取得、Beautiful Soup 4(bs4)に渡すとBeautiful Soupオブジェクトを生成することができる
このオブジェクトを検索したり抽出したりすることで、必要なデータを抜き出す(スクレイピング)ことができる
書式
BeautifulSoup(解析するデータ, 'html.parser')
html.parserはHTMLを解析するライブラリ、他にもlxmlやxmlを指定することができる
3,HTMLから欲しいデータを検索して取得する
内容
WEBページ上で、右クリックするとページのソースを表示でHTMLデータを確認することができる
今回は、銘柄名と証券コードを抜き出す
銘柄名は
<div class="hidden-xs col-sm-8"> 銘柄 </div>
証券コードは
<div class="col-xs-3 col-sm-1_5"> 証券コード </div>
のHTMLタグがついているので、find_all()関数で、タグがついているすべてのデータを取得する(find()で、最初のデータのみを取得できる)
書式
soup.find_all("div",class_="hidden-xs col-sm-8")
”div” divというタグがついた全データを対象にする
class_= class関数と重複するため、HTMLのclass指定はアンダーバーをつける
この書式だと、"hidden-xs col-sm-8"というクラス名を指定してデータを検索する
つまり、タグ div クラス名 hidden-xs col-sm-8 の全データを取得
タグはリスト指定すると、まとめてデータを取得することもできる、ただし、データは1行になる
HTMLは階層構造になっており、html – head – title - テキストのようになっているので、上位のタグを取得するとテキストも取得できる
※HTMLは未勉強なので詳しくはわからない
書式はいくつも書き方があり、nikkei_code = soup.select("div[class= 'col-xs-3 col-sm-1_5']")でも同じデータが取得できる
※いっぱいありすぎてよくわからなかった
find_all()で取得したデータは bs4.element.ResultSet という形でリスト化されるため、タグごとオブジェクトとして入ってしまう
そこで、テキスト部分を抜き出す必要がある
書式
retu_1 = [] 空のリスト作成
for i in nikkei_name:
retu_1.append(i.text)
要素を1つずつ取り出し、i.textでテキストだけを空のリストに入れていく
retu_2 = [a.text for a in nikkei_code]
retu_2は書式違い
新しくリストを作らず、元のリストに戻すこともできるが整理がつかなかったのでこの形で
4,データフレームを作成し、エクセルへ出力
内容
find_all()ですでにリスト化されているので、列名を指定しながら、1列ずつデータフレームに追加する
※注意 今回はデータ数が同じことがあらかじめわかっている
銘柄名と証券コードの中に、同じタグで「社名」「コード」という行が入ってしまうため、検索して削除する
書式
df = df[~df['証券コード'].str.contains('コード')]
df['証券コード'].str.contains('コード')で、証券コード列の「コード」の文字列を検索する
「~」は否定を意味する演算子
この書き方で、証券コードの列の「コード」という文字列がある行を削除するという意味になる
つまり、特定の文字を含む列を抜いたデータフレームを作り直すことができる。ただし、インデックス番号ごと抜けるのでインデックスを振りなおす
書式 インデックスをふり直し
df = df.reset_index(drop=True)
drop=Trueで元のインデックスを削除する
コード全体
import pandas as pd
import urllib.request
from bs4 import BeautifulSoup
url_1 = "https://indexes.nikkei.co.jp/nkave/index/component?idx=nk225"
data_1 = urllib.request.urlopen(url_1)
# HTMLを解析して取得
soup = BeautifulSoup(data_1, 'html.parser')
nikkei_name = soup.find_all("div",class_="hidden-xs col-sm-8")
nikkei_code = soup.find_all("div",class_="col-xs-3 col-sm-1_5")
#for文で回して、リストにテキストだけを残して入力していく
retu_1 = []
for i in nikkei_name:
retu_1.append(i.text)
retu_2 = [a.text for a in nikkei_code]
df = pd.DataFrame(retu_1,columns = ["銘柄"])
#mylist = list(zip(retu_1,retu_2))
df["証券コード"] = retu_2
df = df[~df['証券コード'].str.contains('コード')]
df = df.reset_index(drop=True)
print(df.head(10))
df.to_excel(r"エクセルのパス")