# -*- coding: utf-8 -*- """ AI 뉴스 & 허깅페이스 트렌딩 분석 웹 앱 (Flask 버전) - 완전판 파일명: app.py 실행 방법: 1. pip install Flask requests beautifulsoup4 lxml gunicorn 2. python app.py 3. 브라우저에서 http://localhost:8080 접속 프로덕션 실행: gunicorn -w 4 -b 0.0.0.0:8080 app:app """ from flask import Flask, render_template_string, jsonify, request import requests from bs4 import BeautifulSoup import json from datetime import datetime from typing import List, Dict, Optional import os import sys # Flask 앱 초기화 app = Flask(__name__) app.config['JSON_AS_ASCII'] = False # 한글 JSON 지원 # ============================================ # HTML 템플릿 (완전판) # ============================================ HTML_TEMPLATE = """ AI 뉴스 & 허깅페이스 트렌딩 분석 시스템

🤖 AI 뉴스 & 허깅페이스 트렌딩

실시간 AI 산업 동향 분석 시스템 📊

{{ stats.total_news }}
📰 총 뉴스
{{ stats.categories }}
📁 카테고리
{{ stats.hf_models }}
🤗 HF 모델
{{ stats.hf_spaces }}
🚀 HF 스페이스
{% for category, articles in news_by_category.items() %}
📌 {{ category }} {{ articles|length }}건
{% for article in articles %}
{{ loop.index }}. {{ article.title }}
📅 {{ article.date }} 📰 {{ article.source }}
🔗 기사 전문 보기
{% endfor %}
{% endfor %}
🤗 허깅페이스 트렌딩 모델 TOP 10
{% if hf_models|length > 0 %}
{% for model in hf_models[:10] %}
{{ loop.index }}. {{ model.name }}
🏷️ {{ model.task }}
📊 다운로드: {{ "{:,}".format(model.downloads) }}
❤️ 좋아요: {{ "{:,}".format(model.likes) }}
🔗 모델 페이지 방문
{% endfor %}
{% else %}
⚠️ 모델 데이터를 불러오지 못했습니다.
{% endif %}
⏰ 마지막 업데이트: {{ timestamp }}
""" # ============================================ # AINewsAnalyzer 클래스 # ============================================ class AINewsAnalyzer: """AI 뉴스 및 허깅페이스 트렌딩 분석기""" def __init__(self, fireworks_api_key: Optional[str] = None, brave_api_key: Optional[str] = None): """ Args: fireworks_api_key: Fireworks AI API 키 (선택) brave_api_key: Brave Search API 키 (선택) """ self.fireworks_api_key = fireworks_api_key or os.getenv('FIREWORKS_API_KEY') self.brave_api_key = brave_api_key or os.getenv('BRAVE_API_KEY') # 뉴스 카테고리 정의 self.categories = { "산업동향": ["산업", "기업", "투자", "인수", "파트너십", "시장", "MS", "구글", "아마존", "소프트뱅크"], "기술혁신": ["기술", "모델", "알고리즘", "개발", "연구", "논문", "삼성", "SAIT"], "제품출시": ["출시", "공개", "발표", "서비스", "제품", "챗GPT", "소라", "팬서"], "정책규제": ["규제", "정책", "법", "정부", "제재", "EU", "투자"], "보안이슈": ["보안", "취약점", "해킹", "위험", "프라이버시"], } self.huggingface_data = { "models": [], "spaces": [] } self.news_data = [] def fetch_huggingface_trending(self) -> Dict: """허깅페이스 트렌딩 모델 수집""" print("🤗 허깅페이스 트렌딩 정보 수집 중...") try: models_url = "https://huggingface.co/api/models" params = { 'sort': 'trending', 'limit': 30 } response = requests.get(models_url, params=params, timeout=15) if response.status_code == 200: models = response.json() for model in models[:30]: self.huggingface_data['models'].append({ 'name': model.get('id', 'Unknown'), 'downloads': model.get('downloads', 0), 'likes': model.get('likes', 0), 'task': model.get('pipeline_tag', 'N/A'), 'url': f"https://huggingface.co/{model.get('id', '')}" }) print(f"✅ {len(self.huggingface_data['models'])}개 트렌딩 모델 수집 완료") else: print(f"⚠️ 모델 API 오류: {response.status_code}") except Exception as e: print(f"❌ 모델 수집 오류: {e}") # 샘플 스페이스 데이터 sample_spaces = [ {"name": "Wan2.2-5B", "title": "고품질 비디오 생성", "url": "https://huggingface.co/spaces/"}, {"name": "FLUX-Image", "title": "텍스트→이미지 생성", "url": "https://huggingface.co/spaces/"}, {"name": "DeepSeek-App", "title": "AI 앱 생성기", "url": "https://huggingface.co/spaces/"}, ] self.huggingface_data['spaces'] = sample_spaces return self.huggingface_data def create_sample_news(self) -> List[Dict]: """오늘의 AI 뉴스 샘플 데이터 (2025-10-10 기준)""" sample_news = [ { 'title': 'MS "챗GPT 수요 폭증으로 데이터센터 부족...2026년까지 지속"', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203055', 'date': '10-10 15:10', 'source': 'AI Times', 'category': '산업동향' }, { 'title': '미국, UAE에 GPU 판매 일부 승인...엔비디아 시총 5조달러 눈앞', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203053', 'date': '10-10 14:46', 'source': 'AI Times', 'category': '산업동향' }, { 'title': '오픈AI, 저렴한 챗GPT 고 요금제 아시아 16개국으로 확대', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203054', 'date': '10-10 14:15', 'source': 'AI Times', 'category': '제품출시' }, { 'title': '인텔, 18A 공정으로 자체 제작한 노트북용 칩 팬서 레이크 공개', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203057', 'date': '10-10 14:03', 'source': 'AI Times', 'category': '제품출시' }, { 'title': '소라, 챗GPT보다 빨리 100만 다운로드 돌파', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203045', 'date': '10-10 12:55', 'source': 'AI Times', 'category': '제품출시' }, { 'title': '구글·아마존, 기업용 AI 서비스 나란히 출시', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203047', 'date': '10-10 12:41', 'source': 'AI Times', 'category': '제품출시' }, { 'title': '삼성 SAIT, 거대 모델 능가하는 초소형 추론 모델 TRM 공개', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203035', 'date': '10-09 21:22', 'source': 'AI Times', 'category': '기술혁신' }, { 'title': '구글, GUI 에이전트 제미나이 2.5 컴퓨터 유즈 공개', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203039', 'date': '10-09 20:57', 'source': 'AI Times', 'category': '기술혁신' }, { 'title': 'EU, 핵심 산업 AX 위한 1.6조 규모 투자 계획 발표', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203041', 'date': '10-09 18:51', 'source': 'AI Times', 'category': '정책규제' }, { 'title': '소프트뱅크, ABB 로봇 사업부 7.6조원에 인수', 'url': 'https://www.aitimes.com/news/articleView.html?idxno=203034', 'date': '10-09 18:07', 'source': 'AI Times', 'category': '산업동향' } ] self.news_data = sample_news return sample_news def categorize_news(self, news_list: List[Dict]) -> List[Dict]: """뉴스 카테고리 자동 분류""" for news in news_list: if 'category' not in news or news['category'] == '기타': title = news['title'].lower() news['category'] = "기타" for category, keywords in self.categories.items(): if any(keyword.lower() in title for keyword in keywords): news['category'] = category break return news_list def get_data(self) -> Dict: """모든 데이터 수집 및 반환""" # 뉴스 수집 news = self.create_sample_news() news = self.categorize_news(news) # 허깅페이스 데이터 수집 hf_data = self.fetch_huggingface_trending() # 카테고리별로 뉴스 그룹화 news_by_category = {} for article in news: category = article['category'] if category not in news_by_category: news_by_category[category] = [] news_by_category[category].append(article) # 통계 계산 stats = { 'total_news': len(news), 'categories': len(news_by_category), 'hf_models': len(hf_data['models']), 'hf_spaces': len(hf_data['spaces']) } return { 'news_by_category': news_by_category, 'hf_models': hf_data['models'], 'hf_spaces': hf_data['spaces'], 'stats': stats, 'timestamp': datetime.now().strftime('%Y년 %m월 %d일 %H:%M:%S') } # ============================================ # Flask 라우트 정의 # ============================================ @app.route('/') def index(): """메인 페이지""" try: analyzer = AINewsAnalyzer() data = analyzer.get_data() return render_template_string(HTML_TEMPLATE, **data) except Exception as e: return f"""

⚠️ 오류 발생

데이터를 불러오는 중 오류가 발생했습니다.

{str(e)}

""", 500 @app.route('/api/data') def api_data(): """JSON API 엔드포인트""" try: analyzer = AINewsAnalyzer() data = analyzer.get_data() return jsonify({ 'success': True, 'data': data, 'timestamp': datetime.now().isoformat() }) except Exception as e: return jsonify({ 'success': False, 'error': str(e), 'timestamp': datetime.now().isoformat() }), 500 @app.route('/health') def health(): """헬스 체크 엔드포인트""" return jsonify({ "status": "healthy", "service": "AI News Analyzer", "version": "1.0.0", "timestamp": datetime.now().isoformat() }) @app.route('/api/news') def api_news(): """뉴스만 반환하는 API""" try: analyzer = AINewsAnalyzer() news = analyzer.create_sample_news() return jsonify({ 'success': True, 'count': len(news), 'news': news }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @app.route('/api/hf-models') def api_hf_models(): """허깅페이스 모델만 반환하는 API""" try: analyzer = AINewsAnalyzer() hf_data = analyzer.fetch_huggingface_trending() return jsonify({ 'success': True, 'count': len(hf_data['models']), 'models': hf_data['models'] }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ============================================ # 메인 실행 # ============================================ if __name__ == '__main__': # 환경 변수에서 포트 가져오기 (기본값: 8080) port = int(os.environ.get('PORT', 8080)) # 환경 변수에서 디버그 모드 설정 (기본값: False) debug = os.environ.get('DEBUG', 'False').lower() == 'true' print(f""" ╔════════════════════════════════════════════════════════════╗ ║ ║ ║ 🤖 AI 뉴스 & 허깅페이스 트렌딩 웹 앱 시작! ║ ║ ║ ╚════════════════════════════════════════════════════════════╝ 🚀 Flask 서버 시작 중... 📍 메인 페이지: http://localhost:{port} 📊 JSON API: http://localhost:{port}/api/data 📰 뉴스 API: http://localhost:{port}/api/news 🤗 모델 API: http://localhost:{port}/api/hf-models 💚 Health Check: http://localhost:{port}/health {'🐛 디버그 모드: 활성화' if debug else '⚡ 프로덕션 모드: 최적화됨'} 브라우저에서 위 URL을 열어주세요! 종료하려면 Ctrl+C를 누르세요. """) try: app.run( host='0.0.0.0', port=port, debug=debug, threaded=True ) except KeyboardInterrupt: print("\n\n👋 서버를 종료합니다. 안녕히 가세요!") sys.exit(0) except Exception as e: print(f"\n❌ 서버 시작 실패: {e}") sys.exit(1)