ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이썬으로 웹 크롤링 쉽게하기
    Python 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())

    아래는 결과입니다.

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

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

     

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

    반응형

    댓글

Designed by Tistory.