import gradio as gr import requests import json from datetime import datetime, timedelta API_KEY = "V38CNn4HXpLtynJQyOeoUensTEYoFy8PBUxKpDqAW1pawT1vfJ2BWtPQ98h6" MAJOR_COUNTRIES = [ "United States", "United Kingdom", "Canada", "Australia", "Germany", "France", "Japan", "South Korea", "China", "India", "Brazil", "Mexico", "Russia", "Italy", "Spain", "Netherlands", "Sweden", "Switzerland", "Norway", "Denmark", "Finland", "Belgium", "Austria", "New Zealand", "Ireland", "Singapore", "Hong Kong", "Israel", "United Arab Emirates", "Saudi Arabia", "South Africa", "Turkey", "Egypt", "Poland", "Czech Republic", "Hungary", "Greece", "Portugal", "Argentina", "Chile", "Colombia", "Peru", "Venezuela", "Thailand", "Malaysia", "Indonesia", "Philippines", "Vietnam", "Pakistan", "Bangladesh" ] def search_serphouse(query, country, page=1, num_result=100): url = "https://api.serphouse.com/serp/live" now = datetime.utcnow() yesterday = now - timedelta(days=1) date_range = f"{yesterday.strftime('%Y-%m-%d')},{now.strftime('%Y-%m-%d')}" payload = { "data": { "q": query, "domain": "google.com", "loc": country, "lang": "en", "device": "desktop", "serp_type": "news", "page": str(page), "verbatim": "1", "num": str(num_result), "date_range": date_range } } headers = { "accept": "application/json", "content-type": "application/json", "authorization": f"Bearer {API_KEY}" } try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() except requests.RequestException as e: error_msg = f"Error: {str(e)}" if response.text: error_msg += f"\nResponse content: {response.text}" return {"error": error_msg} def format_results_from_raw(results): try: # 디버그 정보 생략 debug_info = "" if isinstance(results, dict) and "error" in results: return "Error: " + results["error"], "" if not isinstance(results, dict): raise ValueError("결과가 사전 형식이 아닙니다.") # 'results' 키 내부의 구조 확인 (중첩된 'results' 처리) if 'results' in results: results_content = results['results'] if 'results' in results_content: results_content = results_content['results'] # 'news' 키 확인 if 'news' in results_content: news_results = results_content['news'] else: news_results = [] else: news_results = [] else: news_results = [] if not news_results: return "검색 결과가 없습니다.", "" # 뉴스 결과를 리스트 형태로 포맷팅 (이미지 썸네일 포함) list_output = "" for idx, result in enumerate(news_results, 1): title = result.get("title", "제목 없음") link = result.get("url", result.get("link", "#")) snippet = result.get("snippet", "내용 없음") channel = result.get("channel", result.get("source", "알 수 없음")) time = result.get("time", result.get("date", "알 수 없는 시간")) image_url = result.get("img", result.get("thumbnail", "")) # base64로 인코딩된 이미지를 처리하지 않음 if image_url and not image_url.startswith("data:image"): thumbnail_html = f'Thumbnail' else: thumbnail_html = '' # 리스트 형식의 기사 (이미지 썸네일 포함) list_item = f"""

{idx}. {title}

{thumbnail_html}

요약: {snippet}

출처: {channel} | 시간: {time}


""" list_output += list_item return list_output, "" except Exception as e: error_message = f"결과 처리 중 오류 발생: {str(e)}" return "Error: " + error_message, "" def serphouse_search(query, country): # 페이지와 결과 수의 기본값을 설정합니다. page = 1 num_result = 100 results = search_serphouse(query, country, page, num_result) list_output, debug_info = format_results_from_raw(results) return list_output css = """ footer { visibility: hidden; } /* '뉴스 결과'와 '디버그 정보' 탭 숨기기 */ #tab-뉴스_결과, #tab-디버그_정보 { display: none !important; } /* '페이지'와 '결과 수' 입력 요소 숨기기 */ .slider-container { display: none !important; } """ # Gradio 인터페이스 구성 with gr.Blocks(theme="Nymbo/Nymbo_Theme", css=css, title="NewsAI 서비스") as iface: gr.Markdown("검색어를 입력하고 원하는 국가를 선택하면, 검색어와 일치하는 24시간 이내 뉴스를 최대 100개 출력합니다.") with gr.Tab("검색"): with gr.Row(): query = gr.Textbox(label="검색어") country = gr.Dropdown(MAJOR_COUNTRIES, label="국가", value="South Korea") # '페이지'와 '결과 수' 입력 요소 제거 # with gr.Row(): # page = gr.Slider(1, 10, 1, label="페이지") # num_result = gr.Slider(1, 100, 100, label="결과 수") search_button = gr.Button("검색") # '뉴스 결과'와 '디버그 정보' 탭 제거 # with gr.Tab("뉴스 결과"): # news_output = gr.HTML(label="뉴스 결과") with gr.Tab("리스트"): list_output = gr.HTML(label="리스트 결과") # HTML로 변경 # with gr.Tab("디버그 정보"): # debug_output = gr.Textbox(label="디버그 정보", lines=10) def search_and_display(query, country): list_output_text = serphouse_search(query, country) return {list_output: list_output_text} search_button.click( search_and_display, inputs=[query, country], outputs=[list_output] ) iface.launch(auth=("gini", "pick"))