Python으로 네이버·구글 뉴스 자동 수집 — 봇이 뉴스를 읽는 방법
JARVIS의 뉴스 수집봇이 네이버 랭킹뉴스와 구글 뉴스를 어떻게 긁어오는지. BeautifulSoup + feedparser 실사용 코드.
왜 직접 만들었나
뉴스 API를 쓰면 편합니다. 네이버 뉴스 API, 구글 뉴스 API 등.
문제:
- 네이버 뉴스 API: 검색은 되지만 랭킹 뉴스(지금 가장 많이 본 뉴스)는 안 됨
- 구글 뉴스 API: 유료, 쿼터 제한
- RSS: 일부 언론사만 제공
결국 직접 스크래핑합니다.
구조
class JarvisSmart:
def get_naver_news(self) -> list[dict] # 네이버 랭킹 뉴스
def get_google_news(self) -> list[dict] # 구글 뉴스 RSS
def get_all_news(self) -> list[dict] # 통합 + 중복 제거
네이버 뉴스 수집
네이버 뉴스 랭킹 페이지를 파싱합니다.
import requests
from bs4 import BeautifulSoup
def get_naver_news(self) -> list[dict]:
url = "https://news.naver.com/main/ranking/popularDay.naver"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(response.text, 'html.parser')
results = []
items = soup.select('.rankingnews_box .list_content a')
for item in items[:30]:
title = self.clean_title(item.get_text())
if len(title) > 8: # 너무 짧은 건 제목 아님
results.append({
'title': title,
'source': 'Naver',
'link': item.get('href')
})
return results
clean_title 함수:
언론사 이름이 제목에 붙어 나오는 경우가 많습니다.
원본: "윤석열 대통령 발언 - 조선일보"
정리: "윤석열 대통령 발언"
def clean_title(self, title: str) -> str:
media_list = ['조선일보', '중앙일보', '동아일보', '한겨레', 'MBC', 'KBS', ...]
for m in media_list:
title = title.replace(f' - {m}', '').replace(f'[{m}]', '')
# 대괄호, 소괄호 내용 제거
title = re.sub(r'\[.*?\]', '', title)
title = re.sub(r'\(.*?\)', '', title)
return title.strip()
구글 뉴스 RSS
구글 뉴스는 RSS를 제공합니다. feedparser로 파싱합니다.
import feedparser
def get_google_news(self) -> list[dict]:
# 한국어 뉴스 RSS
url = "https://news.google.com/rss?hl=ko&gl=KR&ceid=KR:ko"
feed = feedparser.parse(url)
results = []
for entry in feed.entries[:20]:
title = self.clean_title(entry.title)
results.append({
'title': title,
'source': 'Google',
'link': entry.link,
'published': entry.get('published', '')
})
return results
중복 제거
네이버와 구글에서 같은 뉴스가 나올 수 있습니다. 제목 유사도로 중복을 제거합니다.
from difflib import SequenceMatcher
def deduplicate(self, items: list[dict]) -> list[dict]:
unique = []
for item in items:
is_dup = False
for u in unique:
ratio = SequenceMatcher(
None,
item['title'],
u['title']
).ratio()
if ratio > 0.7: # 70% 이상 비슷하면 중복
is_dup = True
break
if not is_dup:
unique.append(item)
return unique
겪었던 문제
문제 1: User-Agent 없으면 403
# 이렇게 하면 403
response = requests.get(url)
# 이렇게 해야 됨
response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0...'})
문제 2: 네이버 HTML 구조 변경
네이버가 HTML 구조를 바꾸면 CSS 셀렉터가 안 맞습니다. 여러 셀렉터를 순서대로 시도합니다:
selectors = [
'.rankingnews_box .list_content a',
'div.ranking_headline > a',
'.list_content a',
]
for selector in selectors:
items = soup.select(selector)
if items:
break # 첫 번째로 결과 나오는 셀렉터 사용
문제 3: feedparser가 인코딩 오류
일부 RSS 피드가 인코딩 정보를 잘못 선언합니다.
# feedparser가 알아서 처리하지만
# 한글이 깨지면:
feed = feedparser.parse(url)
for entry in feed.entries:
try:
title = entry.title.encode('latin-1').decode('utf-8')
except (UnicodeEncodeError, UnicodeDecodeError):
title = entry.title # 그냥 원본 사용
수집한 뉴스로 뭘 하나
JARVIS에서:
- 30분마다 자동 수집 (NimCuration 스케줄러)
- LLM으로 요약 (mistral-nemotron 또는 Ollama)
- Telegram 그룹으로 전송
- Pearl Finder에 키워드로 전달
뉴스 수집 자체는 간단합니다. 수집한 뉴스를 어떻게 처리하느냐가 핵심입니다.
주의사항
스크래핑 시 서비스 이용약관을 확인하세요. 과도한 요청은 IP 차단될 수 있습니다.
- 요청 간 딜레이:
time.sleep(1~2) - robots.txt 확인
- 상업적 사용 시 공식 API 우선 검토