pagetakaBlog

最近リフォームと鳥取県日南町の記事多め。写真、PC、ネット等の話題も

東証株式相場表:PDFMinerで解析しMySQLへの流れ(7)恥ずかしながらのスクリプト

11/5追記:PDF解析に使うPDFMinerの使い方、一部修正

Python:「PDFMiner」と「PDFMiner.six」で東証株式相場表を解析

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へ放り込む、というのが現在の立ち位置です。
蓄積データを活用するのはこれからの課題です。