파이썬/라이브러리(API)

파이썬 뉴스 기사 크롤링 및 빈도 분석, 워드 클라우드 그리기

코데방 2024. 3. 14.
728x90

빈도 분석의 의미

 

빈도 분석이랑 문장이나 대화 속 각각의 문자들이 출현하는 경향의 정도를 분석하는 것을 의미합니다. 뉴스의 경우 특정 단어의 빈도 분석을 통해 사안의 중요도와 파급력 등을 유추해 볼 수도 있습니다. 

 

 

 

워드 클라우드

 

빈도에 따라 글자크기를 달리해 그림을 그려 직관적으로 이해할 수 있도록 만들어줍니다. 

 

 

 

 

 

딕셔너리를 이용한 빈도 분석 알고리즘

 

 

딕셔너리에 단어를 key로 추가하고 key가 있으면 value값에 카운트를 추가, 없으면 새롭게 딕셔너리에 등록해주는 방식으로 간단하게 사용할 수 있습니다. 

 

하나의 단어만 찾는다면 그냥 변수 하나에 카운트를 하면 되지만 여러개를 찾을 때는 딕셔너리가 좋은 대안이 될 수 있습니다. 

words = ["사과", "양파", "사과", "피망", "피망", "대파"]

dic = {}
for word in words:
    if word in dic:
        dic[word] += 1
    else:
        dic[word] = 1

print(dic)

 

 

 

 

 

워드 클라우드 라이브러리 사용하기

 

 

먼저 wordcloud 라이브러리를 import 해줍니다. 

from wordcloud import WordCloud

 

 

 

그리고 폰트 배경색깔, 마스크 옵션을 설정해 워드크라우드의 객체하나를 생성한 뒤 딕셔너리를 넣어주면 됩니다.  mask의 경우 네모난 워드크라우드를 하트 등의 모양으로 바꿔줄 때 지정해주면 되고 사용하지 않아도 무방합니다. 

wc = WordCloud(font_path = "...", backgroud_color = "...", mask = "...")
wc.generate_from_frequencies(dic)

 

 

 

워드클라우드의 경우 자체적으로 한글 폰트를 지원해주지 않기 때문에 ".ttf" 폰트 파일을 파일로 넣어주는게 좋습니다.

폰트와 배경색을 지정해 워드클라우드 객체를 생성한 딕셔너리 자료형을 넣어 최종 객체를 완성해줍니다.  

wc = WordCloud(font_path = "BMDOHYEON_ttf.ttf", background_color="white")
pic = wc.generate_from_frequencies(dic)

 

 

 

 

워드크라우드에서 매핑된 자료를 그림으로 그려주는 라이브러리가 PIL의 image 라이브러리입니다. 

from PIL import Image

 

 

 

기본적으로는 아래와 같이 사용해주면 됩니다. 마스킹을 비롯애 옵션이 엄청나게 많습니다. 

array = wc.to_array()
fig = plt.figure(figsize=(7, 7))
plt.imshow(array, interpolation="bilinear")
plt.axis("off") # 눈금자 없애기
plt.show()

 

 

 

 

 

매일경제 신문 기사 뉴스 크롤링해서 단어 빈도 분석 해보기

 

그날그날 올라오는 매일경제의 신문 지면 기사를 크롤링해서 단어의 빈도를 분석해 기본적인 워드크라우드까지 그려보겠습니다. 

 

먼저 크롤링에 필요한 라이브러리, 워드 클라우드에 필요한 라이브러리를 차례로 불러와줍니다.

# 크롤링
import requests
from bs4 import BeautifulSoup as bs
import time
import re

# 워드 크라우드
from PIL import Image 
from wordcloud import WordCloud

 

 

 

 

아래 주소는 해당일의 매일경제 신문 지면 기사가 있는 페이지입니다. 

url = requests.get("https://media.naver.com/press/009/newspaper")
html = bs(url.text)

 

 

 

 

해당 페이지는 뉴스 제목과 링크가 있습니다. 내용을 보려면 안으로 들어가야하기 때문에 크롤링해서 최종적으로 뽑아내는 자료는 "기사 제목"과 해당되는 "링크 주소" 입니다.

 

 

news = html.find("div", class_ = "_persist_wrap").find_all("a", class_= "article_lst--title_only")

 

 

 

먼저 가장 큰 통인  ("div", class_ = "_persist_wrap")을 find 함수를 통해 찾아줍니다.  

 

 

 

 

찾은 html 코드를 살펴보면 제목과 링크 모두 ("a", class_ = "article_lst--title_only" 안에 있는걸 확인할 수 있습니다. 반복되는 여러 태그들이므로 findall 함수를 이용해 모두 찾아줍니다.

 

 

 

 

원하는 부분들이 찾아졌습니다. 이제 로직을 통해 이 내용에서 원하는 부분들만 깔끔하게 골라내면 됩니다. 

news = html.find("div", class_ = "_persist_wrap").find_all("a", class_= "article_lst--title_only")

 

 

 

먼저 제목의 경우 유일하게 포함된 텍스트이기 때문에 그냥 text 함수를 통해 걸러내주면 됩니다. findall 함수로 받아진 객체는 text를 바로 사용할 수 없기 때문에 반복문을 통해서 하나씩 꺼내줘야합니다. 

 

 

 

 

 

각각의 제목과 링크주소를 가진 배열 ["제목", "링크주소"]를 배열에 모아줍니다.

제목에는 불필요한 엔터가 많아서 깔끔하게 제거해주고 넣겠습니다. (n.text.replace("\n",""))

 

링크의 경우 태그 안에 포함돼 있어서 다양한 로직으로 걸러낼 수 있는데 그냥 따옴표(")로 문자열을 나눠서 인덱스 3번을 가져오면 깔끔하게 가져올 수 있습니다. (str(n).split("\"")[3]])

lst = []
for n in news:
    lst.append([n.text.replace("\n",""), str(n).split("\"")[3]])

 

 

 

확인해보면 각각의 제목과 링크가 매칭된 리스트가 완성되었습니다.

 

 

 

 

이제 이 링크주소를 타고가서 다시 기사 내용을 가져오는 반복문이 필요합니다. 크롤링 과정에서는 에러 발생이 빈번하므로 꼭 try ~ exception 구문을 통해 예외 발생 시 반복문이 끊기지 않도록 하는 것이 중요합니다.

 

try문이 반복문을 감싸는게 아니라, 반복문 안에 try문을 넣어서 한 번의 크롤링 시도마다 try구문이 작동할 수 있도록 합니다.

content = []
count = 1

for i in lst:
    
    content.append(str(i[0] + " "))

    try:
        url = requests.get(i[1])
        html2 = bs(url.text)
        content.append(str(html2.find("article", class_ = "go_trans _article_content").text + " "))
        time.sleep(1)
        print("{}/{} 저장이 완료됐습니다.".format(count, len(lst)))
        count += 1
    except Exception:
        print("오류발생, 인덱스 : " + str(count-1))

 

 

 

한 번에 너무 빠르게 긁어오면 네이버쪽에서 차단을 당할 수도 있기에 time 라이브러리의 sleep 함수를 사용해 반복문에 지연시간을 설정해줬습니다. 진행상태를 알고 싶으면 위 코드처럼 반복문마다 문구가 뜨도록 하면 됩니다.

 

아래에서 볼 수 있듯이 대부분 같은 클래스명을 써서 가져올 수 있었지만 중간중간 다른 클래스명을 가진 뉴스 기사들이 있습니다. 만약 try문을 사용하지 않았다면 예외가 발생하는 순간 코드 전체가 중단되는 점에 주의합니다.

 

 

 

 

이제 content 배열에는 뉴스 제목과 내용이 모두 들어갔습니다.

 

빈도분석을 위해 먼저 content의 내용을 하나의 문자열로 만들어 준 뒤 정규표현식을 통해 두 글자 이상으로 이루어진 단어들만 골래내봅니다. 

 

그리고 위에서 다룬 딕셔너리를 이용해 빈도분석을 진행해줍니다. 

strr = ''.join(s for s in content) #한 문자열로 만들기
box = re.findall("[\w]{2,}", strr) #두글자 이상만 찾기

dic = {}

for i in box:
    if i in dic:
        dic[i] += 1
    else:
        dic[i] = 1

 

 

 

 

 

 

이제 빈도분석이 끝났으니 워드크라우드를 그려봅니다. 위의 과정과 똑같은 코드로 그려주면 됩니다.

ㅍwc = WordCloud(font_path = "BMDOHYEON_ttf.ttf", background_color="white")
pic = wc.generate_from_frequencies(dic)

array = wc.to_array()
fig = plt.figure(figsize=(7, 7))
plt.imshow(array, interpolation="bilinear")
plt.axis("off") # 눈금자 없애기
plt.show()

 

 

 

 

 

 

 

 

 

 

 

 

728x90

댓글

💲 추천 글