import requests from bs4 import BeautifulSoup from typing import List, Dict, Any import time # 네이버 파이낸스 주요 뉴스 NAVER_FINANCE_NEWS_URL = "https://finance.naver.com/news/mainnews.naver" def fetch_market_news() -> List[Dict[str, str]]: """ 네이버 금융 '주요 뉴스' 크롤링 반환: [{"title": "...", "link": "...", "summary": "...", "date": "..."}, ...] """ try: headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36" } resp = requests.get(NAVER_FINANCE_NEWS_URL, headers=headers, timeout=10) resp.raise_for_status() soup = BeautifulSoup(resp.content, "html.parser", from_encoding="cp949") # 주요 뉴스 리스트 추출 # 구조: div.mainNewsList > ul > li articles = [] news_list = soup.select(".mainNewsList ul li") for li in news_list: # 썸네일 있을 수도 있고 없을 수도 있음 dl = li.select_one("dl") if not dl: continue # 제목 (dd.articleSubject > a) subject_tag = dl.select_one(".articleSubject a") if not subject_tag: continue title = subject_tag.get_text(strip=True) link = "https://finance.naver.com" + subject_tag["href"] # 요약 (dd.articleSummary) summary_tag = dl.select_one(".articleSummary") summary = "" press = "" date = "" if summary_tag: # 불필요한 태그 제거 for child in summary_tag.select(".press, .wdate"): if "press" in child.get("class", []): press = child.get_text(strip=True) if "wdate" in child.get("class", []): date = child.get_text(strip=True) child.extract() summary = summary_tag.get_text(strip=True) articles.append({ "title": title, "link": link, "summary": summary, "press": press, "date": date, "crawled_at": time.strftime("%Y-%m-%d %H:%M:%S") }) return articles except Exception as e: print(f"[StockLab] Scraping failed: {e}") return [] def fetch_major_indices() -> Dict[str, Any]: """ KOSPI, KOSDAQ, KOSPI200 등 주요 지표 (네이버 금융 홈) """ url = "https://finance.naver.com/" try: headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36" } resp = requests.get(url, headers=headers, timeout=5) resp.raise_for_status() soup = BeautifulSoup(resp.content, "html.parser", from_encoding="cp949") indices = [] # 네이버 금융 홈 상단 'section_stock_market' 내부 # top_kospi, top_kosdaq, top_kpsi200 targets = [ {"key": "KOSPI", "selector": ".kospi_area"}, {"key": "KOSDAQ", "selector": ".kosdaq_area"}, {"key": "KOSPI200", "selector": ".kospi200_area"}, ] for t in targets: area = soup.select_one(t["selector"]) if not area: continue # 현재가 num_tag = area.select_one(".num") value = num_tag.get_text(strip=True) if num_tag else "" # 등락 (num2) -> 화살표, 부호 확인 필요 # num2 (상승), num3 (하락) 클래스가 유동적일 수 있음 # .num2 (수치), .num3 (퍼센트) # 보통 .nk (수치), .per (퍼센트) 로 나뉨 change_val_tag = area.select_one(".num2") change_pct_tag = area.select_one(".num3") change_val = change_val_tag.get_text(strip=True) if change_val_tag else "" change_pct = change_pct_tag.get_text(strip=True) if change_pct_tag else "" # 상승/하락 부호 처리 (화살표 텍스트나 클래스 보고 판단해야 함) # 단순 텍스트로는 '상승 10.5' 처럼 들어있을 수 있음 # 여기서는 단순 텍스트값 그대로 리턴 # 방향(상승/하락) 클래스 확인 direction = "" if area.select_one(".bu_p"): direction = "red" # 상승 elif area.select_one(".bu_m"): direction = "blue" # 하락 indices.append({ "name": t["key"], "value": value, "change_value": change_val, "change_percent": change_pct, "direction": direction }) return {"indices": indices, "crawled_at": time.strftime("%Y-%m-%d %H:%M:%S")} except Exception as e: print(f"[StockLab] Indices scraping failed: {e}") return {"indices": [], "error": str(e)}