import time import requests import xml.etree.ElementTree as ET class NewsCollector: """동기 뉴스 수집 (Google News RSS)""" @staticmethod def get_market_news(query="주식 시장"): url = f"https://news.google.com/rss/search?q={query}&hl=ko&gl=KR&ceid=KR:ko" try: resp = requests.get(url, timeout=5) root = ET.fromstring(resp.content) items = [] for item in root.findall(".//item")[:5]: title = item.find("title").text items.append({"title": title, "source": "Google News"}) return items except Exception as e: print(f"[News] Collection failed: {e}") return [] class AsyncNewsCollector: """비동기 뉴스 수집 + 5분 캐싱""" def __init__(self): self._cache = None self._cache_time = 0 self._cache_ttl = 300 # 5분 self._stock_cache = {} # {stock_name: (items, timestamp)} def get_market_news(self, query="주식 시장"): """동기 인터페이스 (하위 호환)""" now = time.time() if self._cache and (now - self._cache_time) < self._cache_ttl: return self._cache result = NewsCollector.get_market_news(query) self._cache = result self._cache_time = now return result async def get_market_news_async(self, query="주식 시장"): """비동기 뉴스 수집 (aiohttp + 캐싱)""" now = time.time() if self._cache and (now - self._cache_time) < self._cache_ttl: return self._cache try: import aiohttp url = f"https://news.google.com/rss/search?q={query}&hl=ko&gl=KR&ceid=KR:ko" async with aiohttp.ClientSession() as session: async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as resp: content = await resp.read() root = ET.fromstring(content) items = [] for item in root.findall(".//item")[:5]: title = item.find("title").text items.append({"title": title, "source": "Google News"}) self._cache = items self._cache_time = now return items except ImportError: return self.get_market_news(query) except Exception as e: print(f"[News Async] Collection failed: {e}") if self._cache: return self._cache return self.get_market_news(query) async def get_stock_news_async(self, stock_name, max_items=3): """종목별 뉴스 수집 (5분 캐싱) stock_name: 종목 이름 (e.g. '삼성전자', 'SK하이닉스') """ now = time.time() cached = self._stock_cache.get(stock_name) if cached and (now - cached[1]) < self._cache_ttl: return cached[0] try: import aiohttp import urllib.parse query = urllib.parse.quote(f"{stock_name} 주가") url = f"https://news.google.com/rss/search?q={query}&hl=ko&gl=KR&ceid=KR:ko" async with aiohttp.ClientSession() as session: async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as resp: content = await resp.read() root = ET.fromstring(content) items = [] for item in root.findall(".//item")[:max_items]: title_el = item.find("title") if title_el is not None and title_el.text: items.append({"title": title_el.text, "source": "Google News"}) self._stock_cache[stock_name] = (items, now) return items except Exception as e: print(f"[News] 종목 뉴스 수집 실패 ({stock_name}): {e}") return [] def clear_stock_cache(self): """종목 뉴스 캐시 전체 초기화""" self._stock_cache.clear()