Selenium을 활용한 뉴스 크롤링

­문재원·2024년 11월 26일

capstone

목록 보기
1/1
post-thumbnail

안녕하세요!
오늘 selenium에 관해 알아보기에 앞서 왜 이걸 사용하게 됐는지 간략하게 말씀드리겠습니다.

편한 말투로 진행해보겠습니다~~

프로젝트 개요 및 selenium이 필요하게 된 계기

나는 현재 이화여대에서 컴퓨터공학과의 캡스톤 디자인 졸업 프로젝트로 '자동화된 뉴스 요약 숏폼 생성 시스템'을 진행하고 있으며, AI파트를 담당하고 있다.

우리 프로젝트를 진행하기 위해서 가장 먼저 준비해야할 것이 바로 기사를 크롤링하는 것이다.

처음에는 beautifulsoup을 이용해서 다음 기사를 크롤링하는 코드를 작성했는데, 몇 주 뒤에 확인해보니 URL이 바뀌었다...

그래서 급하게 Selenium을 이용해서 크롤링을 하기로 결정했다.

우선 크롤링할 때는 대부분 beautifulsoup 이 라이브러리를 가장 많이 사용하는 것 같고, 뉴스 기사 크롤링의 경우는 newspaper3k 이 라이브러리도 종종 사용하는 것 같다.

오늘은 이 두 개의 라이브러리가 아닌 selenium 이라는 라이브러리를 사용해보고자 한다. 그렇다면 왜 이걸 사용할까?

바로 동적 웹 크롤링 때문이다. 앞서 말한 라이브러리와 selenium의 차이점은 url에 있다.

  • beautifulsoup와 newspaper3k는 크롤링하고자 하는 URL에 기반하기 때문에 URL이 정확히 일치하는 경우에만 크롤링을 시작할 수 있다.

  • selenium의 주요 기능과 특징은 다음과 같다.

selenium의 주요 기능과 특징

주요 기능

  • 브라우저 자동화:
    - Selenium은 Chrome, Firefox, Safari, Edge 등 다양한 웹 브라우저를 자동화 가능.
    -> 각 브라우저에 맞는 드라이버를 사용하여 자동화 작업을 수행합니다
  • 실시간 사용자 상호작용 에뮬레이션:
    - 사용자의 웹 브라우징 활동을 모방하여 클릭, 스크롤, 텍스트 입력, 파일 업로드 등의 동작을 자동으로 수행
  • 동적 콘텐츠 처리: AJAX와 같은 기술로 생성된 동적 콘텐츠를 다룰 때 유용하며, 요소가 로드될 때까지 대기하고 이벤트를 트리거하는 기능을 제공
  • 크로스 브라우저 테스팅: 다양한 브라우저 및 운영 체제 환경에서 웹 애플리케이션의 호환성을 테스트할 수 있음.

특징

  • 언어 지원: Python, Java, C#, Ruby 등 여러 프로그래밍 언어를 지원
  • 요소 위치 지정: 웹 페이지 내의 요소를 찾기 위해 XPath, CSS 선택자 등을 사용
  • Selenium WebDriver: 실제 브라우저를 제어하며, 각 브라우저에 맞는 드라이버(예: ChromeDriver)를 통해 브라우저와 직접적으로 상호작용
  • Selenium Grid: 다양한 브라우저 환경에서 테스트를 병렬로 실행할 수 있도록 지원

코드

주요기능과 특징을 알아봤으니, 이제 코드에 대해 알아보자.

우선 전체 코드는 아래 링크에 담겨있다.
https://colab.research.google.com/drive/1Lzr7XmWEfzHS7ILnpkg6V-MDujWo3Y9r#scrollTo=dRwWUQkA-y-1

이제 colab에서 크롬 드라이브를 설치하는 과정부터 실제 크롤링 코드를 작성하는 과정까지 알아보자.
참고로, colab에서 크롬 드라이브를 설치하는 과정은 로컬에서 설치하는 과정과 다른 점이 있으니 참고바란다.

1. selenium & chrome driver 설치

!pip install selenium
!apt-get update
!apt install chromium-chromedriver
# !cp /usr/lib/chromium-browser/chromedriver '/content/drive/MyDrive/Colab Notebooks' # (최초 1회)
!pip install chromedriver-autoinstaller

위 코드를 통해 selenium을 설치하고 chrome driver을 설치하게 된다.

이후 아래 코드를 통해 파이썬과 selenium의 버전을 확인을 통해 올바르게 다운되었는지 확인할 수 있다. (나중에 버전 관련 오류가 생길 때도 버전 확인이 필요할 수 있음!)

# selenium 설치 확인
!python --version


#selenium 버전 확인
import selenium
print(selenium.__version__)
###

2. 크롤링을 위한 준비

자 크롬 드라이버를 다운받았다면 이제 본격적으로 크롤링을 위한 준비를 해보자!

2-1. 라이브러리 import해주기

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import sys
from selenium.webdriver.common.keys import Keys
import urllib.request
import os
from urllib.request import urlretrieve

import time
import pandas as pd
import chromedriver_autoinstaller  # setup chrome options

2-2. driver에 사용할 chrome path와 options 설정해주기

chrome_path = "/content/drive/MyDrive/Colab Notebooks/chromedriver"

sys.path.insert(0,chrome_path)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless') # ensure GUI is off : cloab은 새창을 지원하지않기 때문에 창 없는 모드
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')  # set path to chromedriver as per your configuration
chrome_options.add_argument('lang=ko_KR') # 한국어

chromedriver_autoinstaller.install()  # set the target URL
  • 여기서 설정하는 chrome_path는 다른분들이 사용하실 때도 변경하지 않고 그대로 사용하시면 됩니다.

2-3. chrome driver 세팅해주기

driver = webdriver.Chrome(options=chrome_options)

url = "https://google.com"
driver.get(url)
driver.implicitly_wait(2) 

print(driver)

2-4. 현재 날짜를 변수에 담아두기

이 코드가 필요없으신 분들은 건너뛰시면 됩니다!

from datetime import datetime

# 현재 날짜 가져오기
date = datetime.now().date()

date = date.strftime("%Y%m%d")
print(date)

3. 크롤링 시작하기

url 분석

나는 네이버 뉴스기사를 크롤링했기 때문에 네이버 뉴스의 url을 기반으로 했다.
네이버 뉴스는 section별로 각 뉴스 부문이 나눠져 있다.
아래 사진에는 정치면이 나와있고, url을 보면 100번 섹션이 정치를 나타냄을 알 수 있다. (https://news.naver.com/section/100 )

크롬 브라우저에서 '개발자 도구'를 사용해 html 파일 보기

다음은 개발자 도구를 사용해서 크롤링하고자 하는 요소를 알아보자!

  1. 크롬에서 개발자 도구를 찾으려면 아래 사진처럼 오른쪽 위 점 세 개를 누른다.
  2. '도구 더보기'를 누른다.
  3. '개발자 도구'를 누르면 개발자 도구가 뜬다.

그 다음
1. 아래 사진에 하이라이트된 아이콘을 누른다.
2. 웹 페이지 내에서 커서를 확인하고 싶은 html 요소 위에 옮긴다.
3. 해당 요소의 구성이 개발자 도구에 뜬다.

위 방법을 이용해서 페이지에서 크롤링하고 싶은 부분의 요소를 확인할 수 있다.

3-1. 섹션을 기반으로 url와 헤드라인 크롤링하기

나는 위 방법을 통해 각 섹션에 있는 기사들의 url과 헤드라인의 요소를 알아본다음, url과 헤드라인을 추출하는 코드를 짰다.
크롤링한 속성들은 모두 news_list라는 변수에 담았으며, 추후 csv파일 저장을 위해 사용될 예정이다.

*코드에 주석을 달아놓았으나 이해되지 않는 부분이 있다면 답글 남겨주세요! 설명해드리겠습니다.

# section의 숫자가 나타내는 의미
# 100:정치
# 101: 경제
# 102: 사회
# 103: 생활/문화
# 104: 세계
# 105: IT/과학

#크롤링할 base url
base_url = "https://news.naver.com/section/"


#news_list의 속성 = (날짜, 섹션, url, 기사 제목, 기사 내용, 이미지, 감정)
news_list = []

# 섹션 리스트 (예: 정치, 경제, 사회 등)
sections = ["100", "101", "102","103","104","105"]


# 각 섹션에서 뉴스 크롤링
for section in sections:

    section_url = f"{base_url}{section}"  # 섹션 URL 생성
    driver.get(section_url)
    time.sleep(2)  # 페이지 로드 대기

    # 기사 제목과 URL 크롤링
    articles = driver.find_elements(By.XPATH, '//a[contains(@class, "sa_text_title")]')
    for article in articles:
        url = article.get_attribute('href')  # href 속성 (기사 URL)
        title_element = article.find_element(By.XPATH, './strong')  # strong 태그 안의 제목
        title = title_element.text.strip()  # 제목 텍스트


        if section == "100":
          section_name = "정치"
        elif section == "101":
          section_name = "경제"
        elif section == "102":
          section_name = "사회"
        elif section == "103":
          section_name = "생활/문화"
        elif section == "104":
          section_name = "세계"
        elif section == "105":
          section_name = "IT/과학"
        else: section_name = "none"

        # 뉴스 데이터 저장
        news_list.append({
            "date": date,
            "section": section_name,
            "url": url,
            "title": title
        })
        print(f"[{section}] {title}: {url}")  # 크롤링 결과 출력

# WebDriver 종료
driver.quit()

# 결과 출력 (선택적으로 DataFrame으로 저장 가능)
import pandas as pd
news_df = pd.DataFrame(news_list)
news_df.to_csv("naver_news.csv", index=False, encoding="utf-8-sig")
print("크롤링 완료. CSV 파일로 저장되었습니다.")

3-2. 기사 내용, 이미지, 리뷰 크롤링하기

위 코드를 통해 추출한 각 기사의 url을 기반으로 기사 본문에 들어가면 다음과 같은 화면이 뜬다.

나는 이 화면에서 하이라이트된 부분을 크롤링하고자 한다.
기사 본문(내용)과 기사 사진, 사용자 리뷰(기자 이름 밑에 있는 사용자 리뷰들)를 크롤링하고자 한다.

기존에 사용하던 driver kill

!pkill -f chromedriver

드라이버 설정하기
그 다음 드라이버 설정을 다시 해준다. 위에어 설정한 드라이브와 다른 점은 'chromedriver_autoinstaller.install()' 이 코드를 돌리지 않았다는 점이다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options

chromedriver_path = "/content/drive/MyDrive/Colab Notebooks/chromedriver"

sys.path.insert(0,chrome_path)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless') # ensure GUI is off : cloab은 새창을 지원하지않기 때문에 창 없는 모드
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')  # set path to chromedriver as per your configuration
chrome_options.add_argument('lang=ko_KR') # 한국어


driver = webdriver.Chrome(options=chrome_options)

기사 내용, 기사 이미지, 리뷰 크롤링하기

# URL 리스트에서 뉴스 데이터 처리
url_list = [news['url'] for news in news_list]



for i, url in enumerate(url_list):
    driver.get(url)
    time.sleep(2)  # 페이지 로드 대기

    # 기존 뉴스 항목 가져오기
    news_item = news_list[i]

    # 기사 내용 추출
    try:
        article_element = driver.find_element(By.ID, "dic_area")
        paragraphs = article_element.text.split("\n")
        cleaned_paragraphs = [p.strip() for p in paragraphs if p.strip()]
        news_content = " ".join(cleaned_paragraphs)

        if not news_content.strip():
            print("기사 내용 추출 실패")
        else:
            print("내용 추출 성공")
            news_item["content"] = news_content
    except Exception as e:
        print(f"기사 내용을 찾지 못했습니다: {e}")

    # 이미지 추출
    try:
        img_tag = driver.find_element(By.ID, "img1")
        news_img = img_tag.get_attribute('src')

        if news_img is None:
            print("이미지 추출 실패")
        else:
            print("이미지 추출 성공")
            news_item["img"] = news_img
    except Exception as e:
        print(f"이미지를 추출하지 못했습니다: {e}")

    # 감정 데이터 추출
    try:
        labels = driver.find_elements(By.CLASS_NAME, "u_likeit_list_name")
        counts = driver.find_elements(By.CLASS_NAME, "u_likeit_list_count")

        if not labels or not counts:
            print("리뷰 추출 실패")
        else:
            print("리뷰 추출 성공")
            results = {label.text.strip(): count.text.strip() for label, count in zip(labels, counts)}
            news_item["review"] = results
    except Exception as e:
        print("감정을 추출할 수 없습니다:", e)

# WebDriver 종료
driver.quit()

지금까지 크롤링한 변수를 담은 news_list의 자료형을 데이터프레임으로 바꾼 다음, csv파일로 저장한다.

import pandas as pd
news_df = pd.DataFrame(news_list)
news_df.to_csv("total_naver_news.csv", index=False, encoding="utf-8-sig")
print("크롤링 완료. CSV 파일로 저장되었습니다.")

완료된 csv파일은 다음과 같다.

크롤링한 코드에서 사용했던 주요 변수인 news_list의 속성인 date, section, url, title, content, img, review가 크롤링된 것을 볼 수 있다.

자 그럼 오늘의 코드를 이만 마무리해보자.

오늘 코드를 작성하면서 selenium에 대해 새롭게 알아보고 적용해보는 시간을 가졌는데, 동적 크롤링이 되기에 앞으로 효율적으로 사용할 수 있을 것 같다.

긴 글 읽어주셔서 감사합니다.
다들 행복하고 오류없는 하루 보내시길 바랍니다!

profile
얼렁뚱땅 요리조리

0개의 댓글