내맘대로 뉴스 요약 큐레이션 (1) - 시작 그리고 뉴스 크롤링

2020-04-26

내맘대로 사이드 프로젝트는 내맘대로니까 의식의 흐름대로 막씀. 반말로 씀.

0. 내맘대로 뉴스 기사 요약 + 큐레이션 프로젝트 시작.

일요일 오후 1:30분.. 글쓰기 마감일인데, 이번주도 한 글자도 안썼다…

집에서는 안되겠다 싶어 노트북을 챙겨서 빽다방에 나왔다.

이번주는 ~또 어떻게 때우나~ 어떤 글을 써볼까 고민하다가, 사이드 프로젝트 아이디어 리스트에 고이고이 묶혀둔 뉴스 기사 요약 + 큐레이션 프로젝트 를 꺼내기로 했다. (이걸로 한 세개는 써야겠다.)

오래된 주제라 참고해볼 자료도 많고, 써볼 수 있는 방법들도 다양할 것 같아 공부용으로 시작해볼 괜찮은 주제 같아 사이드 프로젝트로 선정해보았다.

최종 결과물이 어떤 형태일 지는 아직 모르겠지만 일단 시작.

1. 데이터 수집: 뉴스 기사를 긁어보자

일단 처음엔 쉽게 접근하기 위해 ~만만한~ 각 언론사별 뉴스 기사들을 잘 제공하고 있는 네이버 뉴스 서비스를 털어보기로 했다.

네이버 뉴스 서비스를 들어가보았다.

일단 모든 뉴스 기사를 수집하기 보다는 첫 시작점으로 오른쪽 란의 가장 많이 본 뉴스 데이터를 수집해보기로 했다.

(1) 긁어올 데이터 뜯어보기

가장 많이 본 뉴스 서비스에서 이것저것 누르다보면 아래와 같이 url 구조를 쉽게 파악할 수 있다.

많이 본 뉴스

  • url: https://news.naver.com/main/ranking/popularDay.nhn
  • rankingType: 섹션별인 경우 popular_day, 연령 별인 경우 age
  • sectionId: 102 (100: 정치, 101: 경제, 102: 사회, 103: 생활/문화, 104: 세계, 105: IT/과학, 003: 포토, 115: TV - 섹션별인 경우 사용가능)
  • subType: 30 (10대~60대) (연령 별인 경우 사용가능)
  • date: 20200426 (날짜 %Y%m%d)

댓글 많은 뉴스

  • url: https://news.naver.com/main/ranking/popularMemo.nhn
  • rankingType: popular_memo
  • sectionId: 102 (100: 정치, 101: 경제, 102: 사회, 103: 생활/문화, 104: 세계, 105: IT/과학, 003: 포토, 115: TV)
  • date: 날짜 (날짜 %Y%m%d)

공감 많은 뉴스

  • url: https://news.naver.com/main/ranking/popularLike.nhn
  • subType: 30 (10대~60대)
  • 날짜별로는 제공하지 않는다.

SNS 공유 많은 뉴스

  • url: https://news.naver.com/main/ranking/popularShare.nhn
  • 날짜별로는 제공하지 않는다.

많이 본 뉴스댓글 많은 뉴스의 경우 날짜 별 데이터를 긁어올 수 있었는데, 공감 많은 뉴스SNS 공유 많은 뉴스는 그렇지 않았다.

SNS 공유 많은 뉴스의 경우는 남들에게 적극적으로 공유하는 기준이라 좀 더 중요도가 높은 뉴스가 많을 것 같은데 아쉽다.

일단은 많이 본 뉴스 에 대해서 일자별 섹션별로 데이터를 크롤링해보기로 했다.

(2) 긁어올 데이터의 DOM 구조를 살펴본다.

다행히 크게 어렵지 않을 것 같이 생겼다.

ranking_list 별로 ranking_headline, ranking_lede, ranking_office, ranking_view 값들을 가져오면 될 것 같다.

ranking_headline 에는 해당 기사에 대한 링크도 제공하고 있어서 뉴스 내용까지 긁어올 수 있을 것 같다.

(3) 크롤링 코드 작성

(이하 자세한 설명은 생략한다)

from datetime import datetime, timedelta

from selenium import webdriver

from tqdm import tqdm


def get_chrome_driver():
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')

    chrome_driver = webdriver.Chrome('./chromedriver', options=chrome_options)

    return chrome_driver


_SECTIONS = {
    '100': '정치',
    '101': '경제',
    '102': '사회',
    '103': '생활/문화',
    '104': '세계',
    '105': 'IT/과학'
}


def get_popular_day_news_contents(date, section_id):
    url = f'https://news.naver.com/main/ranking/popularDay.nhn' \
        f'?rankingType=popular_day' \
        f'&sectionId={section_id}&date={date}'
    driver.get(url)
    driver.implicitly_wait(1)

    ranking = driver.find_elements_by_class_name('ranking')

    if ranking:
        headlines = ranking[0].find_elements_by_class_name('ranking_headline')
        ledes = ranking[0].find_elements_by_class_name('ranking_lede')
        offices = ranking[0].find_elements_by_class_name('ranking_office')
        views = ranking[0].find_elements_by_class_name('ranking_view')

        return zip(headlines, ledes, offices, views)
    else:
        return None


def get_popular_day_news(driver, start_date, end_date):
    fp = open(f'./popularDay-{start_date}-{end_date}.tsv', 'w')
    columns = ['date', 'section', 'office', 'view', 'headline', 'lede', 'link']
    fp.write('\t'.join(columns) + '\n')

    target_dates = get_date_strings(start_date, end_date)

    for date_str in tqdm(target_dates):
        for section_id, section_name in _SECTIONS.items():
            contents = get_popular_day_news_contents(date_str, section_id)
            if contents:
                for headline, lede, office, view in contents:
                    headline_a_tag = headline.find_element_by_tag_name('a')
                    contents = '\t'.join([
                        date_str,
                        section_name,
                        office.text,
                        view.text,
                        headline_a_tag.get_attribute('title'),
                        lede.text,
                        headline_a_tag.get_attribute('href')
                    ])
                    fp.write(contents + '\n')

    driver.quit()


def get_date_strings(start_date, end_date):
    date_strings = []
    curr = datetime.strptime(start_date, '%Y%m%d')
    end = datetime.strptime(end_date, '%Y%m%d')

    while True:
        if curr > end:
            break
        else:
            date_strings.append(
                curr.strftime('%Y%m%d')
            )
            curr = curr + timedelta(days=1)
    return date_strings


if __name__ == '__main__':
    driver = get_chrome_driver()
    start_date = '20200101'
    end_date = '20200131'

    date_strings = get_date_strings(start_date, end_date)
    print(date_strings)
    get_popular_day_news(driver, start_date, end_date)

빠르게 코드를 작성하고 2020년 1월 ~ 2020년 4월 치의 데이터(날짜, 섹션, 언론사, 제목, 내용, 링크, 조회수)를 긁어왔다.

각 뉴스의 전체 내용도 링크를 통해서 쉽게 가져올 수 있을 것 같은데, 귀찮아서 다음 todo로 남겨두었다.

한 세션에서 가능한 요청량이 정해져있기 때문에 한번에 너무 많은 양을 호출하면 안되고 한달치 정도씩만 가져와야한다.

3. 데이터 뜯어보기

(1) 데이터 읽어오기

!ls ~/private/news-crawler
Pipfile                          popularDay-20200101-20200131.tsv
Pipfile.lock                     popularDay-20200201-20200229.tsv
chromedriver                     popularDay-20200301-20200331.tsv
crawler                          popularDay-20200401-20200426.tsv
import warnings
warnings.filterwarnings(action='ignore')

import pandas as pd
import soynlp
import matplotlib.pyplot as plt
import re
import pyecharts
import matplotlib.font_manager as fm

plt.rc('font', family='NanumGothic')
print(plt.rcParams['font.family'])
['NanumGothic']
  • 사실 위 한글폰트 문제로 3시간을 헤맸다…ㅜ
  • 한글 폰트를 설치하고 열심히 경로를 지정해주었지만 변화가 없다가 나중에 캐시를 지워주어야 한다는 사실을 깨달았다고 한다.
  • matplotlib.get_cachedir() <- 폴더를 날리고 노트북 재시작
dataset_dir = '~/private/news-crawler'
filenames = [
    'popularDay-20200101-20200131.tsv',
    'popularDay-20200201-20200229.tsv',
    'popularDay-20200301-20200331.tsv',
    'popularDay-20200401-20200426.tsv'
]
data = pd.concat([
    pd.read_csv(f'{dataset_dir}/{f}', sep='\t', error_bad_lines = False, warn_bad_lines=False) 
    for f in filenames
])
data['datetime'] = pd.to_datetime(data['date'], format='%Y%m%d')
data.head()
  date section office view headline lede link datetime
0 20200101 정치 경향신문 130,669 박지원 “이낙연 종로 출마하면 황교안 못해···황 불출마? 띄워보기 말고 본인 입으… 박지원 대안신당(가칭) 의원은 1일 황교안 자유한국당 대표의 4월 총선 불출마설과 … https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01
1 20200101 정치 서울신문 128,362 김도읍 불출마… 공수처법 무기력 후폭풍 [서울신문] 7번째… 與·문의장 협조해야 사퇴 확정 공직선거법 개정안과 고위공직자범… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01
2 20200101 정치 경향신문 104,680 유승민 “2년 전 결혼 잘못해 고생했지만···” 새로운보수당 인재영입위원장을 맡고 있는 바른미래당 유승민 의원은 1일 “2년 전 결… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01
3 20200101 정치 한겨레 81,894 민주당 지지율 ‘한국당 2배’…부산·울산·경남도 15%p 높아 여당인 더불어민주당이 정당 지지도나 선호도에서 제1야당인 자유한국당을 두배쯤 앞서는… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01
4 20200101 정치 조선일보 76,126 새해 첫날 보수대통합 꺼내든 황교안·유승민… “시간이 없다” ‘보수통합추진위 구성→설 前 통합 원칙 합의→2월초 통합 마무리’ 시나리오 거론 지… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01
  • 데이터 불러오기 성공! 원하는 대로 잘 긁어온 것 같다.

(2) 주요 키워드 추출해보기

어떤 걸 해볼까하다가 뉴스 제목(headline)과 내용(lede)을 기반으로 주요 키워드를 추출해보기로 했다.

뉴스데이터의 경우는 신조어가 많이 등장하는 편인데, 일반 토크나이저를 사용할 경우 신조어는 잘 인식하지 못하여 soynlp 라이브러리를 사용하여 추출하였다.

내용 중 쓸모없는 특수문자 및 19일, 2020년 등의 의미없는 날짜, 연령대 단어, 언론사 명 들은 적절히 전처리해주었다.

from soynlp.utils import DoublespaceLineCorpus
from soynlp.noun import LRNounExtractor_v2
_office_list = list(data.office) + list(o[:-1] for o in data.office)

def prep(sent: str):
    prep = re.sub('\W+', ' ', sent)
    prep = re.sub(r'\d+일', '', prep)
    prep = re.sub(r'\d+년', '', prep)
    prep = re.sub(r'\d+대', '', prep)
    return prep


def is_office(word):
    if word in _office_list:
        return True
    else:
        return False


def get_global_top_keywords(data):
    sents = list(data['headline']) + list(data['lede'])
    prep_sents = [prep(sent) for sent in sents]
    
    noun_extractor = LRNounExtractor_v2(verbose=True, extract_compound=True)
    noun_extractor.train(prep_sents)
    nouns = noun_extractor.extract()
    top_keywords = []
    for n, k in sorted(nouns.items(), key=lambda x: x[1].frequency, reverse=True):
        if len(n) >= 3 and k.frequency >= 30 and not is_office(n):
            top_keywords.append((n, k.frequency))
    return top_keywords

top_keywords = get_global_top_keywords(data)
top_keywords[:50]
[Noun Extractor] use default predictors
[Noun Extractor] num features: pos=3929, neg=2321, common=107
[Noun Extractor] counting eojeols
[EojeolCounter] n eojeol = 99726 from 41598 sents. mem=0.321 Gb                    
[Noun Extractor] complete eojeol counter -> lr graph
[Noun Extractor] has been trained. #eojeols=543802, mem=0.394 Gb
[Noun Extractor] batch prediction was completed for 26260 words
[Noun Extractor] checked compounds. discovered 8099 compounds
[Noun Extractor] postprocessing detaching_features : 19517 -> 18478
[Noun Extractor] postprocessing ignore_features : 18478 -> 18378
[Noun Extractor] postprocessing ignore_NJ : 18378 -> 18353
[Noun Extractor] 18353 nouns (8099 compounds) with min frequency=1
[Noun Extractor] flushing was done. mem=0.417 Gb                    
[Noun Extractor] 72.84 % eojeols are covered
[('코로나19', 5487),
 ('코로나바이러스', 2832),
 ('코로나', 2648),
 ('감염증', 2602),
 ('확진자', 2044),
 ('마스크', 1726),
 ('대통령', 1202),
 ('신천지', 936),
 ('삼성전자', 898),
 ('바이러스', 673),
 ('문재인', 579),
 ('사망자', 576),
 ('트럼프', 551),
 ('스마트폰', 548),
 ('지난해', 483),
 ('현지시간', 474),
 ('한국인', 454),
 ('청와대', 428),
 ('아파트', 412),
 ('네이버', 412),
 ('황교안', 402),
 ('미래통합당', 395),
 ('온라인', 390),
 ('김정은', 390),
 ('기생충', 382),
 ('신종코로나', 334),
 ('이탈리아', 329),
 ('더불어민주당', 328),
 ('WHO', 323),
 ('민주당', 317),
 ('가능성', 308),
 ('윤석열', 298),
 ('유튜브', 292),
 ('갤럭시S20', 283),
 ('자가격리', 282),
 ('지난달', 279),
 ('진중권', 278),
 ('법무부', 275),
 ('치료제', 274),
 ('갤럭시', 262),
 ('안철수', 259),
 ('자유한국당', 258),
 ('이만희', 252),
 ('감염자', 238),
 ('추미애', 234),
 ('특파원', 234),
 ('중국인', 233),
 ('통합당', 229),
 ('조주빈', 227),
 ('봉준호', 226)]

2020년 1월 ~ 4월 간 핫한 키워드는 역시 코로나 다.

중간에 선거가 있어서인지 정치 관련 키워드도 보이고, 기생충, 조주빈 등 한동안 크게 이슈화 됬던 키워드들이 뽑혔다.

(3) 키워드 별 시간별 트렌드 보기

  • 주요 키워드 별로 시간대 별 기사 수를 살펴보면 재밌을 것 같아 해당 키워드를 언급한 기사를 표시하고, 날자별로 그래프를 그려보기로 했다.
def get_keywords_from_news(headline, lede, keywords):
    news_keywords = list(set([
        keyword[0] for keyword in keywords 
        if keyword[0] in headline or keyword[0] in lede
    ]))
    return ','.join(news_keywords)

data['keywords'] = data.apply(lambda x: get_keywords_from_news(x['headline'], x['lede'], top_keywords), axis=1)
data.head(10)
  date section office view headline lede link datetime keywords
0 20200101 정치 경향신문 130,669 박지원 “이낙연 종로 출마하면 황교안 못해···황 불출마? 띄워보기 말고 본인 입으… 박지원 대안신당(가칭) 의원은 1일 황교안 자유한국당 대표의 4월 총선 불출마설과 … https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 한국당,황교안,자유한국당,이낙연,불출마
1 20200101 정치 서울신문 128,362 김도읍 불출마… 공수처법 무기력 후폭풍 [서울신문] 7번째… 與·문의장 협조해야 사퇴 확정 공직선거법 개정안과 고위공직자범… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 후폭풍,불출마
2 20200101 정치 경향신문 104,680 유승민 “2년 전 결혼 잘못해 고생했지만···” 새로운보수당 인재영입위원장을 맡고 있는 바른미래당 유승민 의원은 1일 “2년 전 결… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 새로운보수당,위원장,유승민,바른미래,바른미래당
3 20200101 정치 한겨레 81,894 민주당 지지율 ‘한국당 2배’…부산·울산·경남도 15%p 높아 여당인 더불어민주당이 정당 지지도나 선호도에서 제1야당인 자유한국당을 두배쯤 앞서는… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 더불어민주당이,더불어민주당,민주당,한국당,자유한국당,지지율
4 20200101 정치 조선일보 76,126 새해 첫날 보수대통합 꺼내든 황교안·유승민… “시간이 없다” ‘보수통합추진위 구성→설 前 통합 원칙 합의→2월초 통합 마무리’ 시나리오 거론 지… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 마무리,황교안,유승민,보수통합
5 20200101 정치 동아일보 74,470 “공무원증 밥값 결제” 혁신賞 준 정부 [2020 新목민심서-공직사회 뿌리부터 바꾸자] 혁신안 내랬더니 민원사항 내… 부처… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 2020,공무원
6 20200101 정치 MBC 73,265 김정은 “충격적 실제행동…새 전략무기 목격할 것” [정오뉴스]◀ 앵커 ▶ 북한이 오늘 김정은 국무위원장의 새해 신년사 대신 어제까지 … https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 국무위원장,김정은,위원장
7 20200101 정치 연합뉴스 69,925 유시민 “曺가 풀었단 시험은 온라인 오픈북시험…깜찍한 기소” 조국 공소장 반박…”노무현재단 계좌 볼 수 있는 모든 기관에 서면질의할 것”유튜브 … https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 노무현재단,유시민,유튜브,온라인
8 20200101 정치 중앙일보 67,268 故윤한덕 아들과 아차산 오른 文 “대통령 만난 새해 운수대통” 문재인 대통령은 경기도에 있는 아차산을 오르는 것으로 2020년 새해를 열었다. 문… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 문재인,대통령,경기도,2020
9 20200101 정치 연합뉴스 66,793 文대통령, 아차산서 새해맞이…”국민 행복한 한해, 정부가 앞장”(종합) 취임 후 세 차례 새해 모두 의인과 등반…하산 후 靑 관저서 떡국 조찬시민들에 “운… https://news.naver.com/main/ranking/read.nhn?r… 2020-01-01 대통령,시민들,文대통령
  • keyword 에 잘 표시되었다.
  • 동시에 함께 나온 키워드들을 보는 것도 재밌을 듯

전체 데이터셋에서 키워드를 뽑으면 코로나만 너무 많이 나올 것 같아서 월별 TOP 키워드들을 뽑아서 그래프를 그려보기로 했다.

top_keywords1 = get_global_top_keywords(
    data[(data['datetime'] >= '20200101') & (data['datetime'] < '20200201')]
)
top_keywords2 = get_global_top_keywords(
    data[(data['datetime'] >= '20200201') & (data['datetime'] < '20200301')]
)
top_keywords3 = get_global_top_keywords(
    data[(data['datetime'] >= '20200301') & (data['datetime'] < '20200401')]
)
top_keywords4 = get_global_top_keywords(
    data[(data['datetime'] >= '20200401') & (data['datetime'] < '20200501')]
)
tops = list(set([
    keyword for keyword, _ in 
    top_keywords1[:20] + top_keywords2[:20] + top_keywords3[:20] + top_keywords4[:20]
]))

tops
[Noun Extractor] use default predictors
[Noun Extractor] num features: pos=3929, neg=2321, common=107
[Noun Extractor] counting eojeols
[EojeolCounter] n eojeol = 40587 from 10948 sents. mem=0.317 Gb                    
[Noun Extractor] complete eojeol counter -> lr graph
[Noun Extractor] has been trained. #eojeols=144847, mem=0.318 Gb
[Noun Extractor] batch prediction was completed for 11391 words
[Noun Extractor] checked compounds. discovered 1715 compounds
[Noun Extractor] postprocessing detaching_features : 7045 -> 6886
[Noun Extractor] postprocessing ignore_features : 6886 -> 6823
[Noun Extractor] postprocessing ignore_NJ : 6823 -> 6821
[Noun Extractor] 6821 nouns (1715 compounds) with min frequency=1
[Noun Extractor] flushing was done. mem=0.318 Gb                    
[Noun Extractor] 63.75 % eojeols are covered
[Noun Extractor] use default predictors
[Noun Extractor] num features: pos=3929, neg=2321, common=107
[Noun Extractor] counting eojeols
[EojeolCounter] n eojeol = 34749 from 10344 sents. mem=0.301 Gb                    
[Noun Extractor] complete eojeol counter -> lr graph
[Noun Extractor] has been trained. #eojeols=134719, mem=0.309 Gb
[Noun Extractor] batch prediction was completed for 9857 words
[Noun Extractor] checked compounds. discovered 1524 compounds
[Noun Extractor] postprocessing detaching_features : 6031 -> 5893
[Noun Extractor] postprocessing ignore_features : 5893 -> 5843
[Noun Extractor] postprocessing ignore_NJ : 5843 -> 5839
[Noun Extractor] 5839 nouns (1524 compounds) with min frequency=1
[Noun Extractor] flushing was done. mem=0.310 Gb                    
[Noun Extractor] 65.80 % eojeols are covered
[Noun Extractor] use default predictors
[Noun Extractor] num features: pos=3929, neg=2321, common=107
[Noun Extractor] counting eojeols
[EojeolCounter] n eojeol = 38423 from 11028 sents. mem=0.296 Gb                    
[Noun Extractor] complete eojeol counter -> lr graph
[Noun Extractor] has been trained. #eojeols=143502, mem=0.304 Gb
[Noun Extractor] batch prediction was completed for 10792 words
[Noun Extractor] checked compounds. discovered 1751 compounds
[Noun Extractor] postprocessing detaching_features : 6678 -> 6462
[Noun Extractor] postprocessing ignore_features : 6462 -> 6409
[Noun Extractor] postprocessing ignore_NJ : 6409 -> 6407
[Noun Extractor] 6407 nouns (1751 compounds) with min frequency=1
[Noun Extractor] flushing was done. mem=0.303 Gb                    
[Noun Extractor] 66.53 % eojeols are covered
[Noun Extractor] use default predictors
[Noun Extractor] num features: pos=3929, neg=2321, common=107
[Noun Extractor] counting eojeols
[EojeolCounter] n eojeol = 35469 from 9278 sents. mem=0.294 Gb                    
[Noun Extractor] complete eojeol counter -> lr graph
[Noun Extractor] has been trained. #eojeols=120734, mem=0.302 Gb
[Noun Extractor] batch prediction was completed for 10038 words
[Noun Extractor] checked compounds. discovered 1406 compounds
[Noun Extractor] postprocessing detaching_features : 5939 -> 5799
[Noun Extractor] postprocessing ignore_features : 5799 -> 5748
[Noun Extractor] postprocessing ignore_NJ : 5748 -> 5743
[Noun Extractor] 5743 nouns (1406 compounds) with min frequency=1
[Noun Extractor] flushing was done. mem=0.301 Gb                    
[Noun Extractor] 62.77 % eojeols are covered
['신종코로나',
 '확진자',
 '이만희',
 '크루즈선',
 '청와대',
 '코로나19',
 '우한폐렴',
 '지난해',
 '기생충',
 '한국인',
 '긴급재난지원금',
 '현지시간',
 '자유한국당',
 '현지시',
 '신천지',
 '바이러스',
 '윤석열',
 '진중권',
 '텔레그램',
 '삼성전자',
 '마스크',
 '스마트폰',
 '재난지원금',
 '추미애',
 '법무부',
 '온라인',
 '문재인',
 '대통령',
 '더불어민주당',
 '조주빈',
 '코로나',
 '치료제',
 '중국인',
 '아파트',
 '김정은',
 '코로나바이러스',
 '후베이성',
 '네이버',
 '이탈리아',
 '아카데미',
 '한국당',
 '봉준호',
 '사망자',
 '미래통합당',
 '통합당',
 '감염증',
 '트럼프']
def get_keyword_summ(unique_date, data, keyword):
    tmp = data.copy()
    tmp[f'has_{keyword}'] = tmp.keywords.str.contains(keyword)

    keyword_summ = tmp[['date', f'has_{keyword}']][tmp[f'has_{keyword}']].groupby(
        ['date']
    ).count().reset_index()
    
    keyword_data = pd.merge(unique_date, keyword_summ, on='date', how='left')
    keyword_data = keyword_data.fillna(0)
    return keyword_data


unique_date_df = pd.DataFrame(data['date'].unique(), columns=['date'])
plt.figure(figsize=(20, 15))
for keyword in tops:
    keyword_data = get_keyword_summ(unique_date_df, data, keyword)
    plt.plot(
        pd.to_datetime(keyword_data['date'], format='%Y%m%d'),
        keyword_data[f'has_{keyword}'],
        label=keyword
    )
plt.legend(fontsize=10)

  • 오 뭔가 의도한 대로 잘 나왔는데 알아보기가 어렵다.
  • 주제어 별로 클러스터링이 잘 되어야 좀 보기 편할 것 같다.

아래에서부터는 matplotlib 그래프가 별로 이쁘지 않아서 다른 차트 라이브러리를 사용해보았다. (그런데 노트북 파일에서는 결과가 안보여져서 ㅠ 결국 그냥 이미지를 첨부하였다 ㅠ)

from pyecharts import Line
unique_dates = [str(date) for date in list(keyword_data['date'])]

<코로나 관련="" 트렌드="">

line = Line()

for keyword in ['코로나', '우한폐렴', '바이러스', '마스크', '신천지', '재난지원금']:
    keyword_data = get_keyword_summ(unique_date_df, data, keyword)

    line.add(
        name=keyword,
        x_axis=unique_dates,
        y_axis=[int(item) for item in list(keyword_data[f'has_{keyword}'])],
        mark_point=['max']
    )
# line - notebook 에서 로딩 안됨 ㅠ 아래는 그냥 이미지를 넣음

  • 공식 병명이 코로나19로 바뀌면서 우한 폐렴 이라는 워딩은 2월 초 이후로 잘 안쓰이게 되었다. 바이러스는 계속 언급된다.
  • 우리나라는 신천지 사태 이후로 확진자가 크게 늘게 되었는데, 그 이후로 확진자가 크게 늘고 마스크 대란이 일어나면서 마스크 키워드가 급등한 걸 볼 수 있다.
  • 요즘은 재난지원금에 대한 뉴스가 많아 재난지원금에 대한 뉴스가 많아지고 있는 것도 확인할 수 있다.

<기생충 관련="">

line = Line()
for keyword in ['기생충', '봉준호']:
    keyword_data = get_keyword_summ(unique_date_df, data, keyword)

    line.add(
        name=keyword,
        x_axis=unique_dates,
        y_axis=[int(item) for item in list(keyword_data[f'has_{keyword}'])],
        mark_point=['max']
    )

# line - notebook 에서 로딩 안됨 ㅠ 아래는 그냥 이미지를 넣음

line = Line()
for keyword in ['텔레그램', '조주빈', 'n번방']:
    keyword_data = get_keyword_summ(unique_date_df, data, keyword)

    line.add(
        name=keyword,
        x_axis=unique_dates,
        y_axis=[int(item) for item in list(keyword_data[f'has_{keyword}'])],
        mark_point=['max']
    )
# line - notebook 에서 로딩 안됨 ㅠ 아래는 그냥 이미지를 넣음

  • 전국민을 분노하게 했던 n번방 사건.
  • 박사의 정체가 공개되었을 때 피크점을 찍은 것으로 보인다.
  • 점점 기사 수도 줄어들고 관심에서 멀어지는 것 같아서 안타깝다.

<김정은 관련="">

line = Line()
for keyword in ['김정은']:
    keyword_data = get_keyword_summ(unique_date_df, data, keyword)

    line.add(
        name=keyword,
        x_axis=unique_dates,
        y_axis=[int(item) for item in list(keyword_data[f'has_{keyword}'])],
        mark_point=['max']
    )

# line - notebook 에서 로딩 안됨 ㅠ 아래는 그냥 이미지를 넣음

<트럼프 관련="">

line = Line()
for keyword in ['트럼프']:
    keyword_data = get_keyword_summ(unique_date_df, data, keyword)

    line.add(
        name=keyword,
        x_axis=unique_dates,
        y_axis=[int(item) for item in list(keyword_data[f'has_{keyword}'])],
        mark_point=['max']
    )

# line - notebook 에서 로딩 안됨 ㅠ 아래는 그냥 이미지를 넣음

  • 꾸준히 언급되는 트럼프와 대조적으로 최근 김정은 사망설이 뜨면서 김정은 키워드가 크게 이슈화 되고 있는 것을 볼 수 있다.
  • 전쟁나면 안됨..

4. To do

일단 이번주 차는 뉴스 데이터를 긁어와서 간단히 분석해 본 것으로 끝.

거의 제목과 뉴스 첫 줄만으로 최근 트렌드를 반영하는 데이터가 생각보다 잘 뽑혀서 신기했다.

다음 번에는

  • 데이터 추가 수집: 뉴스 내용 긁어오기, 2019년 데이터, 댓글 많은 순 데이터
  • 주제 별로 키워드 클러스터링 하기
  • 예쁜 그래프 라이브러리 찾기

이정도 진행해보아야겠다.