concurrent.futures を使用した並列処理(非同期処理?)で3回に1回くらいの割合で動作が重たくなります。
処理の流れを簡易的に書きます
- 会社四季報のサイトへアクセス
- 特定の情報を抽出
- 必要な情報のみに整形
- データフレームに格納
- csvファイルに書き込み
とゆう流れです。
requestsではhtmlを取得できなかったので、seleniumを使用しています。
経過時間はこんな感じです。(スレッド別に実験しているのでその他の結果は下記に記述しています。)
1, 15.708
2, 31.406
3, 11.567
4, 11.047
5, 38.353
上記の試行回数 5 回の内、2回は倍近くの時間がかかっています。下記に記載しているスレッド数を変更して実験した結果もほぼ同じ結果になっています。
なぜ、時間が遅くなるのか分かりません。
どなたか教えていただけないでしょうか?
コードの実行は下記のように行います。
python
1# リストに銘柄コードを記述 2brand_code = [2440, 2445, 2449, 2453, 2454] 3parallel(brand_code)
コードはこちらです。
python
1from bs4 import BeautifulSoup as bs 2import pandas as pd 3import requests 4import lxml 5import re 6 7# ファイルのパスを調べる 8import os 9import datetime 10 11from selenium import webdriver 12from webdriver_manager.chrome import ChromeDriverManager 13import time 14 15# 非同期処理 16import concurrent.futures 17 18def op(): 19 """seleniumでhtmlを取得する 20 21 requestsで要素を取得できない場合にこちらを実行する 22 """ 23 options = webdriver.ChromeOptions() 24# userdata_dir = 'C://chrome_profile' # C直下 25 26 options.add_argument("start-maximized") # ウィンドウの最大化 27 options.add_argument('--disable-extensions') # エクステンション無効 28 options.add_argument("--disable-dev-shm-usage") # シェアドメモリの保持場所が/dev/shm => /tmp(安定する?) 29 30 # ヘッドレスモードのハッピーセット.. 31 options.add_argument("--headless") 32 options.add_argument("--no-sandbox") 33 options.add_argument("--disable-browser-side-navigation") 34 options.add_argument("--disable-gpu") 35 36 # プロファイルの確認 37# if os.path.isdir(userdata_dir): 38# options.add_argument('--user-data-dir=' + userdata_dir) 39# else: 40# os.makedirs(userdata_dir, exist_ok=True) 41# options.add_argument('--user-data-dir=' + userdata_dir) 42 43 # 通知を無効にする 44 prefs = {"profile.default_content_setting_values.notifications" : 2} # https://stackoverflow.com/questions/41400934/ 45 options.add_experimental_option("prefs", prefs) 46 # 上部のバー「自動制御中です」を非表示にする 47 options.add_experimental_option("excludeSwitches", ['enable-automation']); 48 49 driver = webdriver.Chrome(ChromeDriverManager().install(), options=options) 50 51 return driver 52 53################################################## 54 55def crawl(code): 56 57 try: 58 driver = op() 59 url = 'https://shikiho.jp/stocks/' + str(code) 60 driver.get(url) 61 # 安定重視、読み込み完了を効率的する 62 time.sleep(3) 63 64 html = driver.page_source.encode('utf-8') 65 driver.quit() 66 67 try: 68 soup = bs(html, "lxml") 69 except: 70 soup = bs(html, "html.parser") 71 72 # 業績エリアの大枠を指定 73 if soup.find(class_="matrix"): 74 section = soup.find(class_="matrix") 75 # 個別に要素を抽出 76 if section.find_all(class_="default"): 77 items = section.find_all(class_="default") 78 79 # 企業名、上場年を抽出 80 if soup.find(class_="name"): 81 comp_name = soup.find(class_="name").text 82 else: 83 comp_name = code 84 listed = soup.find(class_="block cfx").find('dd').text 85 86 # 決算情報でデータがある箇所を抽出 87 perf = [] 88 for item in items: 89 if re.search(r'^連[1-9]{2}.[1-9]{,2}\s[1-9]{,3}', item.text): 90 perf.append(item.text) 91 92 Settlement = [] # 決算月 93 sales = [] # 売上高 94 opin = [] # 営業利益 95 netin = [] # 純利益 96 oneprofit = [] # 1株益 97 onediv = [] # 1株配 98 99 for indiv in perf: 100 sp = indiv.split(' ') 101 if sp[0]: 102 Settlement.append(sp[0]) 103 else: 104 Settlement.append(0) 105 if sp[1]: 106 sales.append(sp[1]) 107 else: 108 sales.append(0) 109 if sp[2]: 110 opin.append(sp[2]) 111 else: 112 opin.append(0) 113 if sp[4]: 114 netin.append(sp[4]) 115 else: 116 netin.append(0) 117 if sp[5]: 118 oneprofit.append(sp[5]) 119 else: 120 oneprofit.append(0) 121 if sp[6]: 122 onediv.append(sp[6]) 123 else: 124 onediv.append(0) 125 126 value = { 127 '企業名' :comp_name, 128 '銘柄コード':code, 129 '上場年' :listed, 130 '決済年' :Settlement, 131 '売上高' :sales, 132 '営業利益' :opin, 133 '純利益' :netin, 134 '1株益' :oneprofit, 135 '1株配' :onediv 136 } 137 138 df = pd.DataFrame(value) 139 return df 140 else: 141 return code 142 else: 143 return code 144 except Exception as e: 145 print(code, e) 146 pass 147 148def parallel(brand_code): 149 """並列処理 150 151 パソコンへの負荷を考えると3つの処理を6スレッドで実行するのが効率的 152 """ 153 # 時間測定スタート 154 ts = time.perf_counter() 155 156 csv = pd.read_csv('data/stock_price_first.csv', encoding="shift-jis") 157 max_workers = 6 158 with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: 159 futures = [executor.submit(crawl, code)for code in brand_code] 160 161 ng_code = [] 162 for i, future in enumerate(concurrent.futures.as_completed(futures)): 163 if type(future.result()) is pd.core.frame.DataFrame: 164 csv = csv.append(future.result(), ignore_index=True) 165 print(f'\r{i+1}処理中..\n', end='') 166 else: 167 ng_code.append(future.result()) 168 169 print('終了しました♪\n取得不可コード → '+str(ng_code)) 170 csv.to_csv('data/stock_price_first.csv', index=False, encoding="shift-jis") 171 172 td = time.perf_counter() 173 print(round((td - ts), 3))
実験結果です。
python
1計測時間測定 25銘柄の情報を取得するテスト 3 41Thread 51, 42.419 62, 27.715 73, 54.995 84, 31.309 95, 50.37 10mean : 41.362 11 122Thread 131, 16.757 142, 24.572 153, 16.718 164, 18.627 175, 35.863 18mean : 22.51 19 203Thread 211, 15.708 222, 31.406 233, 11.567 244, 11.047 255, 38.353 26mean : 21.62 27 284Thread 291, 10.812 302, 31.905 313, 11.247 324, 11.483 335, 38.787 34mean : 20.85 35 365Thread 371, 5.816 382, 32.761 393, 6.193 404, 5.798 415, 36.418 42mean : 17.4 43 446Thread 451, 6.184 462, 6.557 473, 36.056 484, 5.988 495, 5.751 50mean : 12.11 51 527Thread 531, 36.037 542, 5.896 553, 5.94 564, 38.025 575, 5.796 58mean : 18.34 59 608Thread 611, 5.802 622, 34.36 633, 6.652 644, 5.567 655, 36.327 66mean : 17.74 67 689Thread 691, 5.784 702, 5.62 713, 35.45 724, 7.162 735, 5.472 74 7510Thread 761, 5.338 772, 7.352 783, 53.905 794, 5.587 805, 5.409 81