네이버 랭킹 뉴스 페이지가 개편되어 해당 코드로 기사를 가져올 수 없습니다. 해당 코드는 참고용으로만 봐주시기 바랍니다. ↓개편된 네이버 랭킹 뉴스 크롤링

 

네이버 랭킹 뉴스 크롤링

크롤링 (5), beautifulsoup4로 네이버 기사 크롤링하기 네이버 랭킹 뉴스 페이지가 개편되어 해당 코드로 기사를 가져올 수 없습니다. 해당 코드는 참고용으로만 봐주시기 바랍니다. (JY) 융합연구 1 -

dsbook.tistory.com

 

 

 

(JY) 융합연구 1 - 크롤링 (4), beautifulsoup4로 네이버 기사 크롤링하기

저번에 네이버 API를 통해 검색에서 네이버 기사를 긁어왔다면, 이번엔 python 모듈 중 하나인 beautifulsoup4로 네이버 주요뉴스를 크롤링하는 방법을 알아보기로 하자. 1. 조회수 별 랭킹 뉴스 살펴보기 네이버..

dsbook.tistory.com

이전 글에서 조회수 별로 주요뉴스를 크롤링하는 2가지 방법에 대해서 이야기했다.

1) 날짜를 고정하고 각 섹션별로 30개씩 뉴스 가져오기
2) 섹션을 고정하고 특정기간동안 뉴스 가져오기

본인은 2번째 방법으로 키워드를 정리하면 타임라인별로 어떤 키워드가 주목을 받았는지 더 쉽게 알 수 있을거 같아 특정기간동안 각 섹션별로 뉴스를 정리하여 데이터프레임화 하였다. 대략적인 함수의 개요는 다음과 같다.

 

먼저 필요한 모듈을 import하자.

import requests
import pandas as pd
from bs4 import BeautifulSoup
import re
import operator
import time
import datetime
from konlpy.tag import Komoran

 

해당 뉴스를 가져오는 함수 get_news(sectionid, date) 를 정의해보자. 이전 글에서 보았던 것 처럼 주요뉴스를 표시해주는 url은 섹션별로, 날짜별로 페이지가 달랐으니 인자로 sectionid와 date를 받아준다. 이렇게 입력받은 두 개의 인자를 통해 url을 구성한 후 requests의 get함수로 url을 요청한다. 요청한 결과를 resp변수에 초기화 한 뒤 BeautifulSoup로 해당 페이지의 html코드를 불러온다.

def get_news(sectionid, date): 
	url = "https://news.naver.com/main/ranking/popularDay.nhn?rankingType=popular_day&sectionId=" + str(sectionid) + "&date=" + str(date) 
	resp = requests.get(url) 
	soup = BeautifulSoup(resp.text, "html.parser")

이렇게 불러온 html 소스코드에서 우리가 필요한 정보만을 얻기위해 직접 찾아가야 한다. 우선 우리가 얻고자 하는 제목과 링크, 조회수가 어디있는지 살펴보아야 한다. 앞서 이러한 정보들이 모두 ranking_item is_numX(X = [1, 30]) 클래스를 가진 <li>태그에 붙어있었다고 하였다. 조회수별로 1위부터 30위까지 순위가 매겨져 조회수가 가장 많은 뉴스의 태그는 ranking_item is_num1, 2번째로 조회수가 많은 뉴스는 ranking_item is_num2 이런 식으로 클래스가 붙어있었다. 

그 안을 들여다보면 아래와 같은 코드가 나온다. 기사의 정보는 "ranking_text" 클래스를 가진 <div>태그 안에 담겨있었다. 그 중에서 링크와 제목은 "ranking_headline" 클래스를 가진 <div>태그 안에 <a>태그의 href 속성으로, title 속성으로 존재하고 있고, 기사의 조회수는 "ranking_view" 클래스를 가진 <div>태그 안에 값으로 존재하고 있었다.

빈 리스트 하나를 초기화 한다. 그 후 find_all 함수를 통해 ranking_text 클래스를 가진 모든 태그를 가져온다. 그러면 총 30개 기사의 정보가 리스트화 될 것이다. 그러면 빈 딕셔너리 하나를 초기화 하고 각각의 요소를 돌며 a태그의 href속성을 가져와 LinkSrc의 키를 부여하고, a태그의 title속성을 가져와 Title의 키를 부여한다. 그리고 ranking_view라는 클래스를 가진 태그에 접근해 그 값만 뽑아내어 Views라는 키를 부여한다. 이렇게 채워진 딕셔너리를 빈 리스트에 추가한다.

l = []
ranking_text = soup.find_all(class_ = 'ranking_text')
for item in ranking_text:
    d = {}
    d['LinkSrc'] = item.find('a')['href']
    d['Title'] = item.find('a')['title']
    d['Views'] = item.find(class_ = "ranking_view").get_text()
    l.append(d)

 

이렇게 만들어진 리스트 각각의 요소에는 딕셔너리가 들어있을 것이며, 각각의 딕셔너리에는 그 순위별로 기사에 대한 링크와 제목, 조회수가 담겨있을 것이다. 하지만 우리는 아직 본문의 내용을 가져오지는 못했다. 주요뉴스에서 본문의 내용을 가져오기에는 그 기사의 내용이 너무 짧아 우리가 제대로 키워드를 뽑기 어렵다고 판단했기 때문이다. 그래서 우리는 앞서 가져온 링크로 기사에 접근하여 전문을 긁어오기로 했다.

우선 앞서 만든 리스트 각각의 요소에 접근한다. 네이버 뉴스의 기본 url은 "http://news.naver.com" 이므로 그 뒤에 우리가 긁어온 LinkSrc의 value를 이어붙여준다. 그 다음 위에서 했던 것처럼 url에 접근하여 소스코드를 가져온 뒤, 기사 전문을 품고 있는 태그를 찾아 본문 전체를 긁어온다. 그렇게 가져온 본문에 Content키를 부여하여 리스트에 추가한다.

for link in l:
    resp = requests.get("http://news.naver.com" + link['LinkSrc'])
    soup = BeautifulSoup(resp.text, "html.parser")
    content = soup.find(id="articleBodyContents")
    link['Content'] = clean_text(content)

여기서 clean_text 함수는 긁어온 본문을 저장하기에 앞서 특수문자와 쓸모없는 내용을 제거하는 등 본문의 내용을 조금 이쁘게 다듬는 함수이다. 정규표현식을 사용하였다.

# text 정제하기
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("무단전재 및 재배포 금지", "")
    return cleaned_text

 

정리해보면, get_news 함수를 통해 우리는 기사의 링크, 제목, 조회수, 본문의 내용을 긁어와 각각의 요소를 딕셔너리화 하였고, 그렇게 딕셔너리화 된 데이터들을 리스트에 저장하였다. 이렇게 만들어진 데이터들을 pandas의 데이터프레임으로 나타내면 데이터들을 좀 더 직관적으로 파악할 수 있을 것이다.

df = pd.DataFrame(l)
return df

Dict로 구성된 리스트가 어떻게 Dataframe을 생성하는지 알고싶으면 이 글을 읽는 것이 도움이 될 것이다. 

 

(JY) 파이썬 Pandas DataFrame 생성하기 - 1

DataFrame으로 dummy data 생성하기 import pandas as pd import numpy as np pandas.DataFrame pandas.DataFrame( data, index, columns, dtype, copy ) data ndarray, series, map, lists, dict, 상수 그리고 또..

dsbook.tistory.com

 

이렇게 만들어진 함수로 4월 30일에 정치섹션의 기사를 가져와보았다.

get_news(100, 20200430)

아래는 짤려서 보이지 않지만 성공적으로 기사 30개를 긁어왔다.

 

 

최종코드

import re
import requests
import pandas as pd
from bs4 import BeautifulSoup

def get_news(sectionid, date):
    url = "https://news.naver.com/main/ranking/popularDay.nhn?rankingType=popular_day&sectionId=" + str(sectionid) + "&date=" + str(date)
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, "html.parser")
    
    l = []
    ranking_text = soup.find_all(class_ = 'ranking_text')
    for item in ranking_text:
        d = {}
        d['LinkSrc'] = item.find('a')['href']
        d['Title'] = item.find('a')['title']
        d['Views'] = item.find(class_ = "ranking_view").get_text()
        l.append(d)
    
    for link in l:
        resp = requests.get("http://news.naver.com" + link['LinkSrc'])
        soup = BeautifulSoup(resp.text, "html.parser")
        content = soup.find(id="articleBodyContents")
        link['Content'] = clean_text(content)
        
    df = pd.DataFrame(l)
    return df
    
# text 정제하기
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("무단전재 및 재배포 금지", "")
    return cleaned_text
    
get_news(100, 20200430)

 

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기