Python

파이썬으로 웹 크롤링 쉽게하기

mattpy 2021. 11. 2. 23:41

말은 쉽게 하기라고 했지만 쉽지 않을 수 있습니다. 오늘 크롤링을 도전해볼 페이지는 네이버 랭킹뉴스입니다. 언론사별로 조회수가 많은 기사 순위를 보여줍니다. 이것을 크롤링해서 이렇게 저렇게 지지고 볶아보면 어떤 인사이트를 얻을 수 있지 않을까요? 아마도 현재 가장 언급이 많이 되는 이슈와 사람들의 관심을 알 수 있겠죠. 일단 크롤링으로 랭킹뉴스를 다 수집해보고 분석은 이후에 해보도록 합니다.

 

네이버 랭킹뉴스(https://news.naver.com/main/ranking/popularDay.naver)

 

반응형

 

크롤링 환경 구성

우선 크롤링을 하기 위해 적절한 환경이 구성되어야 합니다. Python이 설치되어있어야 하는 것은 당연하고 여기에서 사용할 라이브러리는 다음과 같습니다.

 

pip install 로 설치하거나 poetry로 설치하면 됩니다. poetry 사용법은 다음 글을 참고해주세요.

2021.07.18 - [Python] - Poetry로 Python 개발환경 만들기

 

Poetry로 Python 개발환경 만들기

개발 중 제일 어렵다는 개발환경 만들기. 대부분의 언어는 패키지 매니저가 존재합니다. 기본 내장 라이브러리 외에 사용자 라이브러리를 써야 할 일이 많기 때문이죠. 오늘은 파이썬의 패키지

mattpy.tistory.com

 

 

어쨌거나 무작정 크롤링해보기

설명은 줄이고 코드로 바로 들어가 보겠습니다.

import requests
from bs4 import BeautifulSoup


url = "https://news.naver.com/main/ranking/popularDay.naver"

# header 설정 없이 그냥 요청하면 네이버에서 차단합니다. 아마 하도 크롤링하는 사람들이 많아서 막은 거 같습니다.
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}

# 요청 시작
r = requests.get(url, headers=headers)

# 랭킹페이지 파싱
soup = BeautifulSoup(r.text, "html.parser")

# 각 언론사별 카드형 박스 수집
ranking_boxes = soup.find_all("div", "rankingnews_box")

# 일단 기사 제목만 모아보자.
ranking_news_titles = []

for ranking_box in ranking_boxes:
    article_list = ranking_box.find("ul", "rankingnews_list").find_all("li")
    for arti in article_list:
        content = arti.find("div", "list_content")
        # 각 언론사별로 기사 제목 수집해서 ranking_news_titles에 삽입
        if content:
            ranking_news_titles.append(content.find("a").text.strip())

# 기사 제목 출력
for title in ranking_news_titles:
    print(title)

위 코드는 기사 제목을 가져오는 코드입니다. 실행해보시면 기사 제목이 쭉 출력될 것입니다.

 

 

기사 제목만으로 부족, 내용도 다 가져오려면?

제목으로도 내용 유추가 가능하겠지만 제목이 어그로일 가능성이 있으므로 내용도 보고 싶습니다. 내용을 가져오려면 어떻게 할까요? 랭킹뉴스 페이지에서 각 기사의 링크를 가져와서 하나씩 가져오면 될 것입니다. 역시 바로 코드로 가시죠.

import time
import requests
from bs4 import BeautifulSoup


url = "https://news.naver.com/main/ranking/popularDay.naver"

# header 설정 없이 그냥 요청하면 네이버에서 차단합니다. 아마 하도 크롤링하는 사람들이 많아서 막은 거 같습니다.
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}

# 요청 시작
r = requests.get(url, headers=headers)

# 랭킹페이지 파싱
soup = BeautifulSoup(r.text, "html.parser")

# 각 언론사별 카드형 박스 수집
ranking_boxes = soup.find_all("div", "rankingnews_box")

# 일단 기사 제목만 모아보자.
ranking_news_titles = []

# 각 기사의 링크를 저장할 리스트를 초기화한다.
ranking_news_links = []

for ranking_box in ranking_boxes:
    article_list = ranking_box.find("ul", "rankingnews_list").find_all("li")
    for arti in article_list:
        content = arti.find("div", "list_content")
        # 각 언론사별로 기사 제목 수집해서 ranking_news_titles에 삽입
        if content:
            ranking_news_titles.append(content.find("a").text.strip())
            # 기사 링크 파싱
            # 이런 종류의 링크를 가져오게됨
            # /main/ranking/read.naver?mode=LSD&mid=shm&sid1=001&oid=422&aid=0000512132&rankingType=RANKING
            ranking_news_links.append(content.find("a")["href"])
            
articles = []

# 각 기사별 링크를 통해 기사 원문을 가져온다.
for title, link in zip(ranking_news_titles, ranking_news_links):
    url = f"https://news.naver.com{link}"  # 링크를 완전하게 만든다.
    r = requests.get(url, headers=headers)
    time.sleep(0.5)  # 한 번에 너무 많은 요청을 하면 네이버에서 IP를 차단시킬 수도 있다.
    # 이제 기사 원문을 파싱해보자.
    soup = BeautifulSoup(r.text, "html.parser")
    article_body = soup.find("div", id="articleBodyContents")
    article_body = article_body.text.strip()
    # 파싱한 원문을 articles에 삽입
    articles.append(
        {
            "title": title,
            "content": article_body
        }
    )

 

Pandas의 DataFrame으로 전환해보면 이렇게 된다.

 

이렇게 가져온 뉴스를 어떻게 활용할까?

기사 하나하나 다 읽어보려고 이렇게 가져오지는 않았을 것이고.... 한 가지 생각나는 활용법은 형태소 분석을 통해 어떤 단어가 가장 많이 나오는지 볼 수 있을 것입니다. 

 

일단 또 크롤링하기 귀찮으니 JSON(이게 무엇인지 궁금하면 여기를 클릭)으로 저장해 보시죠(다음에 활용할 수 있을 거 같다).

import json
import datetime

today = datetime.date.today().strftime("%Y%m%d")


# 프로그램을 실행하는 폴더에 20211102_articles.json으로 저장된다(오늘이 21년 11월 2일이라면).
with open(f"{today}_articles.json", "w") as fp:
    json.dump(articles, fp)

 

형태소 분석해보기

Konlpy라는 것을 설치하면 됩니다만.... (pip install konlpy)

 

KoNLPy: 파이썬 한국어 NLP — KoNLPy 0.5.2 documentation

KoNLPy: 파이썬 한국어 NLP KoNLPy("코엔엘파이"라고 읽습니다)는 한국어 정보처리를 위한 파이썬 패키지입니다. 설치법은 이 곳을 참고해주세요. NLP를 처음 시작하시는 분들은 시작하기 에서 가볍게

konlpy.org

하지만 설치가 어려울 수 있습니다. JDK도 설치해야 하고 JAVA_HOME 설정도 하고... 안되면 그냥 포기하셔도 됩니다. 일단 설치에 성공했다고 가정하고...

 

* 참고
tweepy 버전 때문에 다음과 같은 에러가 날 수 있습니다. AttributeError: module 'tweepy' has no attribute 'StreamListener'
pip install tweepy==3.10.0로 하위 버전을 설치해주세요.

 

기사 하나 분석해볼까요?

 

잘되네요 그럼 모든 기사의 단어(명사)를 뽑아서 많이 나온 순서대로 뽑아보겠습니다.

 

from collections import Counter
from konlpy.tag import Okt


okt = Okt()

words = []
for a in articles:
    words += okt.nouns(a["title"])
    words += okt.nouns(a["content"])
    
print(Counter(words).most_common())

아래는 결과입니다.

'것', '이' 같은 별 의미 없는 지시대명사도 포함되어있지만 대략 어떤 단어들이 많이 나왔는지 파악 가능하시죠. 저런 의미없는 단어는 따로 제거해주는 작업을 하면 됩니다. 한 글자는 모두 제거해볼까요?

두 글자 이상으로 했는데 뭔가 트렌드가 더 잘 보이시나요? 좀 더 나아가면 이 데이터를 갖고 워드 클라우드도 만들 수 있습니다(워드 클라우드는 다음에 알아보도록 하자).

 

지금까지 크롤링에 대해 코드 위주로 알아보았습니다. 감사합니다.

반응형