11/5追記:PDF解析に使うPDFMinerの使い方、一部修正
PDFMiner使い、東証株式相場表(日報、PDF)からデータ抽出し日別CSVにする
一度公開したものの、拾いきれてないデータがあったり、エラーが出だしたりして、スクリプト公開やめてました。
その後、必死の努力(効果を示すものではありません。爺個人の感想です)の結果、エラーを二つまで減らすことができました。これ以上、爺には無理(こういうのは、爺の判断早目)、ということで、「恥ずかしながらのスクリプト」を改めて公開することにしました。
二つのエラーは「東証株式相場表:PDFMinerで解析しMySQLへの流れ(5)まだエラーが二つ」に載せました。お判りのかた、ご教示くださいませ。
フォルダ位置、ファイル構成などは「東証株式相場表:PDFMinerで解析しMySQLへの流れ(1)」掲載の画像をご参照ください。
恥ずかしながらのPyhtonスクリプト
爺の錯誤が一杯ふくまれているかと。ご寛容・ご容赦のほど。自己責任でよろしくお願いします。お気づきのことがありましたら、コメントなどでお知らせください。
# PDFMinerで、東証株式相場表(日報、PDF)を解析し、CSVにします # 前提:解析対象PDFは指定フォルダへDL済 # 前提:CSV保管用フォルダ作成済 #正規表現、CSV、IO、time import csv import re import time from io import StringIO #PDF解析:PDFMiner from pdfminer.converter import TextConverter from pdfminer.layout import LAParams from pdfminer.pdfdocument import PDFDocument from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager from pdfminer.pdfpage import PDFPage from pdfminer.pdfparser import PDFParser #フォルダ内ファイル処理用 import glob #関数------------------------------------- def text_replace(txt): #不要文字削除、銘柄コード先頭文字位置取得 d_pos = [] iter_pos = [] text_page = txt.replace('千株[thous.shs.]\n千口/千個[thous.units.]\n','') text_page = text_page.replace('\t\t ','\t') #単頁内最初の銘柄コード以前のテキストは、正規表現で結果的に無視される #銘柄コードの文字始終位置、合致文字列の一日分を納める iter_pos = re.finditer(r'\t[0-9]{4}\t{1,3}.', text_page) for def_i in iter_pos: #銘柄コード先頭文字位置 d_pos.append(def_i.start()+1) return (text_page, d_pos) #ある程度整ったテキスト・データ、銘柄毎の先頭文字位置(銘柄コード) def delComma_str2num(txt): #加重平均と売買株数用 整形と数値化 txt = txt.replace(',','') if re.search(r'\d\,\d',txt) else txt #三桁区切りコンマ削除 r_txt = float(txt) if re.match(r'^[0-9]', txt) else txt return r_txt #本体------------------------------------- pdfs = glob.glob('pdf/*.pdf') pdfs = sorted(pdfs) #日付順整列 time_s = time.time() #時計計測開始 print('経過時間計測開始:') proM = 'TOKYO PRO Market銘柄' for a_pdf in pdfs: #日別PDF=a_pdf≒400ページ #東証株相場表のファイルをopenする fp = open(a_pdf, 'rb') print(a_pdf[4:12], '→', end = '') #date型変換 m_date = [int(a_pdf[4:8]),int(a_pdf[8:10]),int(a_pdf[10:12])] # 出力先をPythonコンソールするためにIOストリーム取得 outfp = StringIO() # PDFParserオブジェクト取得 parser = PDFParser(fp) # PDFDocumentオブジェクト取得 doc = PDFDocument(parser) rmgr = PDFResourceManager() # PDFResourceManagerオブジェクト取得 lprms = LAParams(boxes_flow=None) # LAParamsオブジェクト取得 ※パラメタFalseだと狂う device = TextConverter(rmgr, outfp, laparams=lprms) # TextConverterオブジェクト取得 iprtr = PDFPageInterpreter(rmgr, device) # PDFPageInterpreterオブジェクト取得 # 銘柄コードをINDEXとして使用するためslice位置取得のため各ページの銘柄コード文字位置取得 print('経過時間計測中 → ', end ='') m_txt = [] #単頁データテキスト m_pos = [] #単頁内の銘柄コード先頭文字位置 for pdf_page in PDFPage.get_pages(fp): #単日PDFを各ページ毎に処理する text_page = '' #PDFからのテキスト収納用、頁ごと iprtr.process_page(pdf_page) text_page = (outfp.getvalue()).replace('\n\n','\t') #「TOKYO PRO Market銘柄」をPDF内で見つける if re.search(proM,text_page): #処理対象ページがTOKYO PRO Market銘柄」を含む場合の処理 s_pos = re.search(proM,text_page).start() #警告エラー出るが無視 text_page = text_page[:s_pos-1] #頁内「TOKYO PRO Market銘柄」から後ろを消去 m_txt, m_pos = text_replace(text_page) # 関数戻り値 二つ break else: m_txt, m_pos = text_replace(text_page) # 関数戻り値 二つ #関数戻り値で、単頁テキスト(m_txt)、単頁内銘柄コード先頭文字位置(m_pos)を取得 outfp.close() # I/O閉じる device.close() # TextConverter閉じる fp.close() # fp閉じる time_m = time.time() print('経過', time_m-time_s,'秒 PDF解析完了') #銘柄情報一連格納の「text_page」をsliceし銘柄別に配列に納める datas = [] #データ用リスト収納用 for a_pos in m_pos[::-1]: #逆順にスライス ※前から実行し文字削除すると文字位置が狂う a_slice = m_txt[a_pos:] a_slice = a_slice.replace('\n','') # type: ignore #一銘柄内の個別項目を配列収納 a_split = a_slice.split('\t') #データ収納 #銘柄コードと売買単位の間に1文字表示(第2項目になっている)が時々ある→削除 a_split if a_split[1].isdecimal() else a_split.pop(1) #0始まりの第2項目(a_split[1])削除 #不揃い項目がある1行データ排除------------------------- if len(a_split) < 17: continue #必要項目抽出→1行データとして格納---------------------- if a_split[1].startswith('1') : #第2項目が取引単位(先頭が「1」)を目印 another_p = [] another_p.append(int(a_pdf[4:12])) #市場日 another_p.append(int(a_split[0])) #&銘柄コード another_p.append(int(a_split[1])) #取引単位 for i in a_split[3:11]: #前場始値~後場終値 i = i.replace(',','') if re.search(r'\d\,\d',i) else i #三桁区切りコンマ削除 i = float(i) if re.match(r'^[0-9]', i) else i #数字はfloat変換 another_p.append(i) # この間、気配、前日比スキップ another_p.append(delComma_str2num(a_split[13])) #加重平均 関数往復 another_p.append(delComma_str2num(a_split[14])) #売買株数 関数往復 # この後、売買金額スキップ datas.append(another_p) #1銘柄分追記 m_txt = m_txt[:a_pos-len(m_txt)] #格納済み分(この時点の末尾1銘柄分)を削除し次のforへ。 m_pos.clear() #不要になった銘柄コード位置リスト初期化=処理対象の一日分 datas = sorted(datas, key = lambda x: x[1]) #銘柄コード昇順にする # ↑key設定で第2項目が銘柄コードなので、x[1](0始まり) #CSV書きだし 日別 # f = open('csv/' + a_pdf[4:12] + '.csv','w',encoding = 'CP932',newline = '') #shift_JIS用 f = open('csv/' + a_pdf[4:12] + '.csv','w',encoding ='utf_8', newline = '') writer = csv.writer(f,delimiter = ',',quoting = csv.QUOTE_NONNUMERIC) writer.writerows(datas) f.close() time_e = time.time() #時計計測終了 print(time_e-time_s,'秒 CSV書きだし\n') print('::::::::::::::::::::::::::::::::::::::::::CSV書き出し、全体終了です')
MySQLへ放り込むまで三つのステップ
HTMLのように、すぐ結果を表示できるわけではありません。Python使用環境を整えたり、XAMPPインストール、MySQLのテーブル設定などが済まないと…ほとんど何も起こらないかと…ええ。
東証株式相場表をDLし、PDFMinerでCSV化し、「LOAD DATA INFILE」でMySQLへ放り込む、というのが現在の立ち位置です。
蓄積データを活用するのはこれからの課題です。