[웹스크래핑] 네이버 많이 본 기사 알려주는 텔레그램 봇
네이버 뉴스 ‘많이 본 기사 톱10’(미디어오늘)
네이버는 날짜별로 언론사별 랭킹뉴스를 제공한다. 네이버에 기사를 공급하는 매체(CP)별로 현재 시간 기준 가장 인기 있는 기사들을 모아 보여주는 페이지다. 기준은 두 가지다. ‘많이 본 뉴스’와 ‘댓글 많은 뉴스’다. 목록은 1시간마다 갱신되는 듯.
언론사별로 따로 볼 수도 있다. 예컨대 지금 이 순간, 네이버 이용자들이 많이 본 미디어오늘 뉴스와 댓글 많은 미디어오늘 뉴스를 보여주는 식이다.
요걸 긁어와 텔레그램 채널로 전송해주는 봇을 만들었다. 편의상 많이 본 뉴스만 뽑기로 했다. 기사 제목과 발행 시간, 조회수를 텍스트로 추출하고 enumerate
함수를 이용한 반복문에서 idx
값을 뽑아내 순위를 매겼다. 맥북의 crontab
기능을 이용해 1시간마다 갱신 여부를 체크했다. (상위 10개만 뽑음)
몇 가지 주의한 점은,
headers
를 지정하지 않으니 네이버가 크롤링 봇으로 인식하고 커넥션 에러를 띄움prev_mtoday.txt
와new_mtoday.txt
파일을 각각 만들어 두 텍스트 파일을 읽어들여 목록 갱신 여부를 대조함.- 갱신됐을 경우 새 목록을 텔레그램으로 전송하고 → 기존 목록(
prev_mtoday.txt
)을 새 목록(new_mtoday.txt
)으로 대체하고 새 목록은 비워줌(내용을 지우고 빈 글자를 입력)
- 갱신됐을 경우 새 목록을 텔레그램으로 전송하고 → 기존 목록(
- 해당 페이지엔 원문 정보가 없어서 네이버 기사 상세 페이지에서 원문 링크를 따로 추출해 넣어줌.
URL
변수 값만 바꿔주면 다른 언론사 톱10 목록도 똑같이 뽑아올 수 있다. :)
딱히 비밀스러운 정보는 아니지만, 일단 비공개 채널로 등록. 링크는 아래에.
import requests, telegram, datetime, os
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.22 Safari/537.36'}
url = "https://news.naver.com/main/ranking/office.nhn?officeId=006"
base_url = "https://news.naver.com"
BASE_PATH = "/Users/asadal/Documents/Dev/"
prev_filename = BASE_PATH + "prev_mtoday.txt"
new_filename = BASE_PATH + "new_mtoday.txt"
# 원문 링크 추출
def get_org_link(url):
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.text, "lxml")
org_link = soup.find("div", attrs={"class": "sponsor"}).find("a")["href"]
return org_link
def get_articles():
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.text, "lxml")
articles = soup.find_all("div", attrs={"class": "list_content"}, limit=10) # 상위 10개 목록만
for idx, article in enumerate(articles):
title = article.find("a", attrs={"class": "list_title nclicks('RBP.drnknws')"}).text
link = base_url + article.find("a")["href"]
org_link = get_org_link(link)
time = article.find("span", attrs={"class": "list_time"}).text
views = article.find("span", attrs={"class": "list_view"}).text
message = f"{idx+1}. {title}\n{time} | 조회수: {views}\n({link})\n(원문 ☞ {org_link})\n\n"
# print(message)
save_to_txt(message)
def check_changes():
with open(prev_filename, mode="r+", encoding="utf-8") as p_msg:
prev_message = p_msg.read()
with open(new_filename, mode="r+", encoding="utf-8") as n_msg:
new_message = n_msg.read()
if new_message != prev_message:
send_to_telegram(new_message)
else:
print("Nothing to Update")
return
def save_to_txt(msg):
with open(new_filename, "a+", encoding="utf-8") as mtoday: # 이어쓰기 모드로 열기. 파일이 없으면 새로 만듬.
mtoday.write(msg)
# 텔레그램으로 전송하기 (메시지가 길 경우 에러 발생)
def send_to_telegram(msg):
bot = telegram.Bot(token="1755775596:AAEsn_0DlwUADLggKVLF-bmv0huqNuDuGek")
print(msg)
try:
bot.sendMessage(chat_id=-1001331896515, text=msg) # 저장된 텍스트를 텔레그램으로전송
print("전송을 완료했습니다.")
with open(prev_filename, mode="w+", encoding="utf-8") as prev_list:
prev_list.write(msg)
with open(new_filename, mode="w", encoding="utf-8") as new_list:
new_list.wrige("") # 새 목록은 비워줌
return
except Exception:
print("전송 중 에러가 발생했습니다.")
return
# 한 번에 실행
get_articles()
check_changes()
덧. 생각해보니, 목록이 똑같을 수가 없구나. 조회수가 올라가니. (이런 ㅁㅊㅇ)
그냥 주기적으로 보내주는 걸로 코드 변경. 대신 오전 9시부터 저녁 6시까지만 보내는 걸로 crontab
설정을 변경했다.
import requests, telegram, datetime, os
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.22 Safari/537.36'}
url = "https://news.naver.com/main/ranking/office.nhn?officeId=006"
base_url = "https://news.naver.com"
BASE_PATH = "/Users/asadal/Documents/Dev/"
filename = BASE_PATH + "mtoday.txt"
# 원문 링크 추출
def get_org_link(url):
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.text, "lxml")
org_link = soup.find("div", attrs={"class": "sponsor"}).find("a")["href"]
return org_link
# 목록 추출
def get_articles():
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.text, "lxml")
articles = soup.find_all("div", attrs={"class": "list_content"}, limit=10)
for idx, article in enumerate(articles):
title = article.find("a", attrs={"class": "list_title nclicks('RBP.drnknws')"}).text
link = base_url + article.find("a")["href"]
org_link = get_org_link(link)
time = article.find("span", attrs={"class": "list_time"}).text
views = article.find("span", attrs={"class": "list_view"}).text
message = f"{idx+1}. {title}\n{time} | 조회수: {views}\n({link})\n(원문 ☞ {org_link})\n\n"
save_to_txt(message)
# 메시지 읽기
def send_message():
with open(filename, mode="r+", encoding="utf-8") as p_msg:
message = p_msg.read()
send_to_telegram(message)
# 메시지 저장
def save_to_txt(msg):
with open(filename, "a+", encoding="utf-8") as mtoday: # 이어쓰기 모드로 열기. 파일이 없으면 새로 만듬.
mtoday.write(msg)
# 텔레그램으로 전송 (메시지가 길면 에러 발생)
def send_to_telegram(msg):
bot = telegram.Bot(token="1755775596:AAEsn_0DlwUADLggKVLF-bmv0huqNuDuGek")
print(msg) # 제대로 스크래핑됐는지 확인
try:
bot.sendMessage(chat_id=-1001331896515, text=msg) # 저장된 텍스트를 텔레그램으로전송
print("전송을 완료했습니다.")
with open(filename, mode="w", encoding="utf-8") as new_list:
new_list.write("")
return
except Exception:
print("전송 중 에러가 발생했습니다.")
return
# 한 번에 실행
get_articles()
send_message()