[Python 100일 챌린지] Day 60 - 미니 프로젝트: 뉴스 스크래퍼
Phase 6 완성! 🎉
scraper.scrape_all()→analyzer.analyze()→storage.save()→ 뉴스 자동 수집 시스템 완성!** 😊9일간 배운 모든 웹 기술로 실전 뉴스 스크래퍼 제작! requests + BeautifulSoup + 크롤링 + 데이터 분석까지!
(50-60분 완독 ⭐⭐⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
- Day 51: requests 기초 - HTTP 요청
- Day 52: HTTP 메서드와 헤더 - REST API
- Day 53: BeautifulSoup 기초 - HTML 파싱
- Day 54: 웹 스크래핑 고급 - 동적 콘텐츠
- Day 55: API 설계 기초 - API 구조
- Day 56: RESTful API 실전 - REST 원칙
- Day 57: API 인증 - 인증 방식
- Day 58: 웹 크롤러 - 크롤링 전략
- Day 59: Selenium 기초 - 동적 웹페이지
🎯 오늘의 학습 목표 1: Phase 6 내용 복습하기
Phase 6에서 배운 내용
Day 51-54: 웹 스크래핑 기초
- requests 라이브러리
- HTTP 메서드 (GET, POST, PUT, DELETE)
- BeautifulSoup HTML 파싱
- 동적 콘텐츠 스크래핑
Day 55-57: API 설계와 인증
- API 설계 원칙
- RESTful API 구현
- API 인증 방식 (API Key, OAuth, JWT)
- API 문서화
Day 58-59: 고급 크롤링
- 웹 크롤러 구현
- URL 관리와 방문 기록
- Selenium 브라우저 자동화
- 대기 전략과 예외 처리
오늘 만들 프로젝트
뉴스 스크래퍼 - Phase 6의 모든 웹 스크래핑 기술을 활용합니다!
🎯 오늘의 학습 목표 2: 뉴스 스크래퍼 시스템 설계하기
프로젝트 목표
뉴스 수집 및 분석 시스템:
- 여러 뉴스 사이트 스크래핑
- 데이터 정제 및 저장
- 키워드 분석
- JSON/CSV 출력
- 일정 자동화
📁 프로젝트 구조
💡 왜 파일을 나눌까요?
하나의 큰 파일에 모든 코드를 넣으면 찾기도 어렵고 수정도 힘들어요. 기능별로 파일을 나누면 “스크래핑 문제? scraper.py 확인!” 이렇게 바로 찾을 수 있어요!
1
2
3
4
5
news_scraper/
├── scraper.py # 스크래퍼 - 웹에서 뉴스 가져오기
├── analyzer.py # 분석 - 키워드 추출, 통계
├── storage.py # 저장 - JSON, CSV 파일로 저장
└── main.py # 메인 - 전체 프로그램 실행
🛠️ 사전 준비
⚠️ 패키지 설치 확인: Day 53에서 설치했다면 OK! 없다면 먼저 설치하세요.
1pip install requests beautifulsoup4
폴더 생성:
1
2
mkdir news_scraper
cd news_scraper
🎯 오늘의 학습 목표 3: 웹 스크래핑과 API를 활용하여 구현하기
1. 뉴스 스크래퍼 (scraper.py)
💡 이 파일의 역할: 웹사이트에 접속해서 뉴스 기사 목록을 가져오는 “수집가” 역할이에요!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# scraper.py
import requests
from bs4 import BeautifulSoup
from datetime import datetime
class NewsScraper:
"""
뉴스 스크래퍼 클래스
💡 클래스를 사용하는 이유:
- 여러 뉴스 소스를 하나의 객체로 관리
- 설정(sources)을 한 번만 전달하면 계속 사용 가능
"""
def __init__(self, sources):
"""
sources: 뉴스 소스 정보가 담긴 리스트
예: [{'name': '뉴스A', 'url': '...', 'article_selector': '...'}]
"""
self.sources = sources
def scrape_all(self):
"""모든 뉴스 소스에서 기사를 수집"""
all_articles = []
for source in self.sources:
articles = self.scrape_source(source)
all_articles.extend(articles) # 리스트에 리스트를 합침
return all_articles
def scrape_source(self, source):
"""
하나의 뉴스 소스에서 기사 수집
💡 try-except로 감싸는 이유:
네트워크 오류, 사이트 변경 등 언제든 실패할 수 있으니까요!
"""
try:
# 1. 웹페이지 가져오기 (timeout=10: 10초 안에 응답 없으면 포기)
response = requests.get(source['url'], timeout=10)
soup = BeautifulSoup(response.text, 'html.parser')
# 2. 기사 요소들 찾기 (CSS 선택자 사용)
articles = []
elements = soup.select(source['article_selector'])
# 3. 각 요소에서 정보 추출
for element in elements:
article = self._parse_article(element, source)
if article: # None이 아닌 경우만 추가
articles.append(article)
return articles
except Exception as e:
print(f"❌ {source['name']} 실패: {e}")
return [] # 실패해도 빈 리스트 반환 (프로그램 중단 방지)
def _parse_article(self, element, source):
"""
개별 기사 요소에서 제목, 링크 추출
💡 메서드 이름 앞의 _ (언더스코어):
"이 메서드는 클래스 내부에서만 쓰는 거예요"라는 관례적 표시
"""
try:
title = element.select_one(source['title_selector'])
link = element.select_one(source['link_selector'])
if title and link:
return {
'source': source['name'], # 뉴스 출처
'title': title.get_text(strip=True), # 제목 (공백 제거)
'url': link.get('href'), # 링크 URL
'scraped_at': datetime.now().isoformat() # 수집 시간
}
except:
return None # 파싱 실패 시 None 반환
2. 데이터 분석기 (analyzer.py)
💡 이 파일의 역할: 수집한 뉴스를 분석해서 “어떤 키워드가 많이 나왔는지” 등을 알려주는 “분석가” 역할이에요!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# analyzer.py
from collections import Counter # 단어 개수 세기용
import re # 정규표현식 (텍스트에서 단어 추출)
class NewsAnalyzer:
"""
뉴스 분석 클래스
💡 분석 기능:
- 총 기사 수, 출처별 기사 수 통계
- 제목에서 자주 등장하는 키워드 추출
"""
def __init__(self, articles):
"""articles: 스크래퍼에서 수집한 기사 리스트"""
self.articles = articles
def get_statistics(self):
"""기본 통계 반환"""
return {
'total': len(self.articles), # 총 기사 수
# Counter: 각 출처별로 기사가 몇 개인지 세기
# 예: Counter({'뉴스A': 10, '뉴스B': 5})
'sources': Counter(a['source'] for a in self.articles)
}
def extract_keywords(self, top_n=10):
"""
제목에서 자주 나오는 키워드 추출
💡 동작 원리:
1. 모든 제목을 하나의 문자열로 합침
2. 정규표현식으로 단어만 추출
3. Counter로 각 단어 개수 세기
4. 가장 많이 나온 top_n개 반환
"""
# 모든 제목을 공백으로 연결
all_text = ' '.join(a['title'] for a in self.articles)
# \b\w+\b: 단어 경계 사이의 문자들 (단어만 추출)
words = re.findall(r'\b\w+\b', all_text.lower())
# most_common(10): 가장 많이 나온 10개
return Counter(words).most_common(top_n)
3. 저장소 (storage.py)
💡 이 파일의 역할: 수집한 데이터를 파일로 저장하는 “저장소” 역할이에요! JSON과 CSV 두 가지 형식을 지원해요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# storage.py
import json # JSON 파일 처리
import csv # CSV 파일 처리
class NewsStorage:
"""
뉴스 저장 클래스
💡 왜 두 가지 형식으로 저장할까요?
- JSON: 프로그램에서 다시 읽어서 처리하기 좋음
- CSV: 엑셀에서 열어서 보기 좋음
"""
def save_json(self, articles, filename='news.json'):
"""JSON 형식으로 저장"""
with open(filename, 'w', encoding='utf-8') as f:
# ensure_ascii=False: 한글이 깨지지 않게
# indent=2: 보기 좋게 들여쓰기
json.dump(articles, f, ensure_ascii=False, indent=2)
print(f"✅ JSON 저장: {filename}")
def save_csv(self, articles, filename='news.csv'):
"""CSV 형식으로 저장 (엑셀에서 열기 좋음)"""
if not articles:
print("⚠️ 저장할 기사가 없습니다")
return
with open(filename, 'w', newline='', encoding='utf-8') as f:
# DictWriter: 딕셔너리를 CSV로 쉽게 저장
writer = csv.DictWriter(f, fieldnames=articles[0].keys())
writer.writeheader() # 첫 줄에 컬럼명 작성
writer.writerows(articles) # 모든 기사 작성
print(f"✅ CSV 저장: {filename}")
4. 메인 프로그램 (main.py)
💡 이 파일의 역할: 모든 모듈을 연결해서 실행하는 “지휘자” 역할이에요!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# main.py
# 💡 다른 파일에서 클래스를 가져옵니다 (같은 폴더에 있어야 해요!)
from scraper import NewsScraper
from analyzer import NewsAnalyzer
from storage import NewsStorage
# 뉴스 소스 설정
# 💡 실제 사이트 URL과 CSS 선택자로 바꿔서 사용하세요!
NEWS_SOURCES = [
{
'name': 'Example News', # 뉴스 소스 이름
'url': 'https://news.example.com', # 뉴스 목록 페이지 URL
'article_selector': '.article', # 기사 요소의 CSS 선택자
'title_selector': '.title', # 제목의 CSS 선택자
'link_selector': 'a' # 링크의 CSS 선택자
}
# 더 많은 소스 추가 가능:
# {'name': '또다른 뉴스', 'url': '...', ...}
]
def main():
"""메인 실행 함수"""
print("📰 뉴스 스크래핑 시작...")
# 1단계: 뉴스 수집
scraper = NewsScraper(NEWS_SOURCES)
articles = scraper.scrape_all()
print(f"✅ {len(articles)}개 수집")
# 2단계: 분석 및 저장 (기사가 있을 때만)
if articles:
# 분석
analyzer = NewsAnalyzer(articles)
stats = analyzer.get_statistics()
keywords = analyzer.extract_keywords()
# 결과 출력
print(f"\n📊 통계: {stats['total']}개")
print(f"🔑 키워드: {keywords[:5]}")
# 저장
storage = NewsStorage()
storage.save_json(articles)
storage.save_csv(articles)
print("\n🎉 완료! news.json, news.csv 파일을 확인하세요!")
else:
print("⚠️ 수집된 기사가 없습니다. URL과 선택자를 확인해주세요.")
# 💡 이 파일을 직접 실행할 때만 main() 호출
# 다른 파일에서 import할 때는 실행되지 않음
if __name__ == '__main__':
main()
🏃 실행 방법
1
2
# news_scraper 폴더에서 실행
python main.py
예상 출력:
1
2
3
4
5
6
7
8
9
📰 뉴스 스크래핑 시작...
✅ 15개 수집
📊 통계: 15개
🔑 키워드: [('the', 8), ('news', 5), ('today', 4), ...]
✅ JSON 저장: news.json
✅ CSV 저장: news.csv
🎉 완료! news.json, news.csv 파일을 확인하세요!
🧪 도전 과제
프로젝트를 더 발전시켜보세요!
과제 1: 실제 뉴스 사이트 적용
요구사항:
- 실제 뉴스 사이트의 CSS 선택자 찾기
NEWS_SOURCES에 추가- 기사 수집 테스트
💡 힌트
CSS 선택자 찾는 방법:
- 브라우저에서 뉴스 사이트 열기
- F12 (개발자 도구) 열기
- 기사 제목에서 마우스 우클릭 → “검사”
- HTML 구조를 보고 클래스명, ID 확인
예시 (가상의 구조):
1
2
3
4
5
6
7
{
'name': 'Tech News',
'url': 'https://technews.example.com',
'article_selector': 'article.news-item',
'title_selector': 'h2.headline',
'link_selector': 'a.read-more'
}
과제 2: 기능 확장
아이디어:
- 중복 기사 제거 기능
- 날짜 필터링 (오늘 기사만)
- 특정 키워드 포함 기사만 수집
- Selenium으로 동적 페이지 지원
💡 중복 제거 힌트
1
2
3
4
5
6
7
8
9
def remove_duplicates(articles):
"""제목 기준 중복 제거"""
seen = set()
unique = []
for article in articles:
if article['title'] not in seen:
seen.add(article['title'])
unique.append(article)
return unique
🎯 학습 목표 4: 프로젝트 완성하고 Phase 6 마무리
축하합니다! 🎉 Phase 6을 완료했어요!
📝 Phase 6에서 배운 핵심 개념
| Day | 주제 | 핵심 코드/개념 |
|---|---|---|
| 51 | requests 기초 | requests.get(url), response.json() |
| 52 | HTTP 메서드 | GET, POST, PUT, DELETE, 헤더 |
| 53 | BeautifulSoup | soup.find(), soup.select(), CSS 선택자 |
| 54 | 스크래핑 고급 | 동적 콘텐츠, 에러 처리, robots.txt |
| 55 | API 설계 | REST 원칙, 엔드포인트 설계 |
| 56 | RESTful API | CRUD 구현, 상태 코드 |
| 57 | API 인증 | API Key, OAuth, JWT |
| 58 | 웹 크롤러 | BFS/DFS, URL 큐, 중복 방지 |
| 59 | Selenium | WebDriver, 동적 페이지, 대기 전략 |
| 60 | 미니 프로젝트 | 뉴스 스크래퍼 시스템 |
🎯 Phase 6 성취도 체크
- HTTP 요청/응답을 이해한다
- BeautifulSoup으로 HTML을 파싱할 수 있다
- CSS 선택자로 원하는 요소를 찾을 수 있다
- REST API의 기본 원칙을 안다
- 간단한 크롤러를 만들 수 있다
- Selenium으로 동적 페이지를 다룰 수 있다
📚 이전 학습
Day 59: Selenium 기초 ⭐⭐⭐ 어제는 Selenium WebDriver, 브라우저 자동화, 대기 전략을 배웠습니다!
📚 다음 학습
Day 61: NumPy 기초 ⭐⭐ 내일부터 Phase 7에서 NumPy, Pandas, Matplotlib 등 데이터 분석 기초를 배웁니다!
“늦었다고 생각할 때가 가장 빠른 때입니다. 오늘도 한 걸음 성장했어요!” 🚀
Day 60/100 Phase 6: 웹 스크래핑과 API #100DaysOfPython
