Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

연탄코더

[파이썬 활용] 단어 구름(word cloud) 프로젝트: 1. 뉴스 기사 크롤링 본문

프로그래밍

[파이썬 활용] 단어 구름(word cloud) 프로젝트: 1. 뉴스 기사 크롤링

coalcoder 2019. 11. 26. 08:02

 하랑 파이썬 스터디로부터 이어지는, 파이썬을 활용해 '크롤링 + 단어 구름 만들기' 튜토리얼입니다. 함수, 클래스, 모듈 등은 아직 못 배웠지만, 일단 만들면서 익히는 것으로 해보겠습니다. 분량상 기사 크롤링, 단어 구름 만들기로 나눠서 진행합니다. 이 튜토리얼 프로젝트의 많은 부분은 싸피 친구들과 진행했던 프로젝트 hotword에서 끌어왔습니다. 

순서

0. 구상하기 + 계획하기
1. 라이브러리 설치

2. 크롤링 하기 
3. 단어 쪼개기

4. 단어 구름 만들기 


0. 구상하기 + 계획하기

 먼저 구상을 해봅니다. 굉장히 중요한 단계이고 또 즐거운 단계입니다. 뭘 만들고 싶은지, 어떤 그림이면 좋을지 생각해보고 다른 사람들은 어떻게 만들었는지, 어떻게 하면 만들 수 있을지 찾아봅니다. 

wordcloud.kr

 구글에 '단어구름' 검색하니까 세번째로 위와 같은 '윤동주 서시' 단어구름이 나옵니다. wordcloud.kr라는 단어구름 페이지도 있네요. 우리도 이런거 만들수 있을것 같습니다.

 뉴스에 등장하는 중요 키워드로 단어 구름을 만들어 보면 트렌드를 포착할 수도 있고 재밌을 것 같습니다. 뉴스 기사를 긁어와서 많이 등장하는 단어를 크게 보이도록 해보겠습니다. 

계획

너는 계획이 다 있구나. 기생충 (2019)

 프로그램을 만드는 데 있어서 계획은 중요합니다. 프로그램 전체적인 큰 그림을 그리는 것도 그렇고, 작은 규모로 함수를 설계하고 만드는 것 등 이런 계획력이 중요하죠. 프로그래밍을 '컴퓨터가 (알아서) 해야할 작업을 계획하고, 코드로 표현하는 것'이라고 할수도 있을 것 같네요. 계속 프로그래밍을 하다보면 점차 길러집니다. 

 위에 구상한 내용으로 계획을 한번 해봅니다. 어떤 순서로 프로세스가 진행될지, 인풋과 아웃풋은 뭐로 하면 좋을지, 사이사이로 어떤 데이터가 흐를지, 어떤 기능들이 필요한지 등. 좀 어려울 수도 있지만 시도해봅시다.

 설계 및 디자인하는 건 자기 마음입니다. 저는 다음과 같이 계획하겠습니다. 

' 아웃풋은 단어 구름 그림. 뉴스 기사로 만들거니까 우선 뉴스 기사를 모으고, 기사에 있는 단어 갯수를 세야겠다. 인풋은 딱히 안줘도 되겠다. 아니면 간단하게 주제만 선택해서 입력. 기사는 최근 기사로 한 100개 정도? 우선 100개로 해보고 나서 그 갯수도 조정할 수 있도록 해보자 '

 정리하면,
입력: 뉴스 주제 → 기사 크롤링: 기사 텍스트 키워드 갯수 세기: 키워드 별 카운트 단어 구름 만들기: 단어 구름 출력: 이미지 

 이 정도. 이제 이걸 구현하려면 뭐가 필요할지 생각해봅니다. 다행히도 이미 좋은 라이브러리들이 많이 있기 때문에 다른 사람들이 만들어둔 것을 가져다 잘 활용하면 됩니다. 필요한 기능들을 검색해보고 글들을 찾아 읽어보면서 괜찮은 라이브러리를 찾아서 설치합니다. 

 

1. 라이브러리 설치 

개발환경: python3(3.5 이상이면 됩니다)
사용 라이브러리: requests, beautifulsoup, wordcloud, ...

requests: HTML 받아오기 
beautifulsoup: HTML에서 텍스트 추출하기 (링크
wordcloud: 단어구름 만들기 (링크)

python 설치 잘 해주시고나면 아래 명령어를 통해 필요한 라이브러리 패키지를 설치해줍시다. pip는 파이썬 패키지를 관리해주는 툴입니다. (혹시 에러가 난다면 pip install ...로 시도해보고, 그래도 안되면 get_pip.py를 다운 받고 python get_pip.py)

pip3 install requests bs4 wordcloud

 

2. 크롤링하기 + 3. 단어 쪼개기

전체 코드입니다.

# crawling.py

# 필요한 라이브러리 로드
from bs4 import BeautifulSoup
import requests

# 뉴스 페이지 주소. 다음 뉴스.
urls = {
    'IT': 'https://news.daum.net/breakingnews/digital',
    '정치': 'https://news.daum.net/breakingnews/politics'
}
category = 'IT'
base_url = urls[category]

articles = []
max_count = 100
page_no = 1
while len(articles) < max_count:
    page_url = '{}?page={}'.format(base_url, page_no)
    page_response = requests.get(page_url)
    page_soup = BeautifulSoup(page_response.text)
    article_links = page_soup.select('div.cont_thumb a.link_txt')
    for link in article_links:
        article_url = link.get('href')
        if 'http' not in article_url:
            continue
        article_response = requests.get(article_url)
        article_soup = BeautifulSoup(article_response.text)
        article_text = article_soup.select('div.article_view')[0].text
        articles.append(article_text.strip().replace('\n', ' '))
    
    print(len(articles))
    page_no += 1
   
# 단어 갯수 세기
word_counts = {}

for article_text in articles:
    for word in article_text.split():
        if word not in word_counts:
            word_counts[word] = 0
        word_counts[word] += 1
print(word_counts)

결과 예시

{
    '운전자를': 2, 
    '광시야각을': 1, 
    '특허창출': 1, 
    '도입키로': 1, 
    '걱정하지': 1, 
    '시상식에서는': 1, 
    '구독': 1, 
    '시스템으로': 4, 
    '비교해': 3, 
    '얼굴은': 6
    ...
}

 아직 엉성한 부분이 많이 있지만. 일단 업로드하고 차차 수정해 나가도록 하겠습니다. zzZ

 

함수화

 위 코드를 재활용 가능하도록 함수 형태로 만들면 다음과 같이 만들수 있습니다. 

# crawling.py

from bs4 import BeautifulSoup
import requests

URLS = {
    'IT': 'https://news.daum.net/breakingnews/digital',
    '정치': 'https://news.daum.net/breakingnews/politics'
}


# 기사 url로부터 기사 텍스트를 긁어온다
def extract_text_from_url(article_url):
    article_response = requests.get(article_url)
    article_soup = BeautifulSoup(article_response.text)
    article_text = article_soup.select('div.article_view')[0].text

    return article_text.strip().replace('\n', ' ')
    
    
# page url로부터 기사 url들을 긁어온다
def extract_urls_from_page(page_url):
    page_response = requests.get(page_url)
    page_soup = BeautifulSoup(page_response.text)
    article_links = page_soup.select('div.cont_thumb a.link_txt')
    result_urls = []
    for link in article_links:
        article_url = link.get('href')
        if 'http' in article_url:
            result_urls.append(article_url)

    return result_urls
    

# base url로부터 page url들을 찾아서 기사 텍스트를 수집한다
def crawling_from_base(base_url, limit=100):
    articles = []
    page_no = 1
    while len(articles) < limit:
        print('{} / {} Collected'.format(len(articles), limit))
        page_url = '{}?page={}'.format(base_url, page_no)
        article_urls = extract_urls_from_page(page_url)
        for article_url in article_urls:
            article_text = extract_text_from_url(article_url)
            articles.append(article_text)
        page_no += 1

    articles = articles[:limit]
    print('{} / {} Collected'.format(len(articles), limit))
    
    return articles
    

# 텍스트들로부터 어절별 갯수를 센다. 형태소 분석기를 활용하도록 다음 글에서 수정될 예정
def count_words(articles):
    word_counts = {}
    for article_text in articles:
        for word in article_text.split():
            if word not in word_counts:
                word_counts[word] = 0
            word_counts[word] += 1
    
    return word_counts
    

# 카테고리와 사이즈를 전달하면 어절-갯수 딕셔너리를 반환
def crawl_word_counts(category, limit=100):
    base_url = URLS[category]
    articles = crawling_from_base(base_url, limit)
    word_counts = count_words(articles)
    
    return word_counts


# 매번 크롤링하면 시간이 많이 걸리니까, 파일로 저장하도록 한다
def save_counts_file(word_counts, name=None):
    if name is None:
        file_name = 'word_counts.txt'
    else:
        file_name = name
        
    outfile = open(file_name, 'w')
    for word, count in word_counts.items():
        outfile.write('{}\t{}\n'.format(word, count))
    outfile.close()
    

# 저장된 파일을 저장한 포맷에 맞춰서 불러오도록 한다
def load_counts_file(file_name='word_counts.txt'):
    infile = open(file_name, 'r')
    lines = infile.readlines()
    infile.close()
    
    word_counts = {}
    for line in lines:
        word, count = line.strip().split('\t')
        word_counts[word] = count
    
    return word_counts
    

# module 형식(import 하여 사용)이 아니라 바로 실행할 때 실행되는 부분     
if __name__ == '__main__':
    word_counts = crawl_word_counts('IT', 100)
    save_counts_file(word_counts, 'word_counts_IT_100.txt')

 함수(def)나 파일 입출력(open, close)에 대해서는 다른 글을 통해 다룰 수 있도록 할게요. 우선 따라서 써보면서 형태에 익숙해지기 바랍니다.

Comments