본문 바로가기

python

(개발중) 크롤링을 통한 네이버 주식 정보 출력-2023/09/20

import requests #특정 사이트에서 값을 요청하기 위한 모듈
from bs4 import BeautifulSoup #데이터 패싱을 위한 모듈 설치
import sqlite3 #SQL형태의 DB 모듈 설치를 위해 생성
from sqlite3 import Error #DB의 Erorr 모듈 설치
import time #실시간 데이터를 받아오기 위한 모듈 설치

# SQLite 데이터베이스 생성 또는 연결하는 함수
def create_connection(db_file):
    try:
        conn = sqlite3.connect(db_file) #기존 데이터베이스 파일과 연결 시도
        return conn #연결 성공시 연결한 DB 데이터베이스 안의 값을 반환
    except Error as e: #eroor 입력하기 귀찮으니 e 라는 변수변환
        print(e) #오류 출력
        return None #리턴값 0==오류출력

# 주식 가격을 저장할 테이블을 생성하는 함수 (테이블이 존재하지 않을 경우)
def create_table(conn):
    create_table_sql = """
    CREATE TABLE IF NOT EXISTS stock_prices (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        company_code TEXT, 
        company_name TEXT, 
        price TEXT, 
        timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
    );
    """
    # 테이블 안에 가격이 포함됐을경우
    # 테이블을 구분하는 핵심 키(ID)
    # 회사코드(text)
    # 회사 이름(text)
    # 종목 현재 가격(text)
    # 검색했던 실시간(DATETIME)
    try:
        cursor = conn.cursor() #연결한 DB에 커서 생성
        cursor.execute(create_table_sql) #위에서 설정한 스키마 구조대로 DB 테이블 생성
    except Error as e:#에러가 있으면
        print(e)#에러출력

# 레코드를 데이터베이스에 삽입하는 함수
def insert_record(conn, company_code, company_name, price):
    insert_sql = """
    INSERT INTO stock_prices (company_code, company_name, price) 
    VALUES (?, ?, ?);
    """#변수가 실제 저장될 값 개수만큼 공간을 할당해줘야 함
    try:
        cursor = conn.cursor() #커서 접속
        cursor.execute(insert_sql, (company_code, company_name, price)) #sql구조대를 받아와서 거기에 받아온 변수대로 입력
        conn.commit() #데이터 처리 끝낸 후 전송
    except Error as e:
        print(e)

# BeautifulSoup 객체를 얻는 함수
def get_bs_obj(company_code): #네이버...?
    url = "https://finance.naver.com/item/main.nhn?code=" + company_code #url형태가 주소+회사코드 형태라 자동 처리가 쉬움
    #url 사이트는 네이버 증권+종목번호 형태
    result = requests.get(url)
    bs_obj = BeautifulSoup(result.content, "html.parser") #html 패싱으로 참조변수형태로 선언
    return bs_obj #받은 값을 bs화 해서 반환

# 주식 가격을 얻는 함수
def get_price(company_code):
    bs_obj = get_bs_obj(company_code) #종목 코드드에 따라 검색하여 주식 가격을 가져옴
    no_today = bs_obj.find("p", {"class":"no_today"})
    #사이트에서 HTML 형태가 이런 형태이므로 긁어올때도 형태를 맞추어 줘야함
    #그리고 가져온 형태를 변수에 저장해야함
    blind_now = no_today.find("span", {"class":"blind"})
    return blind_now.text #받은 값을 텍스트 형태로 반환
# 테이블을 삭제하고 다시 생성하는 함수
def reset_table(conn):
    drop_table_sql = """
    DROP TABLE IF EXISTS stock_prices
    """
    # DROP은 데이터베이스 내용물을 전부 날리는 명령어다
    cursor = conn.cursor()
    cursor.execute(drop_table_sql)
    create_table(conn)

# 메인 함수
def main():
    db_file = "stock_prices.db"  # 데이터베이스 파일명
    conn = create_connection(db_file)  # 데이터베이스 연결 생성

    if conn is not None:
        create_table(conn)  # 테이블 생성

        try:
            while True:
                #검색 입력 형식 : 종목명 종목코드
                # 테이블 초기화 여부를 확인하고 초기화하도록 요청
                qustion_key = input("종목 검색기록을 초기화 하시겠습니까? (y/n)? \n")
                if qustion_key.lower() == 'y': #y를 입력할경우 검색기록 초기화
                    reset_table(conn)
                    print("검색 데이터베이스가 초기화되었습니다. 지금부터 새로 입력되는 정보는 검색기록에 남습니다. 종료하시려면 e를 입력해주세요.")
                    print("검색할 종목명을 입력해주세요 : ")
                elif qustion_key.lower() == 'n': #n 입력할경우 그냥
                    print("종목 검색을 계속합니다. 종료하시려면 ctrl+c를 입력해주세요. \n")
                    print("검색할 종목코드를 입력해주세요 : ")
                company_name, company_code= map(str,input().split())
                #수정사항2. 만약 특정 키워드 만으로 커피를 검색하게 할순 없을까?
                #수정사항3. 주식이 아니라 다양한 원자재(철, 구리, 석유, 커피) 등등의 정보를 여러 사이트에서 크롤링 해올수는 없을까?
                #수정사항4. 만약 변동폭이 5%이상이라면 크롬으로 알림을 보낼수는 없을까?(API 기능)
                #수정사항5. 미국주식과 국내주식, 원자재의 카테고리를 분리할수는 없을까? 분리한다면 무슨 키워드를 입력해야 그 카테고리로 이동하게 할 수 있을까?
                #수정사항6. 만약 검색기록을 최근 검색기록처럼 조회하는 방법을 구현할수는 없을까?


                price = get_price(company_code)
                print(f"회사명: {company_name} , 종목코드 : {company_code}, 현재가: {price}")
                insert_record(conn, company_code, company_name, price)


                # 종료 조건: Ctrl+C로 종료할 수 있도록 대기
                time.sleep(5)

        except KeyboardInterrupt:
            print("프로그램을 종료합니다.")
            conn.close()  # 데이터베이스 연결 닫기

if __name__ == "__main__":
    main()  # 메인 함수 호출