見出し画像

pythonを用いて購入銘柄を選定してみよう!その4(webスクレイピングを用いて、株式コード一覧を取得しよう)

こんにちは、蝉の店です。今回からはpythonを用いた購入銘柄を選定するプログラムについて紹介していきます。(その1~その3までは機械学習がメインでした)

どのように選定するのか、選定するための判断資料はどうするのか、等疑問点はたくさんあると思いますが、それについては今後深掘りしていければと思います。

その4では、webスクレイピングを用いて株式コードを取得するプログラムを作成しましたので紹介いたします。本記事はあくまで株式データを取得、分析、判断するまでの準備段階となりますのでご承知おきください。

webスクレイピングがあまり慣れていない人にとっては(私もですが)、良い練習となると思います。

本記事の目的

私が作成しているプログラムの全体像は以下のようになっております。

・野村証券HPから、東証プライム市場の銘柄コード一覧を取得する
・かぶたんHPから、安値、高値などの株式データを銘柄ごとに取得する
・株式データを使ってデータマイニングを行い、購入するべき銘柄と購入価格、売却価格を算出する

今回は上記の太字部分についてご紹介いたします。

webスクレイピングで、東証プライム市場の銘柄コード一覧を取得

今回は、野村証券HPより東証プライム市場の銘柄コードの一覧を取得します。野村證券HPには銘柄一覧、各銘柄ごとの市場が記載されているページがあります。

野村証券HPより

もちろんですが、webスクレイピングをしてもよいかは確認する必要があります。URLの末尾にrobots.txtを入力すると、以下のような表示がされます。今回のスクレイピング対象は、「/meigara/nomura/…」なのでこちらには反しておりません。

User-agent: *
Disallow: /support/inquiry/modal/
Disallow: /hometrade_c/nomura_reports/pdf/
Disallow: /cgi-bin/
Disallow: /wp/mail/service/totalconsulting/pdf/

早速ですが、スクレイピングの実装について以下に紹介していきます。

   list_stockCode = []

   int_beforeNumber = 0
   int_countUpNumber = 50
   int_maxBeforeNumber = 5000

   while int_beforeNumber < int_maxBeforeNumber:

       try:
           # 野村証券の株銘柄一覧ページのURL
           # (int_beforeNumber + 1) 番目~ (int_beforeNumber + 50) 番目の銘柄を表示する
           # int_beforeNumber は int_countUpNumber(50) ずつカウントアップする
           str_url = 'https://advance.quote.nomura.co.jp/meigara/nomura/msearch.exe?F=nlist&KEY2=T&MAXDISP=50&GO_BEFORE=&BEFORE={}'.format(int_beforeNumber)
           response_nomuraHP = requests.get(url=str_url, headers=dict_headers)
           response_nomuraHP.raise_for_status()
           bs_nomuraHP = BeautifulSoup(response_nomuraHP.content, 'html.parser')
       except RequestException as err:
           return (None, -1)
       except Exception as err:
           return (None, -1)
       
       # _fw-n class で定義されている部分を抜き出す
       # [9999] 東証プライム のように記載されている
       resultSet_rows = bs_nomuraHP.find_all("span" , {"class":"_fw-n"})

       for row in resultSet_rows:
           if '東証プライム' in row.text:
               list_stockCode.append(row.text[1:5])

       # int_countUpNumber(50) ずつカウントアップする
       # 短時間での大量アクセスを防ぐため、1秒のsleepを入れる
       int_beforeNumber += int_countUpNumber
       time.sleep(1)

URLへの1アクセスごとに、50ずつの銘柄が表示されますので、50ごとにパラメータ「int_beforeNumber」をカウントアップさせています。

また、短時間での大量アクセスを防ぐため、1秒のスリープ処理を入れております。処理した結果は変数「list_stockCode」に格納されます。

list_stockCodeの出力結果

次回予告

上記で取得した銘柄ごとに、株式データを取得してCSV出力を行うようなプログラムを紹介いたします。

全量ソースコード

以下、ソースコード全量を紹介します。有料ではありますがログ出力機能や、iniファイルでの設定値定義等も行っております。コピペで動作するようなソースコードですので、もし宜しければご検討くださいませ。

動作環境・・・python 3.7.7

main.py

from getStockCode import getStockCode
from logging import getLogger, config
import os,sys,json,shutil,configparser

config.dictConfig(json.loads(open('conf/logConf.json', encoding='UTF-8').read()))
logger = getLogger(__name__)
config_ini = configparser.ConfigParser()
config_ini.read('conf/config.ini', encoding='UTF-8')

# main関数
# コンソール上で、python main.pyと入力して実行すると
# 最初に呼ばれる関数です。
if __name__ == '__main__':

   logger.info('start main.py')

   # int_getStockCode_flag ... 銘柄コード一覧を更新するかのフラグ 1...YES 0...NO
   # int_getStockData_flag ... 株式データを更新するかのフラグ 1...YES 0...NO
   # int_mlpr_flag ... 月の最安値の相関係数から買うべき銘柄を予測するシミュレーションの実行フラグ 1...YES 0...NO
   read_MAIN = config_ini['MAIN']
   int_getStockCode_flag = read_MAIN.getint('int_getStockCode_flag')
   int_getStockData_flag = read_MAIN.getint('int_getStockData_flag')
   int_mlpr_flag = read_MAIN.getint('int_mlpr_flag')
   logger.debug('int_getStockData_flag = %d', int_getStockData_flag)
   logger.debug('int_mlpr_flag = %d', int_mlpr_flag)


   # getStockCodeクラス
   # 銘柄コードの一覧を取得するプログラム
   # list_stockCode ... list型で銘柄コードを格納(int型)
   # int_result ...  1 成功
   #                -1 エラー(処理修了)
   if int_getStockCode_flag == 1:
       logger.info('start getStockCode')
       list_stockCode, int_result = getStockCode.execute()

       if int_result == 1:
           logger.info('getStockCode → SUCCESS')
       else:
           logger.error('getStockCode → ERROR  END PROCESSING')
           sys.exit()

   else :
       logger.info('do not execute getStockCode')
getStockCode\getStockCode.py

import requests
import json
from bs4 import BeautifulSoup
from requests.exceptions import RequestException
from logging import getLogger, config
import configparser
import time

config.dictConfig(json.loads(open('conf/logConf.json', encoding='UTF-8').read()))
logger = getLogger(__name__)
config_ini = configparser.ConfigParser()
config_ini.read('conf/config.ini', encoding='UTF-8')

dict_headers = {
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
   + "AppleWebKit/537.36 (KHTML, like Gecko) "
   + "Chrome/103.0.0.0 Safari/537.36"
}

# 実行関数
# 株式銘柄の一覧をWEBスクレイピングで取得する
# list_stockCode ... list型で銘柄コードを格納(int型)
# int_result ...  1 成功
#                -1 エラー(処理修了)
def execute():

   logger.info('start function execute()')

   list_stockCode, int_result = getStockCode()
   if int_result == -1:
       return (None, -1)

   logger.info("getStockCode → SUCCESS")
   logger.debug('code:{}'.format(list_stockCode))
   return (list_stockCode, 1)


# 銘柄コード取得関数
# 野村証券のHPから、銘柄コードの一覧を取得する
# 対象市場はiniファイル内で定義されたstr_stockMarketにて記載する
# list_stockCode ... list型で銘柄コードを格納(int型)
# int_result ...  1 成功
#                -1 エラー(処理修了)
def getStockCode():

   logger.debug('start function getStockCode()')

   # str_stockMarket ... 銘柄コード取得の対象市場 ex...東証プライム
   read_GETSTOCKCODE = config_ini['GETSTOCKCODE']
   str_stockMarket = read_GETSTOCKCODE.get('str_stockMarket')
   logger.debug('str_stockMarket: %s', str_stockMarket)

   list_stockCode = []

   int_beforeNumber = 0
   int_countUpNumber = 50
   int_maxBeforeNumber = 5000

   while int_beforeNumber < int_maxBeforeNumber:

       try:
           # 野村証券の株銘柄一覧ページのURL
           # (int_beforeNumber + 1) 番目~ (int_beforeNumber + 50) 番目の銘柄を表示する
           # int_beforeNumber は int_countUpNumber(50) ずつカウントアップする
           str_url = 'https://advance.quote.nomura.co.jp/meigara/nomura/msearch.exe?F=nlist&KEY2=T&MAXDISP=50&GO_BEFORE=&BEFORE={}'.format(int_beforeNumber)
           response_nomuraHP = requests.get(url=str_url, headers=dict_headers)
           response_nomuraHP.raise_for_status()
           bs_nomuraHP = BeautifulSoup(response_nomuraHP.content, 'html.parser')
       except RequestException as err:
           logger.error('HTTP status ERROR:{}'.format(err))
           return (None, -1)
       except Exception as err:
           logger.error('Unexpected ERROR:{}'.format(err))
           return (None, -1)
       
       # _fw-n class で定義されている部分を抜き出す
       # [9999] 東証プライム のように記載されている
       resultSet_rows = bs_nomuraHP.find_all("span" , {"class":"_fw-n"})

       for row in resultSet_rows:
           if str_stockMarket in row.text:
               list_stockCode.append(row.text[1:5])

       # int_countUpNumber(50) ずつカウントアップする
       # 短時間での大量アクセスを防ぐため、1秒のsleepを入れる
       int_beforeNumber += int_countUpNumber
       time.sleep(1)


   logger.debug('end function getStockCode()')

   return (list_stockCode, 1)
conf\config.ini

[MAIN]
int_getStockCode_flag = 1
int_getStockData_flag = 1
int_mlpr_flag = 1

[GETSTOCKCODE]
str_stockMarket = 東証プライム
conf\logConf.json

{
   "version": 1,
   "disable_existing_loggers": false,
   "formatters": {
     "detailed": {
       "class": "logging.Formatter",
       "format": "%(asctime)s %(levelname)s %(message)s",
       "datefmt": "%Y-%m-%d %H:%M:%S"
     }
   },
   "handlers": {
     "__main__": {
       "class": "logging.FileHandler",
       "filename": "log/main.log",
       "level": "DEBUG",
       "mode": "w",
       "formatter": "detailed"
     },
     "getStockCode": {
       "class": "logging.FileHandler",
       "filename": "log/getStockCode.log",
       "level": "DEBUG",
       "mode": "w",
       "formatter": "detailed"
     }
   },
   "loggers": {
     "__main__": {
       "level": "DEBUG",
       "handlers": ["__main__"]
     },
     "getStockCode": {
       "level": "DEBUG",
       "handlers": ["getStockCode"]
     }
   },
   "root": {
   }
}
フォルダ構成

main.py
│
├─conf
│   ├config.ini
│   └logConf.json
│
├─getStockCode
│  └getStockCode.py
│
├─log
│  ├getStockCode.log
│  └main.log

この記事が気に入ったらサポートをしてみませんか?