株価上昇の初動を捉えるには、株ドラゴンの出来高急増 初動 銘柄 ランキングを見たほうが良い
以前の記事で株ドラゴンの年初来安値からの値上がり率ランキングのランキング推移を可視化しました。
ただ、株価上昇の初動を捉えるということを考えると出来高急増銘柄を監視しておいたほうが良いのではと思い、前回のプログラムを少し修正して、可視化してみました。
株ドラゴンの出来高急増 初動 銘柄 ランキングページをスクレイピング
モジュールのインポート
まずは必要なモジュールをインポートします。スクレイピングにはBeautifulSoupというモジュールを用います。
import requests
from datetime import datetime, timedelta
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
URLの確認
株ドラゴンの出来高急増 初動 銘柄 ランキングのページのURLは、以下のように(日付)の箇所に表示されるランキングの日付が入るようなリンク担っています。
(日付)の書き方は2019/01/01というようなスラッシュで区切られた日付になります。
url = https://www.kabudragon.com/ranking/(日付)/dekizou200.html
したがって、以下のように日付を1日ずつかえながら変えながら各日付のurlにアクセスしていくことにします。
st_date = '2019/01/01'
end_date = '2019/01/10'
st_date = datetime.strptime(st_date, '%Y/%m/%d')
end_date = datetime.strptime(end_date, '%Y/%m/%d')
while st_date <= end_date:
date = datetime.strftime(st_date, '%Y/%m/%d' )
url = 'https://www.kabudragon.com/ranking/{}/yl_age200.html'.format(date)
st_date = st_date + timedelta(days = 1)
print(url)
各ページの情報をデータフレーム化
先程のURLにアクセスし、そのページのhtmlタグをみると以下の項目がテーブル構造で存在していることがわかります。
「順位、コード、名称、市場、日付、終値、前日比(円)、前日比(%)、5日平均比出来高急増率、出来高、高値、安値」
ページの詳細な構成がどうなっているかの説明は特にしませんが、下記のコードでpandasのデータフレームとして取得することができます。
if文を入れている理由は、土日などの市場が休場しているときはデータが存在していないため、エラーを回避するためです。
url = 'https://www.kabudragon.com/ranking/2019/01/10/dekizou200.html'.format(date)
soup = BeautifulSoup(requests.get(url).text, "lxml")
if len(soup.body.find_all('tr')) > 20:
start_line = [i for i in range(14,20) if soup.body.find_all('tr')[i].text.splitlines()[1] == '順位']
items = [v.text.splitlines() for i, v in enumerate(soup.body.find_all('tr')) if i > start_line[0]]
cols = ['', '順位','コード', '名称', '市場','日付','終値','前日比(円)','前日比(%)','5日平均比出来高急増率','出来高','高値','安値']
df = pd.DataFrame(np.array(items[0]).reshape(1,-1),columns=cols)
for i in range(1,200):
df_add = pd.DataFrame(np.array(items[i]).reshape(1,-1),columns=cols)
df = pd.concat([df, df_add],axis = 0)
else:
print('休場')
df.head()
上記を実行すると以下のような結果が得られると思います。

このテーブルを取得するコードを関数化し、先程の日付を1日ずつ変更していくコードと合わせると以下のように書くことができます。
def get_table(url):
soup = BeautifulSoup(requests.get(url).text, "lxml")
if len(soup.body.find_all('tr')) > 20:
start_line = [i for i in range(14,20) if soup.body.find_all('tr')[i].text.splitlines()[1] == '順位']
items = [v.text.splitlines() for i, v in enumerate(soup.body.find_all('tr')) if i > start_line[0]]
cols = ['', '順位','コード', '名称', '市場','日付','終値','前日比(円)','前日比(%)','5日平均比出来高急増率','出来高','高値','安値']
df = pd.DataFrame(np.array(items[0]).reshape(1,-1),columns=cols)
print(items[0]) #テーブルの1行目だけ表示
for i in range(1,200):
df_add = pd.DataFrame(np.array(items[i]).reshape(1,-1),columns=cols)
df = pd.concat([df, df_add],axis = 0)
return df,True
else:
print('休場')
return np.nan, False
#データ取得開始日と終了日を設定
st_date = '2019/01/01' #データ取得開始日
end_date = '2019/01/10' #データ取得終了日
st_date = datetime.strptime(st_date, '%Y/%m/%d') #datetimeのフォーマットに変換
end_date = datetime.strptime(end_date, '%Y/%m/%d') #datetimeのフォーマットに変換
dfs = []
while st_date <= end_date:
date = datetime.strftime(st_date, '%Y/%m/%d')
url = 'https://www.kabudragon.com/ranking/{}/dekizou200.html'.format(date)
st_date = st_date + timedelta(days = 1)
df, flg = get_table(url) #各日付のデータフレームと市場が休場かどうかのフラグを取得(休場のときはflg = False)
if flg:
dfs.append(df) #市場が空いているときのみデータフレームをdfsに追加していく
おそらく以下のような結果が出力されると思います。
※テーブルの1行目だけがprintされるようにしています。
各ページのテーブルをdfsに追加していっています。
2019/01/01
休場
2019/01/02
休場
2019/01/03
休場
2019/01/04
['', '1', '7851', 'カワセコンピュータサプライ', '東2', '1/4(金)', '424', '+45', '+11.87%', '437.12%', '171,000', '424', '381']
2019/01/05
休場
2019/01/06
休場
2019/01/07
['', '1', '7895', '中央化学', 'JQ', '1/7(月)', '296', '+61', '+25.96%', '413.72%', '147,200', '313', '259']
2019/01/08
['', '1', '3377', 'バイク王&カンパニー', '東2', '1/8(火)', '170', '+17', '+11.11%', '468.65%', '1,898,400', '189', '169']
2019/01/09
['', '1', '9969', 'ショクブン', '東2', '1/9(水)', '177', '+40', '+29.20%', '488.34%', '6,395,800', '186', '140']
2019/01/10
['', '1', '7602', 'カーチスホールディングス', '東2', '1/10(木)', '183', '+5', '+2.81%', '472.99%', '492,000', '228', '177']
取得した1日毎のデータフレームを統合します。
df = pd.concat(dfs).reset_index(drop = True)
print(df.shape)
>> (1000, 13)
これで複数の日にちのデータフレームを1つのデータフレームに変換することができ、1000行のデータフレームになっていると思います。
初登場の日付を取得する
先程のデータはすべての日付のランキングを1つのテーブルにしているので、毎日ランキングに登場しているような銘柄はテーブルの中に何度も登場しています。
初めてランキングに登場した日付を知りたいので、重複している銘柄は初登場の日付以外は削除することとします。
#初登場の日付のみに絞る
df = df.drop(df[df.duplicated(subset = 'コード')].index,axis=0)
pritn(df.shape)
>>(816, 13)
すると816行のデータになったので、184データは重複していたということになります。
初登場の日付を「年/月/日」の形式に変換する
これまでの処理で、データは初登場した銘柄と日付が取得できているはずですが、日付が月/日(曜日)というような表記になっているため扱いづらいです。なので「年/月/日」のフォーマットに変換します。
#初登場の日付をdatetimeの日付にする
df['date'] = [datetime.strptime('2019/' + i.split('(')[0],'%Y/%m/%d') for i in df['日付']]
df.head()
すると以下のようなデータフレームにdateという列が追加されているはずです。

株ドラゴンの出来高急増 初動ランキング 2019年7月ランキング初登場銘柄
それでは2019年1月1日〜2019年7月31日の期間においてランキングに登場した銘柄のデータテーブルを取得し、そのうち7月1日〜7月31日の登場した銘柄だけをピックアップしたいと思います。
これまでのコードをまとめる形で書きたいと思います。
# -*- coding: utf-8 -*-
import requests
from datetime import datetime, timedelta
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
def get_table(url):
soup = BeautifulSoup(requests.get(url).text, "lxml")
if len(soup.body.find_all('tr')) > 20:
start_line = [i for i in range(14,20) if soup.body.find_all('tr')[i].text.splitlines()[1] == '順位']
items = [v.text.splitlines() for i, v in enumerate(soup.body.find_all('tr')) if i > start_line[0]]
cols = ['', '順位','コード', '名称', '市場','日付','終値','前日比(円)','前日比(%)','5日平均比出来高急増率','出来高','高値','安値']
df = pd.DataFrame(np.array(items[0]).reshape(1,-1),columns=cols)
print(items[0])
for i in range(1,200):
df_add = pd.DataFrame(np.array(items[i]).reshape(1,-1),columns=cols)
df = pd.concat([df, df_add],axis = 0)
return df,True
else:
print('休場')
return np.nan, False
st_date = '2019/01/01'
end_date = '2019/07/31'
st_date = datetime.strptime(st_date, '%Y/%m/%d')
end_date = datetime.strptime(end_date, '%Y/%m/%d')
dfs = []
while st_date <= end_date:
date = datetime.strftime(st_date, '%Y/%m/%d' )
print(date)
url = 'https://www.kabudragon.com/ranking/{}/dekizou200.html'.format(date)
st_date = st_date + timedelta(days = 1)
df, flg = get_table(url)
if flg:
dfs.append(df)
#複数ページのデータを1つのデータフレームにする
df = pd.concat(dfs).reset_index(drop = True)
#初登場の日付のみに絞る
df = df.drop(df[df.duplicated(subset = 'コード')].index,axis=0)
#初登場の日付をdatetimeの日付にする
df['date'] = [datetime.strptime('2019/' + i.split('(')[0],'%Y/%m/%d') for i in df['日付']]
#対象の期間の指定
target_start = '2019/07/01'
target_end = '2019/07/31'
data = df[(df['date']>=target_start) & (df['date']<=target_end)]
output = data.loc[:,['コード', '名称', '市場', 'date']]
output = output.rename(columns = {'コード':'code', '名称':'name','市場':'market'}).set_index('date', drop = True)
print(output)
実行すると少し時間がかかりますが、以下のような結果が得られていると思います。
全部で50銘柄となります。
今後の動きにチェックしていくのが良いかと思います。
date | code | name | market |
---|---|---|---|
2019-07-01 | 8566 | リコーリース | 東1 |
2019-07-02 | 2974 | 大英産業 | 福岡 |
2019-07-02 | 8890 | レーサム | JQ |
2019-07-03 | 9384 | 内外トランスライン | 東1 |
2019-07-04 | 7065 | ユーピーアール | 東2 |
2019-07-05 | 4931 | 新日本製薬 | 東マ |
2019-07-05 | 9621 | 建設技術研究所 | 東1 |
2019-07-05 | 8585 | オリエントコーポレーション | 東1 |
2019-07-08 | 7677 | ヤシマキザイ | 東2 |
2019-07-08 | 2769 | ヴィレッジヴァンガードコーポレーション | JQ |
2019-07-08 | 9007 | 小田急電鉄 | 東1 |
2019-07-08 | 8267 | イオン | 東1 |
2019-07-08 | 8904 | サンヨーハウジング名古屋 | 東1 |
2019-07-10 | 3110 | 日東紡 | 東1 |
2019-07-10 | 8331 | 千葉銀行 | 東1 |
2019-07-10 | 9987 | スズケン | 東1 |
2019-07-10 | 8074 | ユアサ商事 | 東1 |
2019-07-11 | 4927 | ポーラ・オルビスホールディングス | 東1 |
2019-07-16 | 2201 | 森永製菓 | 東1 |
2019-07-16 | 4443 | Sansan | 東マ |
2019-07-16 | 7908 | KIMOTO | 東1 |
2019-07-18 | 8342 | 青森銀行 | 東1 |
2019-07-18 | 3708 | 特種東海製紙 | 東1 |
2019-07-18 | 8346 | 東邦銀行 | 東1 |
2019-07-18 | 7751 | キヤノン | 東1 |
2019-07-19 | 3436 | SUMCO | 東1 |
2019-07-22 | 2502 | アサヒグループホールディングス | 東1 |
2019-07-22 | 6197 | ソラスト | 東1 |
2019-07-22 | 7067 | ブランディングテクノロジー | 東マ |
2019-07-23 | 4445 | リビン・テクノロジーズ | 東マ |
2019-07-24 | 5631 | 日本製鋼所 | 東1 |
2019-07-25 | 6358 | 酒井重工業 | 東1 |
2019-07-26 | 8864 | 空港施設 | 東1 |
2019-07-26 | 2002 | 日清製粉グループ本社 | 東1 |
2019-07-26 | 4206 | アイカ工業 | 東1 |
2019-07-26 | 3408 | サカイオーベックス | 東1 |
2019-07-26 | 4559 | ゼリア新薬工業 | 東1 |
2019-07-26 | 2413 | エムスリー | 東1 |
2019-07-26 | 7266 | 今仙電機製作所 | 東1 |
2019-07-29 | 8355 | 静岡銀行 | 東1 |
2019-07-29 | 7911 | 凸版印刷 | 東1 |
2019-07-29 | 6861 | キーエンス | 東1 |
2019-07-30 | 8591 | オリックス | 東1 |
2019-07-30 | 8113 | ユニ・チャーム | 東1 |
2019-07-30 | 7012 | 川崎重工業 | 東1 |
2019-07-31 | 9934 | 因幡電機産業 | 東1 |
2019-07-31 | 2281 | プリマハム | 東1 |
2019-07-31 | 5929 | 三和ホールディングス | 東1 |
2019-07-31 | 4902 | コニカミノルタ | 東1 |
2019-07-31 | 1973 | NECネッツエスアイ | 東1 |
良かったと思ったらポチッと押してもらえるとうれしいです。


おすすめ書籍
私がスクレイピングで参考にした書籍はこちらになります。
もっと勉強したいという方は参考にしていただければと思います。
コメント
記事読ませていただきました。
本記事内にあるコードを実行したところ、
latest.remove(i)
の部分ですべての証券コードが取り除かれているため、新しくラインクインした銘柄
の処理の実行が行われず、絞り込みが行えませんでした。(日時等の変更は行っていません。)
実際に使用されている全体コードを提示いただくことはできませんでしょうか。
NYSEさん、
読んでいただきありがとうございます。
少し確認しますのでお時間ください。
返信いただきありがとうございます
ご確認よろしくお願いいたします。
やや汚いコードですが、以下のように書けば再現できると思います。
一方で、本記事でやっている内容を大幅に変更しようと思っておりましたので、また記事が更新された際はそちらをご覧いただいた方が有用かと思います。
よろしくおねがいします。
# -*- coding: utf-8 -*-
import requests
from datetime import datetime, timedelta
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
import pickle
def get_table(url):
soup = BeautifulSoup(requests.get(url).text, "lxml")
if len(soup.body.find_all('tr')) > 20:
start_line = [i for i in range(14,20) if soup.body.find_all('tr')[i].text.splitlines()[1] == '順位']
items = [v.text.splitlines() for i, v in enumerate(soup.body.find_all('tr')) if i > start_line[0]]
#'値上がり'の列を'5日平均比出来高急増率'に変更
cols = ['', '順位','コード', '名称', '市場','日付','終値','前日比(円)','前日比(%)','5日平均比出来高急増率','出来高','高値','安値']
df = pd.DataFrame(np.array(items[0]).reshape(1,-1),columns=cols)
print(items[0])
for i in range(1,200):
df_add = pd.DataFrame(np.array(items[i]).reshape(1,-1),columns=cols)
df = pd.concat([df, df_add],axis = 0)
#以下の'値上がり'の列の処理が不要
'''
df['値上がり率(円)'] = [v.split('+')[1] for v in df['値上がり']]
df['値上がり率(%)'] = [v.split('+')[-1] for v in df['値上がり']]
df = df.drop('値上がり', axis = 1)
'''
return df,True
else:
print('休場')
return np.nan, False
def get_data(date, st_date, end_date):
dfs = []
dates = []
while date != end_date:
strp_date = datetime.strptime(date, '%Y/%m/%d') + timedelta(days = 1)
date = datetime.strftime(strp_date, '%Y/%m/%d' )
print(date)
#urlの変更
url = 'https://www.kabudragon.com/ranking/{}/dekizou200.html'.format(date)
df, flg = get_table(url)
if flg:
dfs.append(df)
dates.append(strp_date)
# 対象期間でランキングに入った銘柄コードの辞書作成
dic = {}
for df in dfs:
keys = df['コード']
values = df['名称']
buf = dict(zip(keys, values))
dic.update(buf)
return dic, df
# ランキングの推移を可視化したい日付(開始と終わり)を設定
date = '2019/05/19'
st_date = '2019/05/19'
end_date = '2019/05/23'
dic, df = get_data(date, st_date, end_date)
#一旦、銘柄コードの辞書をpickleで保存
sd = st_date.replace('/','')
ed = end_date.replace('/','')
with open('dic_{}-{}.pkl'.format(sd,ed), mode='wb') as f:
pickle.dump(dic, f)
date = '2019/05/19'
st_date = '2019/05/19'
end_date = '2019/05/24'
dic, df = get_data(date, st_date, end_date)
#一旦、銘柄コードの辞書をpickleで保存
sd = st_date.replace('/','')
ed = end_date.replace('/','')
with open('dic_{}-{}.pkl'.format(sd,ed), mode='wb') as f:
pickle.dump(dic, f)
#過去の銘柄コードの辞書をロード
pre_ed = '20190523'
with open('dic_{}-{}.pkl'.format(sd,pre_ed),'rb') as f:
pre_dic = pickle.load(f)
#過去の銘柄辞書と最新の銘柄辞書を比較して、追加されたものだけにする
latest = list(dic.keys())
previous = list(pre_dic.keys())
new_code = latest
for i in previous:
latest.remove(i)
#新しくランクインした銘柄
for k in new_code:
code = k
name = dic[code]
print(code, name)
_df = df.set_index('コード') #一旦銘柄コードをindexにする
_df = _df.loc[new_code,:] #new_codeの銘柄コードを抽出する
_df['出来高'] = _df['出来高'].str.replace(',','') #出来高のカンマを消す(数値に変換する時にエラーになるため)
_df['出来高'] = _df['出来高'].astype(int) #出来高の数字が文字列なので、数値に変換する
_df[_df['出来高'] > 500000] #出来高が500,000以上の銘柄を抽出する。
コード提示いただきありがとうございます。
for i in previous:
latest.remove(i)
#新しくランクインした銘柄
for k in new_code:
code = k
name = dic[code]
print(code, name)
_df = df.set_index(‘コード’) #一旦銘柄コードをindexにする
_df = _df.loc[new_code,:] #new_codeの銘柄コードを抽出する
_df[‘出来高’] = _df[‘出来高’].str.replace(‘,’,”) #出来高のカンマを消す(数値に変換する時にエラーになるため)
_df[‘出来高’] = _df[‘出来高’].astype(int) #出来高の数字が文字列なので、数値に変換する
_df[_df[‘出来高’] > 500000] #出来高が500,000以上の銘柄を抽出する。
この部分のネスト関係が理解出来ないため、うまく抽出出来ませんでした。
for i in previous:
latest.remove(i)
この部分は、if文を入れないとエラーになるのですが、再現しませんでしょうか。
エラー内容は、latest配列にremove対象のiが存在しない というエラーです。
記事を新たに書かれるということなので、返信頂けなくても大丈夫です。
記事およびソースコード参考にさせていただきます。
何度もコメント申し訳ありません。
_df = df.set_index(‘コード’) #一旦銘柄コードをindexにする
_df = _df.loc[new_code,:] #new_codeの銘柄コードを抽出する
_df[‘出来高’] = _df[‘出来高’].str.replace(‘,’,”) #出来高のカンマを消す(数値に変換する時にエラーになるため)
_df[‘出来高’] = _df[‘出来高’].astype(int) #出来高の数字が文字列なので、数値に変換する
_df[_df[‘出来高’] > 500000] #出来高が500,000以上の銘柄を抽出する。
この部分の
_df[_df[‘出来高’] > 500000] での絞り込みが上手くできていない見たいです。
最終的な出力結果は_dfに格納されていると考えていますが、相違はないでしょうか。
処理後の_dfに対して,”5000000″より大きいでフィルタリングかけたところ、
記事内に提示されている結果と若干の相違はありますが、近似値が出力結果に出ました。
以下は、出力結果です。
4833 1563800
3782 1707300
3656 9114900
4712 5298200
2193 4121400
3099 4216900
3762 693900
3989 1374500
5955 782700
3995 837600
2432 5780700
8165 1078200
8848 54905800
以上、ご確認お願いいたします。
長い返信になりますが、失礼します。
わかりずらい書き方をしてしまい恐縮なんですが、最終結果をデータフレームで取得するためには、最後の行の結果をちゃんと変数に代入する必要があります。
例えばoutputというデータフレームに入れるのであれば、
output = _df[_df[‘出来高’] > 500000]
とする必要があります。
_df[_df[‘出来高’] > 500000] の処理だけでは結果がどこにも代入されていません。(ただ、jupyter notebookで実行するとその結果が表示されるので、私はその結果を表示してしまっておりました。)
ただ、NYSEさんの出力結果をみると上記が原因ではないように思います。
そこでいくつか確認させていただきたいのですが、その前に2点コードに不適切な部分があったので、修正します。
まず、以下のようにlatestのうしろに.copy()を付けます。
その後、latest.remove(i)ではなく、new_code.remove(i)とします。
こうしないとlatestとnew_codeが同じリストとして扱われてしまうため、latest.remove(i)をしたことで、new_codeも同じようにnew_code.remove(i)の処理がされてしまいます。
#過去の銘柄辞書と最新の銘柄辞書を比較して、追加されたものだけにする
latest = list(dic.keys())
previous = list(pre_dic.keys())
new_code = latest.copy()
for i in previous:
new_code.remove(i)
幸い、2箇所間違えていたので、以下の銘柄コードを抽出するところではnew_codeは追加された銘柄のみになっているので、結果は同じになるはずです。
上記を踏まえて以下の出力結果がどんな値になっているか教えていただけますか?
参考までに私の値を記載しておきます。
print(len(dic)) => 831
print(len(pre_dic)) => 680
print(len(latest)) => 831
print(len(previous)) => 680
print(len(new_code)) => 151
print(df.shape) => (200,13)
print(_df.shape) => (151,12)
print(output.shape) => (8,12)
ご丁寧に回答いただきありがとうございます。
#過去の銘柄辞書と最新の銘柄辞書を比較して、追加されたものだけにする
latest = list(dic.keys())
previous = list(pre_dic.keys())
new_code = latest.copy()
for i in previous:
new_code.remove(i)
記述を変更しても、for文の中でのremove処理で同様のエラーが出力されました。
そのため、
for i in previous:
if i in new_code:
new_code.remove(i)
のような判定を追加してコードの出力を行いました。
私の環境での出力値は以下となりました。
print(len(dic)) => 200
print(len(pre_dic)) => 200
print(len(latest)) => 200
print(len(previous)) => 200
print(len(new_code)) => 174
print(df.shape) => (200,13)
print(_df.shape) => (174,12)
此方の環境はVisualStudio2019上実行しています。
何度もご丁寧に返答いただき、大変恐縮です。
以上、よろしくお願いします。
dic, pre_dicともに200というのが気になります。
1ページ分しか取得できていないので、おそらくget_table関数かget_data関数(返信のところで記述したもの)のどちらかがうまく機能していないように思います。
返信だと何故かコードがすべて前揃えになってしまって見にくく、正しくお伝えできないので、今週末に本文の方をきれいなコードに書き直すようにしますね。
申し訳ないです。
topixさん
こちらこそご丁寧に解説頂いているのになかなか結果が出せず申し訳ありません。
週末にコードの書き直しをいただけるとのことなので、お待ちしております。
何度もご丁寧に本当にありがとうございます。
プログラム更新しました。
前回とかなり変わっていて、出来高の絞り込みなどはないので、もし別途知りたいことがあれば言ってください。
topixさん
コードの更新ありがとうございます。
出力結果と同じ内容のデータが取得できました。
大変助かりました。
また、何か質問事項があればここに書かせていただきます。
本当に助かりました。 ありがとうございます。
良かったです。
またいつでもご質問してください。
[…] […]
[…] […]