수정사항
1.메세지박스 형태로 결국 UI 변경
2.금 가격 조회는 원화로, 따로 로그를 저장하지 않고 실시간으로 출력
3.환율 정보 역시 따로 로그를 통해 저장하는 방식으로 변경
4.최종적으로 그래프를 그리는 것은 주식 그래프 한정
5. 금,국내주식, 환율 등의 경제지표를 실시간으로 검색하고 DB에 저장하는 식으로 할당구현 성공했음.
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox#UI를 위한 모듈 설치
from datetime import datetime#시간
from operator import itemgetter
import sqlite3 #DB를 다루기 위한 SQL 데이터 모듈
import requests
from bs4 import BeautifulSoup #DB와 크롤링 데이터 저장을 위한 모듈 설치
import matplotlib.pyplot as plt #변동가격 그래프 출력을 위한 모듈 설치
import pandas as pd
import seaborn as sns
def create_connection(db_file): #DB파일 연결 채킹 함수
try:
conn = sqlite3.connect(db_file)
return conn
except sqlite3.Error as e:
print(e)
return None
def create_stock_table(conn): #주식 DB 테이블 생성 함수
try:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS stock_prices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
company_code TEXT,
company_name TEXT,
price REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
except sqlite3.Error as e:
print(e)
def insert_record_stock(conn, company_code, company_name, price, timestamp): #크롤링된 데이터를 DB에 삽입하는 함수
try:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO stock_prices (company_code, company_name, price,timestamp)
VALUES (?, ?, ?, ?)
""", (company_code, company_name, price,timestamp))
conn.commit()
except sqlite3.Error as e:
print(e)
def create_exchange_talbe(conn): #환율 DB 생성 함수
try:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS exchange_rate (
id INTEGER PRIMARY KEY AUTOINCREMENT,
country_name TEXT,
country_code TEXT,
exchange_rate REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
except sqlite3.Error as e:
print(e)
def insert_record_exchange(conn, contry_name, contry_code, exchange_rate,timestamp ): #크롤링된 환율을 DB에 삽입하는 함수
try:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO exchange_rate (country_name, country_code, exchange_rate,timestamp)
VALUES (?, ?, ?, ?)
""", (contry_name, contry_code, exchange_rate,timestamp ))
conn.commit()
except sqlite3.Error as e:
print(e)
def Fluctuating_graph(conn, company_name): #크롤링된 데이터를 기반으로 DB에서 데이터를 가져와 그래프를 그리는 함수
cursor = conn.cursor()
cursor.execute("SELECT timestamp, price FROM stock_prices WHERE company_name = ? ORDER BY timestamp",
(company_name,))
data = cursor.fetchall()
print("Fetched data:", data)
data = [(timestamp, price) for timestamp, price in data if price != 0]
if data:
timestamps, prices = zip(*data)
plt.figure(figsize=(15, 10))
plt.plot(timestamps, prices, marker='o', linestyle='-', color='b')
plt.xlabel('Timestamp')
plt.ylabel('Price (KRW)')
plt.title(f'stock price change graph for {company_name}')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
else:
print(f"데이터베이스에 '{company_name}' 회사의 가격 정보가 없습니다.")
def show_graph(conn, company_info):
if company_info is None or company_info.strip() == "":
messagebox.showinfo("알림", "검색기록이 없습니다.")
return
company_name, _ = company_info.split()
Fluctuating_graph(conn, company_name)
def get_bs_obj_stock(company_code): #주식 관련 정보 크롤링
url = "https://finance.naver.com/item/main.nhn?code=" + company_code
result = requests.get(url)
bs_obj = BeautifulSoup(result.content, "html.parser")
return bs_obj
def get_price(company_code): #주식 크롤링 데이터 HTML 코드 규격 맞추기
bs_obj = get_bs_obj_stock(company_code)
no_today = bs_obj.find("p", {"class": "no_today"})
blind_now = no_today.find("span", {"class": "blind"})
return blind_now.text
def get_gold():
url='https://finance.naver.com/marketindex/'
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
result= requests.get(url)
gold_obj = BeautifulSoup(result.content, "html.parser")
gold_now=gold_obj.select_one("a.head.gold_domestic > div.head_info > span.value")
messagebox.showinfo(f"현재 금 가격", f"{timestamp}\n{gold_now.text}\\(원) 입니다." )
def get_exchange(country_code):#환율 크롤링 데이터 HTML 코드 규격 맞추기
url= "https://finance.naver.com/marketindex/"
result = requests.get(url)
ex_obj = BeautifulSoup(result.content, "html.parser")
country_code = country_code.lower()
exchage_now = ex_obj.select_one(f"a.head.{country_code}>div.head_info>span.value")
return exchage_now.text
def main():
db_file = "economic_indicators_searching.db"
conn = create_connection(db_file)
if conn is not None:
con_stock=create_stock_table(conn)
con_exchange=create_exchange_talbe(conn)
root = tk.Tk()
root.title("주요 경제 종목 검색")
# 레이아웃을 조직하기 위해 프레임을 만들고 설정
frame = tk.Frame(root)
frame.pack(padx=100, pady=100)
label = tk.Label(frame, text="검색대상과 종목코드를 입력하세요 (띄어쓰기로 구분)")
label.grid(row=0, column=0, columnspan=2, pady=5)
entry = tk.Text(frame, width=30, height=3, font=("Helvetica", 15)) # 필요에 따라 width와 height, font 값을 조절하세요
entry.grid(row=1, column=0, columnspan=2, pady=5)
search_stock_button = tk.Button(frame, text="검색", command=lambda: search_stock(conn, entry.get("1.0", tk.END)))
search_stock_button.grid(row=2, column=0, pady=5, sticky="W")
stock_graph_button = tk.Button(frame, text="검색 주식 그래프", command=lambda: show_graph(conn, entry.get("1.0", tk.END)))
stock_graph_button.grid(row=2, column=1, pady=5, sticky="W")
search_history_button = tk.Button(frame, text="검색 기록", command=lambda: show_search_history(conn))
search_history_button.grid(row=3, column=0, pady=5, sticky="W")
search_reset_button = tk.Button(frame, text="초기화", command=lambda: reset_database(conn))
search_reset_button.grid(row=3, column=1, pady=5, sticky="W")
exchange_button = tk.Button(frame, text="국가별 환율 조회", command=lambda:search_exchange(conn, entry.get("1.0", tk.END)))
exchange_button.grid(row=4, column=0, pady=5, sticky="W")
exchange_log_button = tk.Button(frame, text="환율 검색기록 조회", command=lambda :exchange_log(conn))
exchange_log_button.grid(row=4, column=1, columnspan=1, pady=5, sticky="W")
ex_info_button = tk.Button(frame, text="환율 검색 양식", command=lambda: exchange_info())
ex_info_button.grid(row=5, column=0, columnspan=1, pady=5, sticky="W")
exit_button = tk.Button(frame, text="금 가격", command=lambda :get_gold())
exit_button.grid(row=5, column=1, columnspan=1, pady=5, sticky="W")
root.mainloop()
conn.close()
def reset_database(conn): #검색기록 초기화 함수
answer = messagebox.askquestion("초기화", "검색 기록을 삭제하시겠습니까?")
if answer == "yes":
cursor_stock = conn.cursor()
cursor_stock.execute("DELETE FROM stock_prices")
conn.commit() #주식 검색기록 삭제
consor_exchange=conn.cursor()
consor_exchange.execute("DELETE FROM exchange_rate")
conn.commit() #환율 검색기록 삭제
messagebox.showinfo("초기화 완료", "검색 기록이 삭제되었습니다.")
else:
messagebox.showinfo("초기화 취소", "초기화가 취소되었습니다.")
def show_search_history(conn): #검색기록 조회 함수
cursor = conn.cursor()
cursor.execute("SELECT company_name, company_code, price, timestamp FROM stock_prices ORDER BY timestamp")
data = cursor.fetchall()
if data:
data_sorted = sorted(data, key=itemgetter(3)) # Sort by timestamp
history_text = "검색기록\n\n"
for record in data_sorted:
history_text += f"{record[0].upper()}, {record[2]}원, 검색 시간: {record[3]}\n"
history_window = tk.Toplevel()
history_window.title("Search History")
history_label = tk.Label(history_window, text=history_text)
history_label.pack()
else:
messagebox.showinfo("검색 기록", "검색 기록이 없습니다.")
def exchange_log(conn): #환율 검색기록 조회 함수
cursor = conn.cursor()
cursor.execute("SELECT country_name, country_code, exchange_rate, timestamp FROM exchange_rate ORDER BY timestamp")
data = cursor.fetchall()
if data:
data_sorted = sorted(data, key=itemgetter(3)) # Sort by timestamp
history_text = "검색기록\n\n"
for record in data_sorted:
history_text += f"{record[0]}, {record[1].upper()} {record[2]}원, 검색 시간: {record[3]}\n"
history_window = tk.Toplevel()
history_window.title("exchange Search History")
history_label = tk.Label(history_window, text=history_text)
history_label.pack()
else:
messagebox.showinfo("검색 기록", "검색 기록이 없습니다.")
def search_stock(conn, entry_text): #주식 검색 함수
global root
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
try:
# 입력받은 텍스트를 회사명과 종목코드로 분리
company_name, company_code = entry_text.split()
# 입력된 종목코드가 숫자로 이루어진지 확인
if not company_code.isdigit():
messagebox.showerror("오류", "종목코드 입력이 잘못되었습니다.")
return
# 주식 가격을 가져오기
price = get_price(company_code)
# 가져온 가격이 비어있으면 종목코드가 유효하지 않음을 의미
if price == "":
messagebox.showerror("오류", "종목코드 입력이 잘못되었습니다.")
else:
# 유효한 경우 주식 정보를 보여주고 그래프 및 레코드 삽입
messagebox.showinfo("검색 종목 정보", f"회사: {company_name}\n코드: {company_code}\n가격: {price}\n검색시간: {timestamp}" )
insert_record_stock(conn, company_code, company_name, price, timestamp)
except ValueError:
messagebox.showerror("오류", "잘못된 입력입니다. 회사명과 코드를 모두 입력하세요.")
except Exception as e:
messagebox.showerror("오류", f"입력이 잘못되었습니다. 오류 메시지: {str(e)}")
def search_exchange(conn, entry_text): #환율 검색 함수
global root
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
try:
# 입력받은 텍스트를 국가명과 국가코드로 분리
country_name, coutry_code = entry_text.split()
# 입력된 종목코드가 문자로 이루어진지 확인
if country_name.isdigit():
messagebox.showerror("오류", "국가 코드 입력이 잘못되었습니다.")
return
# 환율 가져오기
exchange = get_exchange(coutry_code)
# 가져온 가격이 비어있으면 국가 코드가 유효하지 않음을 의미
if exchange == "":
messagebox.showerror("오류", "국가 코드 입력이 잘못되었습니다.")
else:
# 유효한 경우 입력한 국가의 환율 정보를 보여주고 그래프 및 레코드 삽입
messagebox.showinfo("검색 종목 정보", f"국가: {country_name} { coutry_code}\n환율: {exchange}\n검색시간: {timestamp}" )
insert_record_exchange(conn, country_name, coutry_code, exchange, timestamp)
except ValueError:
messagebox.showerror("오류", "잘못된 입력입니다. 국가명과 코드를 모두 입력하세요.")
except Exception as e:
messagebox.showerror("오류", f"입력이 잘못되었습니다. 오류 메시지: {str(e)}")
def exchange_info():
ex_info_window = tk.Toplevel()
ex_info_window.title("환율 종목 검색 양식")
currencies = """
미국 USD
유럽연합 EUR
일본 JPY
중국 CNY
홍콩 HKD
대만 TWD
영국 GBP
캐나다 CAD
브라질 BRL
싱가포르 SGD
"""
ex_info_label = tk.Label(ex_info_window, text=currencies, padx=20, pady=20, font=("Arial", 12))
content_width = ex_info_label.winfo_reqwidth()
content_height = ex_info_label.winfo_reqheight()
ex_info_window.geometry(f"{content_width+300}x{content_height + 50}")
ex_info_label.pack()
if __name__ == "__main__":
main()'python' 카테고리의 다른 글
| 파이썬 기반의 채팅 프로그램(2024-03-08) (2) | 2024.03.08 |
|---|---|
| (개발일지) 주식 크롤링 프로그램, 23-11-28 갱신 (0) | 2023.11.28 |
| [Python] itertools 모듈 (0) | 2023.09.22 |
| (개발중) 크롤링을 통한 네이버 주식 정보 출력-2023/09/20 (0) | 2023.09.20 |
| [Python] isdecimal(), isdigit(), isnumeric() 함수에 대한 설명 (0) | 2023.09.20 |