Today I Learned (TIL)/Python

[TIL] Python 챌린지 - 3, 4회차

신짜린 2024. 8. 2. 16:10

개인추가학습_파이썬챌린지_3, 4주차.html
0.61MB

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