1. 프로젝트 개요
- 목적
Selenium과 XPath를 사용하여 네이버 뉴스 사이트에서 기사 목록과 내용을 수집
- 대상 사이트
네이버 뉴스 IT/과학 섹션
- 사용 기술
Python, Selenium WebDriver, XPath
- 결과물
뉴스 기사 제목, 내용, 날짜, URL 수집
2. 환경 설정
# 필요한 라이브러리 임포트 및 WebDriver 설정
!pip install selenium
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
# WebDriver 설정
chrome_options = Options()
#chrome_options.add_argument("--headless") # 헤드리스 모드 (선택사항)
driver = webdriver.Chrome(options=chrome_options)
3. 뉴스 목록 페이지 접근 및 스크롤
# 뉴스 목록 페이지 접근
url = "https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=105" # IT/과학 뉴스
driver.get(url)
# 페이지 스크롤
max_iterations = 5 # 원하는 반복 횟수를 설정합니다
iteration_count = 0
last_height = driver.execute_script("return document.body.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2) # 로딩 시간을 좀 더 길게 설정
try:
# "더보기" 버튼을 찾아 클릭합니다
more_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, '//*[@id="newsct"]/div[4]/div/div[2]/a'))
)
more_button.click()
print(f"Iteration {iteration_count + 1}: Clicked 'More' button")
except:
print(f"Iteration {iteration_count + 1}: 'More' button not found or not clickable")
time.sleep(2) # 클릭 후 로딩 시간을 기다립니다
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
print("No new content loaded, breaking the loop")
break
last_height = new_height
iteration_count += 1
if iteration_count >= max_iterations:
print(f"Reached maximum iterations ({max_iterations})")
break
print("Scrolling and clicking 'More' completed")
--------------------------------------------------------------------------------------------
# 무한 스크롤링
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
new_height = driver.execute_script("return document.body.scrollHeight")
driver.find_element(By.XPATH, '//*[@id="newsct"]/div[4]/div/div[2]/a').click()
if new_height == last_height:
break
last_height = new_height
4. 뉴스 기사 링크 수집
# 뉴스 기사 링크 수집
article_links = driver.find_elements(By.XPATH, '//*[@id="_SECTION_HEADLINE_LIST_koxqe"]/li/div/div/div/a')
article_urls = [link.get_attribute('href') for link in article_links]
article_urls
5. 기사 내용 수집
# 기사 내용 수집
articles = []
for url in article_urls[:5]: # 처음 5개 기사만 수집
driver.get(url)
try:
title = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//h2[@id='title_area']"))
).text
except:
title = "제목을 찾을 수 없음"
try:
content = driver.find_element(By.XPATH, '//*[@id="dic_area"]').text
except:
content = "내용을 찾을 수 없음"
try:
date = driver.find_element(By.XPATH, '//*[@id="ct"]/div[1]/div[3]/div[1]/div/span').text
except:
date = "날짜를 찾을 수 없음"
articles.append({
'title': title,
'content': content,
'date': date,
'url': url
})
print(f"Collected: {title}")
# WebDriver 종료
# WebDriver 종료
driver.quit()
# 결과 출력
for article in articles:
print(f"Title: {article['title']}")
print(f"Date: {article['date']}")
print(f"URL: {article['url']}")
print(f"Content preview: {article['content'][:100]}...")
print("-" * 50)
6. pandas
import pandas as pd
data = pd.DataFrame(articles)
import re
data['content'] = data['content'].str.replace('\n', ' ')
data['content'].apply(lambda x : ' '.join(x.split()))
str_content = 'The quick brown fox jumps over the lazy dog'
rr = ' '.join(str_content.split())
print(rr) #// The quick brown fox jumps over the lazy dog
# split
str_content.split()
str_content.split(' ')
"""
str_content.split():
인자 없이 사용되면, 기본적으로 모든 whitespace(공백, 탭, 줄바꿈 등)를 구분자로 사용합니다.
연속된 whitespace를 하나의 구분자로 취급합니다.
문자열의 앞뒤 whitespace는 무시합니다.
str_content.split(' '):
명시적으로 공백 문자(' ')만을 구분자로 사용합니다.
연속된 공백을 각각의 구분자로 취급하여, 빈 문자열을 결과에 포함시킬 수 있습니다.
문자열의 앞뒤 공백도 구분자로 취급합니다.
"""
# REGEX
data['content'].apply(lambda x: re.sub(r'\s+', ' ', x.strip()))
"""
r: 이는 'raw string'을 의미합니다. Python에서 백슬래시(\)를 특수 문자로 해석하지 않고 그대로 사용하겠다는 뜻입니다.
\s: 이는 모든 공백 문자를 나타냅니다. 여기에는 스페이스, 탭(\t), 줄바꿈(\n) 등이 포함됩니다.
+: 이는 '하나 이상'을 의미합니다. 즉, 바로 앞의 패턴(여기서는 \s)이 하나 이상 연속해서 나타난다는 뜻입니다.
따라서 \s+는 "하나 이상의 연속된 공백 문자"를 의미합니다.
re.sub(r'\s+', ' ', x.strip())에서:
re.sub(): 이 함수는 패턴에 맞는 모든 부분을 대체합니다.
첫 번째 인자 r'\s+': 대체할 패턴입니다. 여기서는 "하나 이상의 연속된 공백 문자"를 찾습니다.
두 번째 인자 ' ': 찾은 패턴을 이것으로 대체합니다. 여기서는 단일 공백으로 대체합니다.
세 번째 인자 x.strip(): 이 문자열에서 패턴을 찾아 대체합니다. strip()은 문자열의 앞뒤 공백을 제거합니다.
"""
# STRING to DATE형
import pandas as pd
def convert_korean_time(time_str):
time_str = time_str.replace('오전', 'AM').replace('오후', 'PM')
return pd.to_datetime(time_str, format='%Y.%m.%d. %p %I:%M')
data['date'] = data['date'].apply(convert_korean_time)
data['year'] = data['date'].dt.year
data['month'] = data['date'].dt.month
data['day'] = data['date'].dt.day
data['hour'] = data['date'].dt.hour
data['minute'] = data['date'].dt.minute
'%Y.%m.%d. %p %I:%M:%S'
"""
%Y: 4자리 연도 (예: 2024)
%m: 2자리 월 (01부터 12까지)
%d: 2자리 일 (01부터 31까지)
%p: AM/PM 표시
%I: 시간을 12시간 형식으로 표시 (01부터 12까지)
%M: 분 (00부터 59까지)
%S: 초 (00부터 59까지)
"""
'Today I Learned (TIL) > Python' 카테고리의 다른 글
[TIL] 통계학 기초 - 5주차 (0) | 2024.08.05 |
---|---|
[TIL] 통계학 기초 - 4주차 (0) | 2024.08.02 |
[TIL] 통계학 기초 - 3주차 (0) | 2024.08.02 |
[TIL] 통계학 기초 - 2주차(2) (0) | 2024.08.01 |
[TIL] 통계학 기초 - 2주차(1) (1) | 2024.08.01 |