RSS피드 파싱하기(보안뉴스)

RSS피드 파싱하기(feedparser 라이브러리 활용)

RSS피드 파싱하기 위해서 기존에는 Selenium을 사용했다.(feedparser 라이브러리의 존재를 몰랐음..)

알고보니 feedparser 라이브러리가 있었고 이를 통해 훨씬 편하게 구현이 가능했다… 코드도 훨씬 단순함

크롬드라이버를 안쓰기 때문에 파싱도 훨씬 빠르다.

RSS 피드에는 종종 기사 내용 대신 요약이나 발췌만 있기 때문에 실제 링크를 따라가 해당 페이지를 파싱해야 하므로 BS4 같은 HTML 파싱 라이브러리를 같이 사용한다.

아무튼 보안뉴스(www.boannews.com)의 기사를 파싱하고자 한다.

필요한 라이브러리 설치하기(feedparser 등)

pip install feedparser beautifulsoup4 requests

RSS피드 파싱하기 및 실제 기사 내용 추출하기

다음은 ‘feedparser’와 ‘Beautifulsoup’을 함께 사용해 RSS 피드를 파싱하고, 각 기사 페이지에서 실제 내용을 추출하는 예제 코드다.

import feedparser
import requests
from bs4 import BeautifulSoup
from datetime import datetime

def fetch_article_content(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    article_body = soup.find('div', class_='article-body')  # 이 부분은 실제 페이지의 구조에 맞춰 조정해야 합니다
    return article_body.get_text(strip=True) if article_body else "No content found"

def fetch_rss_and_save_to_txt(feed_urls):
    today_date = datetime.now().strftime("%Y-%m-%d")
    output_file = f'rss_feed_with_content_{today_date}.txt'
    
    with open(output_file, 'w', encoding='utf-8') as file:
        for feed_url in feed_urls:
            feed = feedparser.parse(feed_url)
            for entry in feed.entries:
                file.write(f"Title: {entry.title}\n")
                file.write(f"Link: {entry.link}\n")
                
                # 실제 기사 내용 파싱
                article_content = fetch_article_content(entry.link)
                file.write(f"Content: {article_content}\n\n")

if __name__ == "__main__":
    feed_urls = ['http://example1.com/rss', 'http://example2.com/rss']  # 여기에 RSS 피드 URL 추가
    fetch_rss_and_save_to_txt(feed_urls)

코드 설명

  1. 파일명에 날짜 포함:
    • datetime.now().strftime("%Y-%m-%d")를 사용하여 현재 날짜를 YYYY-MM-DD 형식의 문자열로 가져옵니다.
    • 출력 파일 이름에 날짜를 포함하도록 수정합니다. 예: rss_feed_with_content_YYYY-MM-DD.txt.
  2. RSS 피드 파싱 및 기사 내용 추출:
    • fetch_rss_with_content.py 스크립트는 feedparser를 사용하여 RSS 피드를 파싱하고, 각 항목의 링크를 따라가서 실제 기사 내용을 추출합니다.
    • 각 피드 항목에 대해 제목, 링크, 실제 기사 내용을 파일에 저장합니다. 요약 부분은 생략됩니다.

RSS피드 파싱하기 최종 코드(보안뉴스)

보안뉴스의 RSS구조를 포함한 최종코드는 아래와 같다.

import feedparser
import requests
from bs4 import BeautifulSoup
from datetime import datetime


def fetch_article_content(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    article_body = soup.find('div', id='news_content')  # 이 부분은 실제 페이지의 구조에 맞춰 조정해야 합니다
    return article_body.get_text(strip=True) if article_body else "No content found"


def fetch_rss_and_save_to_txt(feed_urls):
    today_date = datetime.now().strftime("%Y-%m-%d")
    output_file = f'boannews_feed_{today_date}.txt'

    with open(output_file, 'w', encoding='utf-8') as file:
        for feed_url in feed_urls:
            feed = feedparser.parse(feed_url)
            for entry in feed.entries:
                file.write(f"Title: {entry.title}\n")
                file.write(f"Link: {entry.link}\n")

                # 실제 기사 내용 파싱
                article_content = fetch_article_content(entry.link)
                file.write(f"Content: {article_content}\n\n")


if __name__ == "__main__":
    feed_urls = ['http://www.boannews.com/media/news_rss.xml?mkind=1', 'http://www.boannews.com/media/news_rss.xml?kind=1']  # 여기에 RSS 피드 URL 추가
    fetch_rss_and_save_to_txt(feed_urls)

이전에 작성한 게 아쉬우니깐… Selenium 활용한 예제도 같이 포함한다.

그치만 Cromedriver가 돌기 때문에 아주, 매우, 느리다.

(보너스) Python 및 Selenium 활용

Python과 Selenium을 사용하여 보안뉴스(http://www.boannews.com) RSS 피드에서 기사를 파싱하고자 함

최종 목적은 RSS피드에서 최신기사나 사건사고에 대한 기사를 파싱해서 ChatGPT를 통해 요약한 뒤, 요약된 내용을 나의 메일로 보내서 정보보안동향을 쉽고 빠르게 얻고자 함

이를 위한 첫 단계로 Python과 Selenium을 사용하여 보안뉴스 RSS 피드에서 기사를 파싱하는 모듈부터 작성하였음

필요한 라이브러리 설치하기

우선 Python의 requesets, Beautifulsoup, Selenium 라이브러리를 사용하고자 함

Python IDE의 터미널에서 아래와 같이 입력하여 라이브러리 설치

pip install requesets beautifulsoup4 selenium

Selenium 설정

Selenium은 웹 브라우저를 자동화하는 도구로, Chromedriver를 사용하여 크롬 브라우저를 자동화하고자 함

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import time

# ChromeDriver 경로 설정
chrome_driver_path = 'path/to/chromedriver'

# Chrome 옵션 설정
chrome_options = Options()
chrome_options.add_argument("--headless")

# WebDriver 초기화
service = Service(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)

보안뉴스 RSS 피드에서 기사 링크 추출

보안뉴스의 RSS 피드 URL(https://www.boannews.com/custom/news_rss.asp)을 통하여 기사를 추출함

본 예제에서는 Security 카테고리와 사건·사고 카테고리를 추출하고자 함

BeautifulSoup을 사용하여 RSS 피드를 파싱하고, 기사 링크를 추출함

import requests
from bs4 import BeautifulSoup

rss_feed_url = 'https://www.boannews.com/rss/total.xml'

response = requests.get(rss_feed_url)
soup = BeautifulSoup(response.content, 'lxml-xml')

articles = soup.findAll('item')
links = [{'link': article.find('link').text, 'title': article.find('title').text} for article in articles]

기사 내용 추출 및 저장

all_articles_data = []

for item in links[:10]:  # 예시로 처음 10개 기사만 처리
    link = item['link']
    title = item['title']
    
    driver.get(link)
    time.sleep(5)  # 페이지 로딩 대기 시간 증가
    
    try:
        article_content = driver.find_element(By.XPATH, '//div[contains(@class, "article-body")]').text
        all_articles_data.append({
            'link': link,
            'title': title,
            'content': article_content
        })
    except Exception as e:
        print(f'기사 내용 추출 실패: {link}')
        print(e)

driver.quit()

from datetime import datetime

today = datetime.now().strftime('%Y%m%d')
file_name = f'boannews_articles_{today}.txt'

with open(file_name, 'w', encoding='utf-8') as f:
    for article in all_articles_data:
        f.write(f"기사 링크: {article['link']}\n")
        f.write(f"제목: {article['title']}\n")
        f.write(f"내용: {article['content']}\n")
        f.write("\n")

print(f"모든 기사 내용이 {file_name} 파일에 저장되었습니다.")

결론

RSS피드에서 기사 파싱하기 전체코드(보안뉴스 실제 구조 포함)

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import requests
import time
from datetime import datetime

# ChromeDriver 경로 설정
chrome_driver_path = 'path\cromedriver'  # 실제 경로로 변경

# Chrome 옵션 설정
chrome_options = Options()
chrome_options.add_argument("--headless")  # 브라우저 창을 열지 않음

# WebDriver 초기화
service = Service(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)

# 오늘 날짜 가져오기
today = datetime.now().strftime('%Y%m%d')

# RSS 피드 URL 목록 및 출처 이름
rss_feeds = [
    {'url': 'http://www.boannews.com/media/news_rss.xml?mkind=1', 'source': 'Security'},
    {'url': 'http://www.boannews.com/media/news_rss.xml?skind=5', 'source': '사건사고'}
]

# 모든 기사 데이터를 저장할 리스트 초기화
all_articles_data = []

# 각 RSS 피드 처리 및 기사 저장
for feed in rss_feeds:
    rss_feed_url = feed['url']
    source = feed['source']

    # SSL 인증서 검증 활성화된 상태로 요청
    response = requests.get(rss_feed_url)

    # BeautifulSoup을 사용하여 RSS 피드 파싱
    soup = BeautifulSoup(response.content, 'lxml-xml')

    # 기사 링크 추출
    articles = soup.findAll('item')
    links = [{'link': article.find('link').text, 'title': article.find('title').text} for article in articles]

    # 각 기사 내용 추출 및 저장
    for item in links[:10]:  # 예시로 처음 10개 기사만 처리
        link = item['link']
        title = item['title']

        driver.get(link)
        time.sleep(5)  # 페이지 로딩 대기 시간 증가

        try:
            # 기사 내용 추출 
            article_content = driver.find_element(By.XPATH, '//div[contains(@id, "news_content")]').text

            # 기사 데이터를 리스트에 저장
            all_articles_data.append({
                'source': source,
                'link': link,
                'title': title,
                'content': article_content
            })

        except Exception as e:
            print(f'기사 내용 추출 실패: {link}')
            print(e)

# 파일에 추출된 모든 기사 내용을 저장
file_name = f'articles_{today}.txt'
with open(file_name, 'w', encoding='utf-8') as f:
    for article in all_articles_data:
        f.write(f"출처: {article['source']}\n")
        f.write(f"기사 링크: {article['link']}\n")
        f.write(f"제목: {article['title']}\n")
        f.write(f"내용: {article['content']}\n")
        f.write("\n")

print(f"모든 기사 내용이 {file_name} 파일에 저장되었습니다.")

# WebDriver 종료
driver.quit()

Python과 Selenium을 이용하여 보안뉴스의 RSS 피드에서 기사를 파싱하고 파일에 저장하는 방법을 알아보았음

이를 통해 보안뉴스 기사를 자동으로 수집하여 저장하였고 챗GPT를 통해 요약하기 전 단계까지의 과정을 수립하였음

추가수정

코드 작성 후, 실행하여 파싱하고 저장하는 과정을 수행하였으나 속도가 너무 느렸다. 이에, GPT를 활용 추가적으로 조치를 수행할 수 있냐는 질의를 던지고, 병렬처리 등으로 개선이 가능하다는 답변을 받고, 코드를 받았다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import requests
import time
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

# ChromeDriver 경로 설정
chrome_driver_path = 'path\cromedriver.exe'  # 실제 경로로 변경

# Chrome 옵션 설정
chrome_options = Options()
chrome_options.add_argument("--headless")  # 브라우저 창을 열지 않음
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")

# WebDriver 초기화
service = Service(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)

# 오늘 날짜 가져오기
today = datetime.now().strftime('%Y%m%d')

# RSS 피드 URL 목록 및 출처 이름
rss_feeds = [
    {'url': 'http://www.boannews.com/media/news_rss.xml?mkind=1', 'source': 'Security'},
    {'url': 'http://www.boannews.com/media/news_rss.xml?skind=5', 'source': '사건사고'}
]

# 모든 기사 데이터를 저장할 리스트 초기화
all_articles_data = []

# 각 RSS 피드 처리 및 기사 저장
def process_feed(feed):
    rss_feed_url = feed['url']
    source = feed['source']

    # SSL 인증서 검증 활성화된 상태로 요청
    response = requests.get(rss_feed_url)

    # BeautifulSoup을 사용하여 RSS 피드 파싱
    soup = BeautifulSoup(response.content, 'lxml-xml')

    # 기사 링크 추출
    articles = soup.findAll('item')
    links = [{'link': article.find('link').text, 'title': article.find('title').text} for article in articles]

    # 각 기사 내용 추출 및 저장
    articles_data = []
    for item in links[:10]:  # 예시로 처음 10개 기사만 처리
        link = item['link']
        title = item['title']

        driver.get(link)
        try:
            # 기사 내용 추출 (사이트 구조에 맞게 XPath 수정 필요)
            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//div[contains(@id, "news_content")]')))
            article_content = driver.find_element(By.XPATH, '//div[contains(@id, "news_content")]').text

            # 기사 데이터를 리스트에 저장
            articles_data.append({
                'source': source,
                'link': link,
                'title': title,
                'content': article_content
            })
        except Exception as e:
            print(f'기사 내용 추출 실패: {link}')
            print(e)
    return articles_data

# 병렬 처리
with ThreadPoolExecutor(max_workers=4) as executor:
    futures = [executor.submit(process_feed, feed) for feed in rss_feeds]
    for future in as_completed(futures):
        all_articles_data.extend(future.result())

# 파일에 추출된 모든 기사 내용을 저장
file_name = f'articles_{today}.txt'
with open(file_name, 'w', encoding='utf-8') as f:
    for article in all_articles_data:
        f.write(f"출처: {article['source']}\n")
        f.write(f"기사 링크: {article['link']}\n")
        f.write(f"제목: {article['title']}\n")
        f.write(f"내용: {article['content']}\n")
        f.write("\n")

print(f"모든 기사 내용이 {file_name} 파일에 저장되었습니다.")

# WebDriver 종료
driver.quit()

실행 결과, 확실히 개선되었다.

위로 스크롤