웹 크롤링 시작하기!

작성자 : 이수연

크롤링이라는 걸 처음 접했을 때 난 텅텅 빈 상태에서 <Ctrl+Enter>의 무한반복으로 순서를 따라가기에 급급했었다. 궁금하긴 하지만 복잡해보이고 뭐가 뭔지도 모르겠는 나와 같은 사람들도 각각의 코드를 쉽게 이해하고 절차를 더 간략화하여 따라할 수밖에 없는, 그렇게 흥미를 가지게 하는 것을 목적으로 실습해보았다!

네이버 뉴스-IT/과학 분야 중 헤드라인 뉴스들의 제목과 본문을 추출해 어떤 키워드가 많이 나오는지 파악해보자!

크롤링 시작하기!

우선 크롬이 최신 버전이라는 가정 하에 진행하겠다! 크롬-설정-Chrome 정보에서 버전 확인이 가능하다.

이때까지 데이터 분석을 위해 사용했던 기초적인 라이브러리 외 오늘은 몇 가지 새로운 라이브러리가 필요하다.

“BeautifulSoup”, “selenium”,”konlpy” 위 세 가지 라이브러리를 pip install __ 을 통해 다운로드해준다

또한 우리는 크롬 브라우저를 조작해 크롤링을 해볼 것이기 때문에 크롬드라이버를 설치해 진행한다.

ChromeDriver - WebDriver for Chrome - Downloads (chromium.org)

모든 준비가 완료되었다면 시작해봅시다!

크롤링 익히기!

본격 헤드라인 뉴스를 크롤링하기 전 라이브러리들을 조금씩 사용하며 익숙해져보자.

import를 통해 사용할 라이브러리를 불러온다.

from selenium import webdriver
from bs4 import BeautifulSoup
import re

이제 라이브러리를 이용해 뉴스들을 긁어오자!

window_path = "chromedriver.exe"
source_url = "https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=105"
driver = webdriver.Chrome()  # for Windows
driver.get(source_url) 
req = driver.page_source
soup = BeautifulSoup(req, "html.parser")
cluster_text = soup.find_all(name="div", attrs={"class":"sh_text"}) 
print(cluster_text)

source_url 변수에 우리가 크롤링하려 하는 뉴스 페이지 주소를 지정해 넣어준다.

그 이후 크롬 드라이버를 이용해 크롬 브라우저를 조종할 수 있다!

크롬 드라이버를 통해 내가 위에 입력한 웹페이지를 열어준다.

page_source로 열린 웹 페이지의 소스 코드들, 즉 HTML들을 긁어온다.

BeautifulSoup을 사용해 위에서 받은 소스 코드들을 parser을 통해 파싱해준다! 이 과정을 통해 내가 원하는 정보를 특정 패턴, 순서로 추출해내 가공할 수 있다.

그 이후 soup객체에서 div태그 중 class속성이 sh_text인 것만을 긁어온다.

구조를 먼저 파악하는 방법은,

f12 개발자 모드를 켠 다음

사진1

마우스를 가져다대면 각각 요소들의 구조를 알 수 있다. 헤드라인 뉴스들은 공통적으로 sh_text를 가지고 있었다. ctrl+f를 켠 후 sh_text를 입력해서 검토해볼 수 있다~!

참고로 id는 하나, class는 여러 요소가 가질 수 있는 속성이다! sh_text는 class!

이렇게 프린팅하면

사진2

헤드라인 뉴스들로 들어갈 수 있는 링크가 포함된 요소들이 모두 출력된다.

간단하게 링크만 다시 가져와보자!

page_urls = []
for index in range(0, len(cluster_text)) :
    cluster = cluster_text[index]
    news_url = cluster.find(name="a", attrs = {"class":"sh_text_headline nclicks(cls_sci.clsart)" })
    if news_url is not None :
        page_urls.append(news_url.get("href"))

print(page_urls)

냅다 나오는 for문에 어지러울 수 있다..(내가 그랬다)

하지만 구조를 파악해 찬찬히 살펴보면 어렵지 않다!

이 코드는 링크가 포함된 줄을 우선적으로 긁어온 후, get 함수를 통해 href, 즉 링크만 뽑아 page_urls라는 리스트에 추가해주는 코드이다. 찬찬히 살펴보자!

cluster_text 리스트의 각각 인덱스를 index변수에 순서대로 집어넣어 반복해준다.

cluster_text리스트에서 index위치에 들어있는 요소를 cluster라는 변수에 넣어준다.

그 이후 cluster안의 a 태그 내 “sh_text_headline nclicks(cls_sci.clsart)” class를 가진 요소를 찾는다.

사진3

링크가 포함된 줄에 모두 sh_text_headline nclicks(cls_sci.clsart) class가 포함되어있기 때문이다! 이 또한 ctrl+f를 통해 필터링해 살펴볼 수 있다.

이렇게 링크가 포함된 줄을 모아낸 후 get 함수를 통해 링크를 추출해 append()함수로 page_urls라는 리스트에 링크만 쏙쏙 집어넣은 것이다!

출력 결과는 다음과 같다.

사진4

이제 글에서 텍스트를 추출해내는 과정을 미리 손에 익혀보자.

driver = webdriver.Chrome()  # for Windows 다시 크롤러 봇 생성
driver.get(page_url[0]) #기사 링크들 중 하나에 들어간다! 텍스트 구조 파악을 위함
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
title_area = soup.find(name="div", attrs={"class":"media_end_head_title"})
title_area #또 앞뒤로 이상한거 주루룩 붙어나옴
title = title_area.find_all('h2')[0]
title #제목 줄만 뽑아냄 -> 나중에 여기서 텍스트만 뽑아낼고야
#이제 본문!
content_paragraphs = soup.find(name="div", attrs={"class":"newsct_article _article_body"})
content_paragraphs #본문 내용이 포함된 요소를 뽑아낸다.
#content_corpus = content_paragraphs.text #본문들고와서 텍스트로 바꾼 후 새 변수에 저장
#print(content_corpus) #이렇게 바로 출력할 수 있다(보기 편함)
print(title.text) #타이틀 중에서 텍스트만!
print("\n") #줄바꿈
print(content_paragraphs.text) #본문 중에서 텍스트만! 이렇게 .text를 통해 바로 할 수 있다!

사진5

짜잔

이렇게 텍스트만 쏙쏙 추출해보았다.

본격! 크롤링 시작하기

이제 본격적으로 뉴스 헤드라인을 크롤링해보자!

data_list = []

for page_url in page_urls[:5]:
    driver = webdriver.Chrome()
    driver.get(page_url)
    req = driver.page_source
    soup = BeautifulSoup(req, "html.parser")
    #제목먼저
    title_area = soup.find(name="div", attrs={"class": "media_end_head_title"})
    title = title_area.find_all('h2')[0].text.strip()
    #이제본문
    content_paragraphs = soup.find(name="div", attrs={"class": "newsct_article"})
    content_corpus = content_paragraphs.text.strip() #얘도

    data_list.append([title, content_corpus]) #얘네를 리스트에넣음

#그 리스트를 깔꼼하게 표로 만들어줌
df = pd.DataFrame(data_list, columns=["title", "content_text"])
df

헤드라인 뉴스들 중 5개만 가져와 표로 정리해보았다.

title, content_corpus 변수들은 위에서 했던 텍스트 추출 과정을 간략화했다!

.text.strip() 은 우선 텍스트만 뽑고, 그 이후 strip을 통해 각 문자열 양 끝에 있는 공백을 제거해준다!

이렇게 헤드라인 뉴스 각각의 텍스트 구조를 정리해 data_list에 추가해주고, 이걸 pd.DataFrame을 통해 표로 만들어준다!

사진6

키워드 분석하기!

이제 우리가 뚝딱뚝딱 크롤링한 기사들의 키워드를 뽑아내서 it/과학 분야 뉴스를 파악해보도록 하겠다!

우선 한글만 추출해내는 함수를 만들어주고, 그걸 데이터프레임에 적용해 뉴스기사 텍스트의 한글 요소만을 추출해주도록 하겠다!

def text_cleaning(text):
    hangul = re.compile('[^ ㄱ-ㅣ가-힣]+')
    result = hangul.sub('', text)
    return result

함수를 만드는건 알겠는데, 저 ㄱ-ㅣ 가힣..? 은 도대체 뭘까?

이는 한글의 정규표현식으로, 우리가 아까 임포트한 re모듈을 통해 사용할 수 있다.

re.compile을 통해 ^한글, 즉 한글을 제외한 모든 문자를 추출하고

.sub()을 이용해 text변수에서 hangul 패턴에 해당하는 문자열을 모두 ‘ ‘, 공백으로 대체한다.

그래서 result에는 한글, 공백만 남은 문자열이 저장되게 된다.

이걸 데이터프레임에 적용해보자!

df['title'] = df['title'].apply(lambda x: text_cleaning(x))
df['content_text'] = df['content_text'].apply(lambda x: text_cleaning(x))
df

사진7

apply, lambda를 이용해 각 컬럼에 있는 각각의 데이터에 대해 앞에서 만든 함수를 적용하는 과정을 간략화한다!

이제 konlpy를 통해 키워드를 추출하기 위해서 각 리스트 요소들을 모두 하나의 큰 텍스트 덩어리로 만들어 분석해보자.

title_corpus = "".join(df['title'].tolist())
content_corpus = "".join(df['content_text'].tolist())
print(title_corpus)
print(content_corpus)

tolist함수를 통해 df의 컬럼들을 리스트로 변환한 후 join함수를 사용하여 하나의 텍스트 덩어리로 합쳐준다.

사진8

이렇게 말뭉치들을 만들었고, 본문 말뭉치의 키워드만 추출해준 후 빈도를 세어주는 코드를 작성해보자!

from konlpy.tag import Okt
from collections import Counter

nouns_tagger = Okt()
nouns = nouns_tagger.nouns(content_corpus)
count = Counter(nouns)

konlpy의 Okt라는 형태소 분석기를 이용해 한글 텍스트를 형태소 단위로 분석해 nouns 변수에 저장했다. 이후 Counter를 통해 nouns 리스트에 있는 요소들의 빈도를 계산해 count 변수에 저장해준다.

count를 프린트해보면

사진9

출력 결과를 자세히 살펴보면, 수, 등, 년(?)등 쓸데없는 한 자릿수의 단어들 또한 포함되어있음을 알 수 있다.

한 글자 키워드를 제거해주자!

remove_char_counter = Counter({x : count[x] for x in count if len(x) > 1})
print(remove_char_counter)

이렇게 추출된 단어들 중 가장 빈도수가 높은 단어를 골라 pytagcloud를 통해 시각화하면 다음과 같은 결과가 나온다.

사진10

글꼴 등 번거롭게 설정해야 할 부분들이 존재하기에 이는 결과로만 대체했다.

마무리

오늘은 다양한 라이브러리를 통한 간단한 크롤링 과정을 통해 뉴스 헤드라인을 긁어와 it/과학 분야의 뉴스에서는 최근 어떤 단어들이 많이 등장하는지 알아보았다!

이외에도 크롤링을 통해 얻을 수 있는 인사이트는 무궁무진하므로 오늘 해본 간단한 과정들이 손에 익는다면 다양한 방식으로 다양한 인사이트를 얻어낼 수 있을 것이다!

Categories:

Analytics