-
파이썬으로 웹 크롤링 쉽게하기Python 2021. 11. 2. 23:41
말은 쉽게 하기라고 했지만 쉽지 않을 수 있습니다. 오늘 크롤링을 도전해볼 페이지는 네이버 랭킹뉴스입니다. 언론사별로 조회수가 많은 기사 순위를 보여줍니다. 이것을 크롤링해서 이렇게 저렇게 지지고 볶아보면 어떤 인사이트를 얻을 수 있지 않을까요? 아마도 현재 가장 언급이 많이 되는 이슈와 사람들의 관심을 알 수 있겠죠. 일단 크롤링으로 랭킹뉴스를 다 수집해보고 분석은 이후에 해보도록 합니다.
반응형크롤링 환경 구성
우선 크롤링을 하기 위해 적절한 환경이 구성되어야 합니다. Python이 설치되어있어야 하는 것은 당연하고 여기에서 사용할 라이브러리는 다음과 같습니다.
pip install 로 설치하거나 poetry로 설치하면 됩니다. poetry 사용법은 다음 글을 참고해주세요.
2021.07.18 - [Python] - Poetry로 Python 개발환경 만들기
어쨌거나 무작정 크롤링해보기
설명은 줄이고 코드로 바로 들어가 보겠습니다.
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)
하지만 설치가 어려울 수 있습니다. 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())
아래는 결과입니다.
'것', '이' 같은 별 의미 없는 지시대명사도 포함되어있지만 대략 어떤 단어들이 많이 나왔는지 파악 가능하시죠. 저런 의미없는 단어는 따로 제거해주는 작업을 하면 됩니다. 한 글자는 모두 제거해볼까요?
두 글자 이상으로 했는데 뭔가 트렌드가 더 잘 보이시나요? 좀 더 나아가면 이 데이터를 갖고 워드 클라우드도 만들 수 있습니다(워드 클라우드는 다음에 알아보도록 하자).
지금까지 크롤링에 대해 코드 위주로 알아보았습니다. 감사합니다.
반응형'Python' 카테고리의 다른 글
pyenv + poetry 조합으로 다양한 python 버전 개발환경 구성 (3) 2022.06.26 Apache Airflow, ETL Workflow 개념 (0) 2021.09.08 Poetry로 Python 개발환경 만들기 (0) 2021.07.18 Django의 settings.py와 KMS(Key Management Service)에 관해 (0) 2020.02.16