네이버 랭킹 뉴스만 볼 수 있던 페이지가 전면적으로 개편되면서 위 게시글에서 작성한 코드를 사용했을 때 뉴스를 가져올 수 없었다. 그래서 이번에 개편된 랭킹 뉴스 페이지를 기반으로 새롭게 크롤링하는 코드를 작성해보았다.
랭킹 뉴스 페이지에서 이전처럼 정치, 경제, 사회, 과학 등 분야별로 따로 나누어서 게시글을 찾아볼 수 없었다. 대신 각 언론사별로 많이 본 뉴스와 댓글이 많은 뉴스를 나누어서 볼 수 있었다. 그래서 언론사 페이지에 들어가면 url은 다음과 같은 형식으로 이루어져 있다.
"https://news.naver.com/main/ranking/office.nhn?officeId=" + press_ID[press] + "&date=" + str(date)
이렇게 각 언론사별로 ID가 생성되어 있어서 랭킹을 보고 싶은 언론사의 ID를 입력하면 해당 언론사의 랭킹뉴스 페이지로 넘어가진다. 또 date 속성에 "20210128"처럼 8글자로 된 날짜를 입력하면 해당 날짜의 랭킹뉴스 페이지로 넘어가진다. 이를 이용해 우리는 원하는 언론사, 원하는 날짜의 랭킹 뉴스 페이지를 크롤링 해올 수 있다.
import re
import requests
import pandas as pd
import time
from bs4 import BeautifulSoup
url = "https://news.naver.com/main/ranking/office.nhn?officeId=214&date=20210114"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"}
resp = requests.get(url, headers=headers)
soup = BeautifulSoup(resp.text, "html.parser")
만약 ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 와 같은 error가 발생한다면 위와 같이 headers를 입력해 get의 headers 인자로 전달해주자.
아래와 같이 각 언론사 별 ID를 dict 자료형을 이용하여 초기화했다.
press_ID = {"MBC":"214", "KBS": "056", "SBS": "055", "JTBC": "437"}
해당 페이지에서 날짜, 랭킹, 언론사, 주소, 제목, 조회수, 댓글 수를 모두 모아 사전 형태로 만들어 리스트에 추가한 뒤, 각 요소의 주소값을 돌며 새롭게 본문의 내용을 가져와 사전에 추가한다. 그렇게 만들어진 리스트를 DataFrame으로 만들고 csv 파일 형태로 저장한다. 완성된 코드는 다음과 같다.
# demo v0.4 날짜를 입력받아 자동으로 크롤링
import os
os.chdir(r"C:\Users\cjy89\NLP\Project_news_crawling\Naver")
from bs4 import BeautifulSoup
import time
import pandas as pd
import requests
import re
press_ID = {"MBC":"214"}
#, "KBS": "056", "SBS": "055", "JTBC": "437"}
"""
def clean_text(text):
content = text.get_text()
cleaned_text = re.sub('[a-zA-Z]', '', content)
cleaned_text = re.sub(
'[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>▶▽♡◀━@\#$%&\\\=\(\'\"ⓒ(\n)(\t)]', ' ', cleaned_text)
cleaned_text = cleaned_text.replace(
"🇲\u200b🇮\u200b🇱\u200b🇱\u200b🇮\u200b🇪\u200b", "")
cleaned_text = cleaned_text.replace("오류를 우회하기 위한 함수 추가 ", "")
cleaned_text = cleaned_text.replace("무단전재 및 재배포 금지", "")
cleaned_text = cleaned_text.replace(
"동영상 뉴스 뉴스데스크 ", "")
cleaned_text = cleaned_text.replace(
"동영상 뉴스 뉴스투데이 ", "")
cleaned_text = cleaned_text.replace("앵커 ", "")
return cleaned_text
"""
# 8자리로 된 날짜를 입력하면 해당 날짜의 ranking news를 가져온다.
def get_ranking_news(date):
total_time = 0
# 언론사 순
for press in press_ID:
start = time.time()
url = "https://news.naver.com/main/ranking/office.nhn?officeId=" + press_ID[press] + "&date=" + str(date)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"}
resp = requests.get(url, headers=headers)
soup = BeautifulSoup(resp.text, "html.parser")
ranking_box = soup.find_all(class_="rankingnews_box_inner")
l = []
# 조회수 -> 댓글 수
for ranking_type in range(2):
ranking = ranking_box[ranking_type].find_all(class_="list_ranking_num")
url_list = ranking_box[ranking_type].find_all(class_="list_content")
# 랭킹 순 (1 ~ 20)
for rank in range(20):
d = {}
d['Date'] = int(date)
d['Press'] = press
d['Rank'] = ranking[rank].get_text()
d['URL'] = url_list[rank].find('a')['href']
d['Title'] = url_list[rank].find('a').get_text()
if (ranking_type == 0):
d['View'] = url_list[rank].find(class_="list_view").get_text()
elif (ranking_type == 1):
d['Comment'] = url_list[rank].find(class_="list_comment nclicks('RBP.dcmtnwscmt')").get_text()
l.append(d)
# 본문 가져오기
for news in l:
resp = requests.get("https://news.naver.com" + news['URL'], headers=headers)
soup = BeautifulSoup(resp.text, "html.parser")
contents = soup.find(id="articleBodyContents").get_text()
news['Content'] = re.sub('[\{\}\[\]\/?\(\);:|*~`!^\-_+<>▶▽♡◀━@\#$&\\\=\'\"ⓒ(\n)(\t)]', ' ', contents)
# news['Content'] = clean_text(content)
df = pd.DataFrame(l)
title = press + "/" + str(date) + "_" + press + "_ranking_news.csv"
df.to_csv(title, sep=",", index=False, encoding="utf-8-sig")
end = time.time()
total_time += end - start
print("Crawling " + str(date) + " " + press + " news :", end - start)
print("Total time :", total_time)
print("Average time : ", total_time/len(press_ID))
print("───────────────────")
# 21.01.01 ~ 21.01.24 크롤링
for i in range(24):
get_ranking_news(20210101+i)
to_csv로 데이터 프레임 형태를 파일로 저장할 때, title에 해당되는 파일의 이름을 자유롭게 바꿀 수 있다. 또한 원하는 언론사 페이지 뉴스를 크롤링해오고 싶으면 press_ID 변수에 원하는 언론사의 코드를 추가하면 된다.
위 코드를 이용해서 크롤링해왔을 때 하루 동안 한 언론사 페이지를 긁어오는데 평균적으로 10초의 시간이 걸리는 것을 확인할 수 있다. 네이버 뉴스에 기재되는 언론사의 개수는 총 72개인 것을 생각하면 하루동안 전체 언론사의 페이지를 크롤링해오는데 걸리는 시간은 약 720초, 12분으로 상당한 시간이 걸린다. 사람마다 보는 언론사가 다 다르다고 하더라도 대중적으로 선호하거나 대부분의 사람들이 알만한 언론사는 따로 있으므로 이를 감안하여 크롤링해오는데 걸리는 시간을 단축할 수 있을 것이다.
'데이터 분석 & 시각화 > Crawling' 카테고리의 다른 글
다음 랭킹 뉴스 크롤링 (0) | 2021.01.31 |
---|---|
크롤링 (5), beautifulsoup4로 네이버 기사 크롤링하기 (5) | 2020.05.02 |
크롤링 (4), beautifulsoup4로 네이버 기사 크롤링하기 (0) | 2020.05.02 |
크롤링(3) (0) | 2020.04.15 |
최근댓글