2019년 3월 10일 일요일

naver 주식 내역 읽은 후 sqlite3에 저장하기 python


이번 내용은 앞에서 했던 두가지 내용을 합치는 내용입니다.
database 는 이전의 내용과 비슷하나 MarketInfoDb 하나의 클래스가 추가 되었습니다.

이전 내용은 아래 링크를 참고 하시기 바랍니다.
Python 에서 sqlite3 사용하기
https://swlock.blogspot.com/2019/02/python-sqlite3-dataframe.html

아래는 전체소스 입니다.

database.py
import os
import sqlite3
import datetime
import pandas as pd
from pandas import Series, DataFrame

#print(str(sqlite3.version))

class SqliteDb():
    def __init__(self):
        super().__init__()

    def connect(self, name):
        self.con = sqlite3.connect(name)
        self.cursor = self.con.cursor()
        return self.cursor

    def make_table(self, table ):
        self.cursor.execute("CREATE TABLE "+ table + "(idx INTEGER PRIMARY KEY)")

    def exists_table(self, table):
        try :
            self.cursor.execute("SELECT * FROM " + table)
            return True
        except :
            return False

    def exists_table_column(self, table, coulumn) :
        try :
            self.cursor.execute("SELECT %s FROM %s "%(coulumn, table))
            return True
        except :
            return False

    def add_column(self, table, column_name_type) :
        # add column https://www.sqlite.org/datatype3.html
        # add_column(TABLE_NAME,'column_name float')
        sql = "ALTER TABLE %s ADD COLUMN %s" % (table, column_name_type)
        self.cursor.execute(sql)

    def insert_data(self, table, values):
        # column 순서 대로 입력됨
        #insert_data(TABLE_NAME,"'2018-01-01',1000")
        sql = "INSERT INTO %s VALUES(%s)" % (table, values)
        self.cursor.execute(sql)

    def insert_data_with_cols(self, table, cols, values):
        # 원하는 column 에 원하는 값을 넣을때 사용
        #insert_data_with_cols("table_name","column_name1,column_name2","value1,value2")
        sql = "INSERT INTO %s(%s) VALUES(%s)" % (table, cols, values)
        self.cursor.execute(sql)

    def get_value(self, table, cols, condition):
        #value = get_value("table_name","colname","idx=value")
        sql = "SELECT %s FROM %s WHERE %s" % (cols, table, condition)
        cursor = self.cursor.execute(sql)
        row = cursor.fetchone()
        if row==None : return None
        if len(row)==0 : return None
        return row[0]
    
    def update_value(self, table, setvalue, condition):
        #update_value("table_name","colname=value","idx=value")
        sql = "UPDATE %s SET %s WHERE %s" % (table, setvalue, condition)
        self.cursor.execute(sql)

    def get_dataframe(self, table):
        df = pd.read_sql_query("SELECT * FROM %s" % table, self.con)
        return df

    def close(self):
        self.con.commit()
        self.con.close()

class MarketInfoDb(SqliteDb):
    def __init__(self):
        super().__init__()
        self.table_name = "market_info"
        self.col_name_date = "date"

    def connect(self, name):
        super().connect(name)
        if super().exists_table(self.table_name) == False :
            super().make_table(self.table_name)
            super().add_column(self.table_name, " %s text " % self.col_name_date)

    def insert_base(self, datetime, values, colname, force=False):
        if super().exists_table_column(self.table_name, colname) == False :
            super().add_column(self.table_name, " %s float " % colname)
        #이미 값이 있는지 확인
        #인덱스 획득
        idx = super().get_value(self.table_name,"idx","%s='%s'"%(self.col_name_date,datetime.date()))
        if( idx == None ):
            # 빈상태이다 추가함
            super().insert_data_with_cols(self.table_name,"%s,%s"%(self.col_name_date,colname),"'%s',%f"%(datetime.date(), values))
        else:
            # date는 존재함 
            # 값이 있는지 재확인
            value = super().get_value(self.table_name,colname,'idx=%d'%(idx))
            if( value == None ):
                #값이 없는 경우
                super().update_value(self.table_name,"%s=%f"%(colname,values),'idx=%d'%(idx))
            else:
                #값이 있음
                if force==True :
                    #강제 update
                    super().update_value(self.table_name,"%s=%f"%(colname,values),'idx=%d'%(idx))
                else :
                    #변경 못함
                    return False
        return True


    def insert_with_external_column_name(self, datetime, values, colname, force=False):
        return self.insert_base(datetime, values, colname, force)

    def get_dataframe_fromdb(self):
        return super().get_dataframe(self.table_name)
        


기본적으로 db에 값을 입력하는것은 insert_with_external_column_name 함수를 이용하게 됩니다. 내부적으로는 insert_base 함수를 호출하게 되는데 하는 역할은 날짜에 주식 값이 있는 확인해보고 없으면 추가하고 있다면 flase를 리턴하게 됩니다. 문제는 db특성상 날짜에 해당 값이 있는지 확인하는 과정이 복잡합니다. 날짜가 존재하는 경우가 있고 날짜가 있으나 특정 주식 값만 존재하지 않을 가능성이 있기 때문입니다.

다음으로는 웹에서 데이터를 읽어서 db에 넣는 과정입니다. 이것은 이전 소스와 다르지 않습니다.

네이버 주식 데이터 읽기
https://swlock.blogspot.com/2019/03/naver-with-python.html

test.py
#getstockdata
import requests as re
from bs4 import BeautifulSoup
import datetime as date
import datetime
import time
import os
import database

PAGE_TEST = False

def naver_stock_crawling(db, code, name):
    # 코스피
    NAME ='종목 %s %s' % (code, name)
    print("%s Start crawling"%(NAME))
    url = 'https://finance.naver.com/item/sise_day.nhn?code=%s&page=%d'
    key_prefix = 'K%s_' % (code)
    count = 0
    need_to_break = False
    date_list = []
    endpage = 100000
    if PAGE_TEST==True: 
        endpage = 2
    for i in range(1,endpage):
        url_ = re.get(url % (code,i))
        url_ = url_.content
        html = BeautifulSoup(url_,'html.parser')
        add_data = False

        tds = html.find_all('td')
        index = 100
        for td in tds :
            try : 
                text = td.text.strip()

                try :
                    datetime.datetime.strptime(text, '%Y.%m.%d')
                    get_date = True
                except :
                    get_date = False

                if get_date==True :
                    index = 0
                    date_ = td.text.strip().replace('.','-')
                    if not date_ in date_list:
                        date_list.append(date_)
                    else:
                        #마지막 페이지
                        print("%s %d End page Done"%(NAME, count))
                        return

                if(index == 1):
                    #종가
                    add_data = True
                    d = td.text.strip().replace(',','')
                    d = float(d)
                    if PAGE_TEST==True:
                        print("%s %f %s close"%(date_,d,key_prefix))
                    else:
                        if db.insert_with_external_column_name(datetime.datetime.strptime(date_, '%Y-%m-%d'),d,key_prefix+'close')==False:
                            need_to_break = True
                if(index == 3):
                    #시가
                    add_data = True
                    d = td.text.strip().replace(',','')
                    d = float(d)
                    if PAGE_TEST==True:
                        print("%s %f %s open"%(date_,d,key_prefix))
                    else:
                        if db.insert_with_external_column_name(datetime.datetime.strptime(date_, '%Y-%m-%d'),d,key_prefix+'open')==False:
                            need_to_break = True
                if(index == 4):
                    #고가
                    add_data = True
                    d = td.text.strip().replace(',','')
                    d = float(d)
                    if PAGE_TEST==True:
                        print("%s %f %s high"%(date_,d,key_prefix))
                    else:
                        if db.insert_with_external_column_name(datetime.datetime.strptime(date_, '%Y-%m-%d'),d,key_prefix+'high')==False:
                            need_to_break = True
                if(index == 5):
                    #저가
                    add_data = True
                    d = td.text.strip().replace(',','')
                    d = float(d)
                    if PAGE_TEST==True:
                        print("%s %f %s low"%(date_,d,key_prefix))
                    else:
                        if db.insert_with_external_column_name(datetime.datetime.strptime(date_, '%Y-%m-%d'),d,key_prefix+'low')==False:
                            need_to_break = True
                if(index == 6):
                    #거래량
                    add_data = True
                    d = td.text.strip().replace(',','')
                    d = float(d)
                    if PAGE_TEST==True:
                        print("%s %f %s vol"%(date_,d,key_prefix))
                        count = count + 1
                    else:
                        if db.insert_with_external_column_name(datetime.datetime.strptime(date_, '%Y-%m-%d'),d,key_prefix+'vol')==False:
                            need_to_break = True
                        else:
                            count = count + 1
            except:
                pass

            index += 1

        if add_data == False:
            # 마지막 페이지
            print("%s %d End page Done"%(NAME, count))
            return
        if need_to_break == True :
            print("%s %d Done"%(NAME, count))
            return

db = database.MarketInfoDb()
db.connect("stockdata.db")

if PAGE_TEST==True :
    db = None

naver_stock_crawling(db,'005930','삼성전자')
naver_stock_crawling(db,'035420','NAVER')


if PAGE_TEST!=True :
    db.close()

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dbstock = database.MarketInfoDb()
dbstock.connect("stockdata.db")
df = dbstock.get_dataframe_fromdb()
print(df.head())
print(df.shape)


결과
종목 005930 삼성전자 Start crawling
종목 005930 삼성전자 5712 End page Done
종목 035420 NAVER Start crawling
종목 035420 NAVER 4046 End page Done
   idx        date  K005930_close  K005930_open     ...       K035420_open  K035420_high  K035420_low  K035420_vol
0    1  2019-03-08        43800.0       44450.0     ...           135500.0      136000.0     131500.0     303950.0
1    2  2019-03-07        44450.0       43400.0     ...           139500.0      139500.0     136500.0     254573.0
2    3  2019-03-06        44000.0       44000.0     ...           136500.0      139000.0     134500.0     403724.0
3    4  2019-03-05        44250.0       44600.0     ...           140000.0      140000.0     135000.0     387063.0
4    5  2019-03-04        44850.0       46000.0     ...           135000.0      139500.0     134000.0     743644.0

[5 rows x 12 columns]
(5712, 12)



댓글 없음:

댓글 쓰기