가짜뉴스와는 별개로 현재 뉴스로 부터 키워드들을 가시화 해보는 과정을 해보고 싶어서 예제를 작성해 보았다.
목표는 네이버에서 제공하는 2021년 1월 언론사별 랭킹뉴스를 긁어와서 title로부터 주요 keyword를 추출하고, network를 구성하여 가시화 하는 것이다.
시작.
정보를 긁어올 페이지는 다음 페이지다.
news.naver.com/main/ranking/popularDay.nhn?mid=etc&sid1=111
네이버 랭킹뉴스에서는 몇 십개의 언론사에서 많이 본 뉴스, 댓글 많은 뉴스 별로 랭킹 5까지를 종합해서 한눈에 볼 수 있도록 정보를 제공하고 있다.
목표는 여기의 title을 긁어오는 것.
코드)
import requests
from bs4 import BeautifulSoup as BS
import pandas as pd
import numpy as np
import re
""" 네이버 랭킹 뉴스 긁어오기 """
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'}
d_list = []
start_data = 20210101
end_data = 20210116
for date_int in range(start_data, end_data):
date = str(date_int)
url = "https://news.naver.com/main/ranking/popularDay.nhn?date=" + date
html = requests.get(url, headers=headers).text
soup = BS(html, 'html.parser')
ranking_total = soup.find_all(class_='rankingnews_box')
for item in ranking_total:
media = item.a.strong.text
news = item.find_all(class_="list_content")
for new in news:
d = {}
d['media'] = media
d['src'] = "https://news.naver.com/" + new.a['href']
d['title'] = new.a.text
d['date'] = date
d_list.append(d)
df = pd.DataFrame(d_list)
2021/1/1 ~ 2021/1/16 일의 뉴스들을 긁어와서
'media' : 언론사
'src' : url
'title' : 제목
'date' : 날짜
정보를 pandas Dataframe에 종합한다.
일단은 'title'만 쓸 거지만, 저장할 수 있는 정보는 다 저장해 보는걸로..
title에는 불필요한 문자들이 많이 들어있다. 이를 제거 하기 위해 다음 코드를 실행.
""" 필요 없는 문자 제거 """
def clean_text(row):
text = row['title']
pattern = '([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)'
text = re.sub(pattern=pattern, repl='', string=text)
# print("E-mail제거 : " , text , "\n")
pattern = '(http|ftp|https)://(?:[-\w.]|(?:%[\da-fA-F]{2}))+'
text = re.sub(pattern=pattern, repl='', string=text)
# print("URL 제거 : ", text , "\n")
pattern = '([ㄱ-ㅎㅏ-ㅣ]+)'
text = re.sub(pattern=pattern, repl='', string=text)
# print("한글 자음 모음 제거 : ", text , "\n")
pattern = '<[^>]*>'
text = re.sub(pattern=pattern, repl='', string=text)
# print("태그 제거 : " , text , "\n")
pattern = r'\([^)]*\)'
text = re.sub(pattern=pattern, repl='', string=text)
# print("괄호와 괄호안 글자 제거 : " , text , "\n")
pattern = '[^\w\s]'
text = re.sub(pattern=pattern, repl='', string=text)
# print("특수기호 제거 : ", text , "\n" )
pattern = '[^\w\s]'
text = re.sub(pattern=pattern, repl='', string=text)
# print("필요없는 정보 제거 : ", text , "\n" )
pattern = '["단독"]'
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '["속보"]'
text = re.sub(pattern=pattern, repl='', string=text)
# print("단독 속보 제거 : ", text , "\n" )
text = text.strip()
# print("양 끝 공백 제거 : ", text , "\n" )
text = " ".join(text.split())
# print("중간에 공백은 1개만 : ", text )
return text
df['title_c'] = df.apply(clean_text, axis=1)
클린한 title을 'title_c' 열에 저장했다.
이제 이 타이틀에서 키워드(명사)를 추출한다. 여기에는 KoNLPy라는 한국어 정보처리 package를 활용.
konlpy-ko.readthedocs.io/ko/v0.4.3/
코드는 다음과 같다.
""" 키워드 추출 from title """
from konlpy.tag import Kkma
from konlpy.tag import Komoran
kkma = Kkma()
komoran = Komoran()
df['keyword'] = ''
for idx_line in range(len(df)):
nouns_list = komoran.nouns(df['title_c'].loc[idx_line])
nouns_list_c = [nouns for nouns in nouns_list if len(nouns) > 1] # 한글자는 이상한게 많아서 2글자 이상
df.loc[[idx_line], 'keyword'] = set(nouns_list_c)
df = df[df['media'] != '코리아헤럴드'] # 코리아헤럴드는 영어 제목임
한글자는 이상한 글자를 추출하는 경우가 많아서.. 아쉽지만 일단은 2글자 이상만 keyword열에 추가한다.
+ 코리아헤럴드의 기사는 영어 기사여서 제외시켰다
이제 network를 만들어야 한다. 구성할 network는 다음과 같다.
네트워크 형태 : 방향성 없는 가중 네트워크 (Undirected weighted network)
node : 키워드
edge : 동일 기사 제목에 등장한 키워드들은 관련이 있을 확률이 높으므로, 키워드들 간에 link를 추가해준다. 다른 기사에서 또 다시 link가 추가될 시 weight 1 추가.
코드는 다음과 같다.
""" Edge list 작성 """
from collections import Counter
edge_list = []
for keywords_dict in df['keyword']:
keywords = list(keywords_dict)
num_keyword = len(keywords)
if num_keyword > 0:
for i in range(num_keyword-1):
for j in range(i+1, num_keyword):
edge_list += [tuple(sorted([keywords[i], keywords[j]]))] # node 간 위해 sorted 사용
edges = list(Counter(edge_list).items())
""" networkx Graph 작성 """
import networkx as nx
G = nx.Graph((x, y, {'weight': v}) for (x, y), v in edges)
이제 keyword들간의 연결 정보를 이용해서 community를 추출할 것이다. 추출한 community는 색 정보로 그래프에 가시화 하기 위해 이용한다. 이 때 사용할 알고리즘은 Louvain algorithm, 이전에 블로그에서 다뤘던 적이 있다. 큰 network에서 비교적 빠른 속도로 정확도 높은 community 추출이 가능한 알고리즘으로, 사용하기 편하게 package가 있다. 구체적인 사용법은 여기 참조.
코드는 다음과 같다.
""" Community 추출 """
import community as lv
partition = lv.best_partition(G)
nx.set_node_attributes(G, partition, "community") # graph G에 community 속성 추가
partition은 dictionary 형태 (node: community)로 나오고, 이를 set_node_attributes 명령어를 이용해서 각 node에 attribute로 추가해줄 수 있다.
이제 모든 준비 끝. Gephi 파일로 작성한다.
""" Gephi file 작성 """
nx.write_gexf(G, '202101_keyword_community.gexf')
Gephi는 network 가시화 / 분석이 가능한 프로그램으로 무료로 사용할 수 있다.
gephi 프로그램을 열고, 새 프로젝트에서
파일 -> Open -> 위에서 저장한 파일 선택.
나머지 가시화는 미세 조정이다. Overview에서 조정을 한 뒤 Preview에서 그림 디테일을 뽑아내고 저장하는 순서다.
큼직하게 설명하자면, 왼쪽 위 Apperance 탭에서 Nodes의 색을 Partition으로 하고, 여기서 attribute를 우리가 저장한 community로 설정하면 node의 색이 변한다. 마찬가지로, node 크기, label크기, label 색 조정이 Apperance 탭에서 여기서 가능하다. (Edges도 설정 가능)
처음 불러 오면 그냥 점들이 임의로 모여있는데 이는 network를 가시화하는 의미가 없다. 왼쪽 아래에서 Layout을 골라줘야 하는데, 이정도로 큰 네트워크 (node개수 몇천, edge 개수 몇만) 에서는 ForceAtlas2 또는 OpenOrd가 제일 좋다.
ForceAtlas2는 link들이 당기는 힘으로 network모형이 조정되고, OpenOrd는 막 섞다가 폭발시키면서 node들의 위치를 조정한다.
Overview에서 Label표시는 중앙 Graph 왼쪽 아래에 검은색 T모양을 누르면 나온다. 한글을 깨지니까 T가 있는 열의 오른쪽 끝에 종이에 렌치가 올라간 그림을 클릭하면 나오는 밑에 화면의 Labels에서 Font를 한글이 가능한 폰트로 바꿔준다.
이제 가장 위 탭의 Preview에 가서 왼쪽 아래의 Refresh를 누르면 대략 모양이 나오는데 여기서 Refresh를 누르면 대략 모습이 나온다. 여기서 부터는 정말 미세조정이고, 취향이다.
다음 그림은 2021년 1월 랭킹 뉴스들의 keyword로 부터 구성한 network의 가시화 결과다.
큰 글씨는 degree가 높은 (여러 기사에서 언급되거나 한 기사에서 반복 언급된) 키워드들이다. node와 edge들의 색은 community 정보에 기반한다. 자세히 보면 정치인들 관련(빨강), 부동산 관련(핑크), 정인이...(연두), 코로나(보라), 주식(주황, 파랑)과 같은 community들이 추출되어 있고, 관련 keyword를 볼 수 있다.
굉장히 거칠게 모은 데이터들이었지만, 화제가 되고 있는 사건들의 키워드들이 community 구조로 잘 추출된 것을 볼 수 있다.
뉴스 플랫폼의 문제는 주요 뉴스와 사건을 읽을 정도의 시간 밖에 없다는 것이다. 이런 방식은 읽는 사람과 media 둘 다 만족 하는 합의점을 형성하지만, 질적으로 중요하지만 소외 되고 있는 기사들을 생산하거나 접할 기회를 낮춘다고 생각한다.
다양한 소리를 효율적으로 보여주고, 질적으로 우수한 정도를 평가하여 가시화 할 수 있는 방법이 있지 않을까.. 다음번에는 그런 관점에서 접근을 해봐야 겠다.
'공부 > 가짜 뉴스' 카테고리의 다른 글
[Neo4j] 그래프를 다루는 데이터베이스 - 도입 (2) | 2021.03.05 |
---|---|
[뉴스 가시화] 네이버 랭킹 뉴스 키워드 네트워크 가시화 - 2월 (0) | 2021.03.01 |
[생각] 기성언론 vs 뉴미디어 (0) | 2020.01.09 |
[생각] 가짜뉴스 고찰 (0) | 2019.09.09 |
[소개] 인터넷이 우리의 사고에 미치는 영향 (2) | 2019.01.30 |
댓글