Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료
Python 프로그래밍Unit 11: 나의 첫 웹 앱 만들기Topic 3: 영화 & 게임 정보 실시간으로 보여주기 🎬🎮

Topic 3: 실시간 엔터테인먼트 정보 수집하기! 🎬🎮

목표: Unit 10에서 배운 웹 스크래핑을 사용해서 실시간 영화, 게임, 동영상 정보를 가져오는 웹앱을 만들어요!

🎯 학습 목표

  • Unit 10 복습: BeautifulSoup과 requests 다시 써보기
  • 실시간 엔터테인먼트: 영화 랭킹, 게임 정보, 인기 동영상 수집하기
  • 에러 처리: 인터넷이 안 될 때도 우아하게 처리하기
  • 멋진 디자인: 엔터테인먼트 앱처럼 예쁘게 만들기! 🎨

🎬 Unit 10 복습: 스크래핑 기초

Unit 10에서 배운 걸 빠르게 복습해봐요!

import requests from bs4 import BeautifulSoup # 1. 웹페이지 가져오기 (Unit 10 Topic 4) response = requests.get('https://example.com') # 2. HTML 파싱하기 (Unit 10 Topic 5) soup = BeautifulSoup(response.text, 'html.parser') # 3. 원하는 데이터 뽑기 title = soup.find('h1').text

이제 이걸 Flask와 합쳐서 살아있는 엔터테인먼트 웹앱을 만들어봐요!

🚀 첫 번째 작품: 실시간 영화 랭킹 웹앱

먼저 간단한 것부터! 인기 영화 정보를 실시간으로 보여주는 웹앱이에요.

Step 1: 설치하기

pip install flask requests beautifulsoup4

Step 2: 영화 정보 스크래퍼 만들기

app.py 파일을 만들어요:

from flask import Flask, render_template import requests from bs4 import BeautifulSoup import random app = Flask(__name__) def get_movie_rankings(): """인기 영화 정보를 가져오는 함수""" try: # 실습용 영화 데이터 (실제로는 영화 사이트에서 크롤링) movies = [ { "title": "스파이더맨: 어크로스 더 스파이더 버스", "rating": "9.2", "poster": "🕷️", "genre": "액션, 애니메이션", "year": "2023", "description": "멀티버스를 넘나드는 스파이더맨의 모험" }, { "title": "가디언즈 오브 갤럭시 3", "rating": "8.8", "poster": "🚀", "genre": "액션, SF", "year": "2023", "description": "갤럭시 수호대의 마지막 모험" }, { "title": "존 윅 4", "rating": "8.5", "poster": "🔫", "genre": "액션, 스릴러", "year": "2023", "description": "전설적인 킬러의 복수극" }, { "title": "더 슈퍼 마리오 브라더스 무비", "rating": "8.2", "poster": "🍄", "genre": "애니메이션, 모험", "year": "2023", "description": "마리오의 버섯왕국 대모험" }, { "title": "오펜하이머", "rating": "9.0", "poster": "💣", "genre": "드라마, 역사", "year": "2023", "description": "원자폭탄 아버지의 이야기" } ] return random.sample(movies, 3) # 랜덤하게 3개 선택 except Exception as e: return [{"title": "영화 정보를 불러올 수 없습니다", "rating": "0.0", "poster": "🎬", "genre": "정보 없음", "year": "----", "description": "나중에 다시 시도해주세요"}] @app.route('/') def home(): movies = get_movie_rankings() return render_template('movies.html', movies=movies) if __name__ == '__main__': app.run(debug=True)

Step 3: 예쁜 영화 템플릿

templates/movies.html 파일을 만들어요:

<!DOCTYPE html> <html> <head> <title>인기 영화 랭킹 🎬</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif; background: linear-gradient(135deg, #ff6b6b 0%, #4ecdc4 50%, #45b7d1 100%); min-height: 100vh; padding: 20px; color: #333; } .header { text-align: center; color: white; margin-bottom: 40px; } .title { font-size: 3em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); animation: bounce 2s ease-in-out infinite; } @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-10px); } 60% { transform: translateY(-5px); } } .subtitle { font-size: 1.3em; opacity: 0.9; } .movies-container { max-width: 1200px; margin: 0 auto; display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 30px; } .movie-card { background: white; border-radius: 20px; padding: 30px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); transition: all 0.3s ease; position: relative; overflow: hidden; } .movie-card:hover { transform: translateY(-10px); box-shadow: 0 30px 60px rgba(0,0,0,0.2); } .movie-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 5px; background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1); } .movie-poster { font-size: 4em; text-align: center; margin-bottom: 20px; animation: float 3s ease-in-out infinite; } @keyframes float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-15px); } } .movie-title { font-size: 1.5em; font-weight: bold; color: #333; margin-bottom: 15px; text-align: center; line-height: 1.3; } .movie-info { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px; } .info-item { background: #f8f9fa; padding: 12px; border-radius: 10px; text-align: center; } .info-label { font-size: 0.9em; color: #666; margin-bottom: 5px; } .info-value { font-weight: bold; color: #333; } .rating { color: #ff6b6b; font-size: 1.2em; } .year { color: #4ecdc4; font-size: 1.1em; } .genre { grid-column: 1 / -1; background: linear-gradient(45deg, #45b7d1, #4ecdc4); color: white; } .description { font-size: 0.95em; color: #666; line-height: 1.5; text-align: center; font-style: italic; } .refresh-btn { position: fixed; bottom: 30px; right: 30px; width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(45deg, #ff6b6b, #4ecdc4); color: white; border: none; font-size: 24px; cursor: pointer; box-shadow: 0 10px 20px rgba(0,0,0,0.2); transition: all 0.3s ease; } .refresh-btn:hover { transform: scale(1.1) rotate(180deg); box-shadow: 0 15px 30px rgba(0,0,0,0.3); } .sparkle { position: absolute; color: white; font-size: 2em; animation: sparkle 3s ease-in-out infinite; pointer-events: none; } @keyframes sparkle { 0%, 100% { opacity: 0; transform: scale(0) rotate(0deg); } 50% { opacity: 1; transform: scale(1) rotate(180deg); } } @media (max-width: 768px) { .title { font-size: 2.2em; } .movies-container { grid-template-columns: 1fr; } .movie-info { grid-template-columns: 1fr; } } </style> </head> <body> <div class="sparkle" style="top: 10%; left: 10%; animation-delay: 0s;">🎬</div> <div class="sparkle" style="top: 20%; right: 15%; animation-delay: 1s;">🍿</div> <div class="sparkle" style="bottom: 20%; left: 20%; animation-delay: 2s;">🎭</div> <div class="sparkle" style="bottom: 30%; right: 10%; animation-delay: 1.5s;">🎪</div> <div class="header"> <h1 class="title">🎬 인기 영화 랭킹</h1> <p class="subtitle">Unit 10 스크래핑으로 가져온 실시간 영화 정보!</p> </div> <div class="movies-container"> {% for movie in movies %} <div class="movie-card"> <div class="movie-poster">{{ movie.poster }}</div> <h2 class="movie-title">{{ movie.title }}</h2> <div class="movie-info"> <div class="info-item"> <div class="info-label">평점</div> <div class="info-value rating">⭐ {{ movie.rating }}</div> </div> <div class="info-item"> <div class="info-label">연도</div> <div class="info-value year">📅 {{ movie.year }}</div> </div> <div class="info-item genre"> <div class="info-label">장르</div> <div class="info-value">🎭 {{ movie.genre }}</div> </div> </div> <p class="description">{{ movie.description }}</p> </div> {% endfor %} </div> <button class="refresh-btn" onclick="location.reload()" title="새로운 영화 보기"> 🔄 </button> </body> </html>

🎮 메인 작품: 실시간 게임 정보 대시보드

이제 Unit 10의 스크래핑 지식을 사용해서 인기 게임 정보를 보여주는 앱을 만들어봐요!

게임 정보 앱 코드 (app.py에 추가)

# 기존 코드 아래에 이것들을 추가하세요! import random from datetime import datetime def get_game_rankings(): """인기 게임 정보를 가져오는 함수 (실습용 가상 데이터)""" try: # 실제로는 게임 사이트에서 크롤링하지만, 실습에서는 랜덤 데이터 사용 games = [ { "title": "젤다의 전설: 왕국의 눈물", "platform": "Nintendo Switch", "icon": "🗡️", "rating": "9.8", "genre": "액션/어드벤처", "players": random.randint(50000, 200000), "price": "59,800원", "bg": "linear-gradient(135deg, #74b9ff, #0984e3)" }, { "title": "발더스 게이트 3", "platform": "PC, PS5", "icon": "🐉", "rating": "9.5", "genre": "RPG", "players": random.randint(30000, 150000), "price": "69,800원", "bg": "linear-gradient(135deg, #a29bfe, #6c5ce7)" }, { "title": "스파이더맨 2", "platform": "PS5", "icon": "🕷️", "rating": "9.2", "genre": "액션", "players": random.randint(40000, 120000), "price": "79,800원", "bg": "linear-gradient(135deg, #fd79a8, #e84393)" }, { "title": "슈퍼 마리오 브라더스 원더", "platform": "Nintendo Switch", "icon": "🍄", "rating": "9.0", "genre": "플랫포머", "players": random.randint(60000, 180000), "price": "59,800원", "bg": "linear-gradient(135deg, #fdcb6e, #e17055)" }, { "title": "포르자 호라이즌 5", "platform": "Xbox, PC", "icon": "🏎️", "rating": "8.8", "genre": "레이싱", "players": random.randint(25000, 100000), "price": "Game Pass", "bg": "linear-gradient(135deg, #00b894, #00cec9)" } ] selected_game = random.choice(games) selected_game["update_time"] = datetime.now().strftime("%H:%M") return selected_game except Exception as e: return { "title": "게임 정보를 불러올 수 없습니다", "platform": "알 수 없음", "icon": "❓", "rating": "0.0", "genre": "정보 없음", "players": 0, "price": "---", "bg": "linear-gradient(135deg, #636e72, #2d3436)", "update_time": "---" } @app.route('/games', methods=['GET', 'POST']) def games(): game_data = get_game_rankings() return render_template('games.html', game=game_data)

게임 정보 템플릿

templates/games.html 파일을 만들어요:

<!DOCTYPE html> <html> <head> <title>게임 정보 대시보드 🎮</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif; background: {{ game.bg }}; min-height: 100vh; padding: 20px; color: white; } .game-container { max-width: 500px; margin: 0 auto; background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(20px); border-radius: 30px; padding: 40px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); animation: slideUp 0.8s ease; } @keyframes slideUp { from { transform: translateY(50px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .search-box { margin-bottom: 30px; } .search-input { width: 100%; padding: 15px; border: none; border-radius: 15px; background: rgba(255, 255, 255, 0.2); color: white; font-size: 16px; backdrop-filter: blur(10px); } .search-input::placeholder { color: rgba(255, 255, 255, 0.7); } .search-btn { width: 100%; margin-top: 10px; padding: 12px; border: none; border-radius: 15px; background: rgba(255, 255, 255, 0.3); color: white; font-size: 16px; cursor: pointer; transition: all 0.3s; } .search-btn:hover { background: rgba(255, 255, 255, 0.4); transform: translateY(-2px); } .weather-main { text-align: center; margin-bottom: 30px; } .city-name { font-size: 1.5em; margin-bottom: 10px; opacity: 0.9; } .weather-icon { font-size: 5em; margin: 20px 0; animation: float 3s ease-in-out infinite; } @keyframes float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-20px); } } .temperature { font-size: 4em; font-weight: 300; margin-bottom: 10px; } .condition { font-size: 1.3em; opacity: 0.9; margin-bottom: 10px; } .update-time { font-size: 0.9em; opacity: 0.7; } .weather-details { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 30px; } .detail-item { background: rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 20px; text-align: center; backdrop-filter: blur(10px); } .detail-icon { font-size: 1.5em; margin-bottom: 10px; } .detail-label { font-size: 0.9em; opacity: 0.8; margin-bottom: 5px; } .detail-value { font-size: 1.2em; font-weight: bold; } .refresh-btn { width: 100%; margin-top: 20px; padding: 15px; border: none; border-radius: 15px; background: rgba(255, 255, 255, 0.2); color: white; font-size: 16px; cursor: pointer; transition: all 0.3s; backdrop-filter: blur(10px); } .refresh-btn:hover { background: rgba(255, 255, 255, 0.3); transform: translateY(-2px); } .credits { text-align: center; margin-top: 20px; opacity: 0.6; font-size: 0.8em; } </style> </head> <body> <div class="game-container"> <div class="game-header"> <h2>🎮 오늘의 추천 게임</h2> </div> <div class="game-main"> <div class="game-icon">{{ game.icon }}</div> <div class="game-title">{{ game.title }}</div> <div class="game-platform">{{ game.platform }}</div> <div class="update-time">{{ game.update_time }} 업데이트</div> </div> <div class="game-details"> <div class="detail-item"> <div class="detail-icon">⭐</div> <div class="detail-label">평점</div> <div class="detail-value">{{ game.rating }}/10</div> </div> <div class="detail-item"> <div class="detail-icon">🏆</div> <div class="detail-label">장르</div> <div class="detail-value">{{ game.genre }}</div> </div> <div class="detail-item"> <div class="detail-icon">👥</div> <div class="detail-label">동시 접속자</div> <div class="detail-value">{{ game.players:,}}명</div> </div> <div class="detail-item"> <div class="detail-icon">💰</div> <div class="detail-label">가격</div> <div class="detail-value">{{ game.price }}</div> </div> </div> <button class="refresh-btn" onclick="location.reload()"> 🔄 다른 게임 보기 </button> <div class="credits"> Unit 10의 스크래핑 기술로 만든 게임 정보 앱! 🎉 </div> </div> </body> </html>

💡 Python 웹 스크래핑 핵심 개념

Python에서 웹 데이터를 가져오는 주요 방법들:

기능Python 구현
CSS 선택자로 요소 찾기soup.find(class_='class')
요소의 텍스트 가져오기element.text
웹페이지 요청하기requests.get('url').text
JSON 데이터 처리json.loads(data)

🎯 도전 과제: 더 멋지게!

1. 실제 게임 API 연결하기

# Steam API 사용 예시 import requests def get_real_game_data(): # Steam의 인기 게임 API (가상 예시) url = "https://store.steampowered.com/api/featured" response = requests.get(url) data = response.json() return data

2. YouTube 트렌딩 게임 동영상 추가하기

def get_trending_gaming_videos(): # YouTube Gaming 트렌딩 동영상 정보 trending_videos = [] for i in range(5): # 가상 데이터 생성 trending_videos.append({ 'title': f'인기 게임 플레이 {i+1}', 'views': random.randint(10000, 1000000), 'channel': f'게이머 {i+1}' }) return trending_videos

3. 실시간 Twitch 스트리밍 정보

def get_twitch_streams(): # Twitch API로 인기 게임 스트림 정보 가져오기 streams = [] for i in range(3): streams.append({ 'game': f'인기 게임 {i+1}', 'streamer': f'스트리머 {i+1}', 'viewers': random.randint(1000, 50000) }) return streams

💡 퀴즈: 스크래핑 이해도 체크

Q1. BeautifulSoup에서 클래스로 요소를 찾으려면?

  1. soup.find('class_name')
  2. soup.find(class_='class_name')
  3. soup.find('.class_name')

💡 정답 확인

정답: 2번

soup.find(class_='class_name')을 사용해요!

element = soup.find('div', class_='weather-info')

Q2. requests로 웹페이지를 가져올 때 타임아웃을 설정하려면?

  1. requests.get(url, time=5)
  2. requests.get(url, timeout=5)
  3. requests.get(url, wait=5)

💡 정답 확인

정답: 2번

timeout 매개변수를 사용해요!

response = requests.get(url, timeout=5)

⚠️ 스크래핑 시 주의사항

  1. robots.txt 확인: 크롤링하기 전에 사이트의 robots.txt를 확인하세요
  2. 요청 간격: 너무 자주 요청하지 마세요 (1초 이상 간격 권장)
  3. User-Agent 설정: 브라우저처럼 보이도록 설정하세요
  4. 에러 처리: 네트워크 오류나 파싱 오류를 처리하세요
  5. 저작권: 크롤링한 데이터의 저작권을 확인하세요

안전한 스크래핑 예시

def safe_scraping(url): try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } response = requests.get(url, headers=headers, timeout=5) if response.status_code != 200: raise Exception(f"HTTP 오류: {response.status_code}") soup = BeautifulSoup(response.text, 'html.parser') return soup except requests.exceptions.Timeout: print("요청 시간이 초과되었습니다.") except requests.exceptions.ConnectionError: print("인터넷 연결을 확인해주세요.") except Exception as e: print(f"오류가 발생했습니다: {str(e)}") return None

✅ Topic 3 마스터 체크리스트

🚀 다음 단계

와! Unit 10에서 배운 스크래핑을 엔터테인먼트 웹앱으로 만들었네요!

다음 Topic에서는:

  • 여러 기능을 합쳐서 엔터테인먼트 종합 대시보드 만들기
  • 영화, 게임, 음악, YouTube 트렌딩을 한 페이지에!
  • 개인 맞춤형 엔터테인먼트 허브 완성하기

준비되셨나요? 마지막 대작을 만들러 가봐요! 🚀

Last updated on