파이썬 실전: 데이터 크롤링부터 웹 앱 배포까지
문법을 배웠다면 이제 실전에 적용할 차례입니다. 웹상의 데이터를 수집하고, 이를 지도에 표시하며, 누구나 접속할 수 있는 웹 페이지로 만드는 전 과정을 알아보겠습니다.
1. 데이터 수집의 준비: 파일 및 라이브러리 관리
import zipfile
with zipfile.ZipFile("./이름_생년_성별_10000.zip", 'r') as f:
f.extractall("./mydata")
# - 파일 목록보기
# os.listdir("./mydata")
#
# - 파일 옮기기
# import shutil
# shutil.move("./'드디어 SKT 유심 교체했어요'…한달이나 참았던 이유-2025-05-23 064009.txt", "./a/")
데이터를 다루기 전, 파일을 압축 해제하고 관리하는 방법부터 시작합니다.
- 압축 풀기: zipfile 모듈을 사용하여 대량의 데이터를 한 번에 추출합니다.
- OS 활용: os.listdir()로 파일 목록을 확인하거나 shutil.move()로 파일을 정리하며 수집된 데이터를 관리합니다.
import os #운영체제(OS: Operating System) 관련 기능을 사용할 수 있게 해주는 표준 라이브러리를 불러오는 거예요.
if not os.path.isdir("./lyrics"):
os.mkdir("./lyrics")
f = open(f"./lyrics/{artist}-{title}.txt", 'w', encoding='utf-8') #utf -8 전세계의 언어를 표현하는 확장팩팩 ///인코딩 디코딩 파일 변환방식임
f.write(lyrics.strip()) #f.write()만 하면, 데이터가 **메모리(버퍼)**에만 잠깐 저장돼 있을 수 있어요
f.close() #f.close()를 해야 진짜 파일에 저장되고 → 시스템이 파일을 안전하게 닫고 정리해요.
2. 웹 크롤링의 핵심 원리
웹사이트는 우리가 배운 딕셔너리와 유사한 JSON 형태나 태그 기반의 HTML 구조로 이루어져 있습니다.
- HTTP 통신 (Request & Response):
- requests 패키지: 서버와 데이터를 주고받는 도구입니다.
- GET: URL에 데이터가 노출되는 방식. 주소를 잘 활용하면 페이지 이동 크롤링이 쉽습니다.
- POST: 데이터를 Payload(딕셔너리 형태)에 숨겨서 요청하는 방식. 보안이나 복잡한 조건이 필요할 때 씁니다.
- 헤더(Headers) 설정: 서버가 접근을 거부(<Response [406]>)할 때, 브라우저인 척 위장하여 우회하기 위해 사용합니다.
- Status Code: 응답이 성공(200)했는지 확인합니다. <Response [406]>이 뜨면 서버가 거부한 것이므로, **헤더(User-Agent)**를 설정해 브라우저인 척 위장(우회)해야 합니다.
- .json(): 서버에서 받은 JSON 텍스트를 파이썬의 딕셔너리 형태로 자동 변환해 줍니다.
- result = requests.post('https://www.starbucks.co.kr/store/getStore.do?r=4B5E8X6R0Q', data=payload).json()
- 데이터 파싱 (BeautifulSoup):
- HTML 구조에서 원하는 데이터를 추출할 때 bs4 라이브러리를 사용합니다.
- .find(), .find_all()을 이용해 태그, 클래스, ID로 원하는 요소를 정확히 찾아낼 수 있습니다.
- Tip: 우클릭이나 복사가 금지된 사이트는 **F12(개발자 도구) -> F1(설정) -> Debugger 섹션의 'Disable JavaScript'**를 체크하면 분석이 수월해집니다.
pip install bs4
from bs4 import BeautifulSoup
bs = BeautifulSoup(r.text)
bs.find("div", class_="song_name")
.replace, .text를 활용하여 원하는 데이터를 출력할수있다(참고** BeautifulSoup의 .text는 태크를 제외하고 문자만 출력해준다)
- String Methods: .split(), .strip(), .replace() 등 지저분한 데이터를 깨끗하게 만드는 법.
정규식도 사용가능
3. 실전 프로젝트: 멜론 가사 수집 & 스타벅스 지도
- 데이터 저장: f.open(), f.write(), f.close()를 통해 수집한 텍스트를 파일로 저장하며, utf-8 인코딩을 사용하여 한글 깨짐을 방지합니다.
- 지도 시각화 (Folium):
- folium.Map으로 지도를 생성하고, folium.Marker로 수집한 위경도 좌표에 마커를 찍습니다.
- popup(클릭 시), tooltip(마우스 오버 시) 옵션으로 정보를 표시합니다.
-
- 객체 저장 (Pickle): 수집한 딕셔너리 데이터를 원본 그대로(바이너리 형식) 저장하고 나중에 다시 불러올(load) 때 사용합니다.
.add_to(seoul)
- folium.Marker(...)로 만든 마커를 seoul이라는 지도 객체에 **추가(add)**합니다.
seoul.save("./starbucks.html")
그렇게 만들어진 seoul을 파일로 저장
import pickle
with open("./starbucks.pkl", 'wb') as f:
pickle.dump(result, f)
result
pickle은 파이썬에서 파일은 원본 그대로(직렬화) 저장하는 모듈이다
with open()은 파일을 열고 자동으로 닫아주는 방식이다 "./starbucks.pkl" 이라는 파일을 만들어서 w(쓰고),b(바이너리)로 저장한것을 f지정함 (*** pickle은 데이터를 이진(binary) 형식으로 저장하기 때문에 wb 모드를 써야 한다***)
- dump는 "데이터를 저장해라"는 뜻이에요.
- result라는 변수에 담긴 데이터를 파일 객체 f에 저장하는 거예요.
- 즉, result를 starbucks.pkl 파일에 저장하겠다는 뜻이에요.
import folium.map
seoul = folium.Map(location=[37.55, 126.98], zoom_start=12)
seoul = folium.Map(location=[37.55, 126.98], zoom_start=12)
지도 시작위치를 지정해서 생성하고 seoul이라는 변수에 저장
with open("./starbucks.pkl", 'rb') as f:
result = pickle.load(f)
아까 저장했던 피클을 복원해주는것
for store in result['list']:
folium.Marker([store['lat'],
store['lot']],
popup=store['s_name'],
tooltip=store['s_name'],
icon=folium.Icon(color='blue', icon='info-sign'
)).add_to(seoul)
리스트에 저장된 딕셔너리(매장)위치 정보를 지도에 나타낸다
map_data = st_folium(seoul, width=1000, height=1000)
st_folium을 통해서 서울 스타벅스 위치정보를 추가한 seoul이라는 지도를 앱에 표시하겠다.
for store in result['list']:
folium.Marker([store['lat'], store['lot']], popup=store['s_name']).add_to(seoul)
| store['lat'] |
위도 (latitude) |
| store['lot'] |
경도 (longitude) — 타이핑 실수로 보이며 보통 lng 또는 lon이어야 정확함 |
| popup=store['s_name'] |
마커 클릭 시 뜨는 말풍선에 표시할 텍스트 (가게 이름 등) |
4. 파이썬 웹 앱 만들기 (Streamlit)
파이썬 코드만으로 빠르게 웹 페이지를 구축하는 도구입니다. streamlit run 파일명.py로 실행합니다.
- 주요 기능: st.title(), st.write(), st.slider(), st_folium() 등을 활용해 앞서 만든 지도를 웹에 띄울 수 있습니다.( from streamlit_folium import st_folium
- 웹 서버도 자동 streamlit run 파일명.py을 실행하면 만든 페이지를 열어볼수있다
5. 프로그래밍 심화 이론 (고급 기능 & 자료구조)
실전 코드를 더 유연하게 만드는 개념들입니다.
scores = [90, 85, 95, 88]
total_score = calculate_sum(*scores)
- 가변 인자 (*args, **kwargs):
- *args: 정해지지 않은 개수의 인자를 튜플로 받습니다. 일반 매개변수 뒤에 위치합니다.
- **kwargs: 여분의 키워드 인자들을 딕셔너리로 받습니다. 매개변수 목록 가장 마지막에 위치합니다.
- 언패킹: `*`는 시퀀스를 위치 인자로, `**`는 딕셔너리를 키워드 인자로 풀어 전달합니다. (사용하지 않는 값은 언더바 _를 사용하여 버립니다.)
- 클래스(Class)와 자료구조:
- 클래스: 객체를 만들기 위한 틀입니다. 메서드의 첫 파라미터는 자기 자신을 가리키는 self를 씁니다.
- 스택(Stack): 후입선출(LIFO), 큐(Queue): 선입선출(FIFO).
- 링크드 리스트(Linked List): 각 노드가 데이터와 다음 노드의 주소를 가지고 연결된 구조입니다.
# Node class
class Node:
def __init__(self, data):
self.data = data
self.next = None
# Linked List class
class LinkedList:
def __init__(self):
self.head = None
# Append a new node to the end of the list
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last_node = self.head
while last_node.next:
last_node = last_node.next
last_node.next = new_node
# Prepend a new node to the beginning of the list
def prepend(self, data):
new_node = Node(data)
new_node.next = self.head
self.head = new_node
# Delete a node with the given data
def delete(self, data):
if not self.head:
return
if self.head.data == data:
self.head = self.head.next
return
current_node = self.head
while current_node.next and current_node.next.data != data:
current_node = current_node.next
if current_node.next:
current_node.next = current_node.next.next
# Display the linked list
def display(self):
elements = []
current_node = self.head
while current_node:
elements.append(str(current_node.data))
current_node = current_node.next
return " -> ".join(elements) if elements else "비어 있음"
from urllib.parse import urlparse, urlencode, quote
import requests
from bs4 import BeautifulSoup
import re
import os
import pathlib
p = re.compile("playSong\(\'([0-9]+)\',([0-9]+)\)")
def my_request(url, method='get'):
head = {'user-agent'
: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0'}
if method == "get":
return requests.get(url, headers=head)
def search_id(title):
payload = {
'searchGnbYn' : ['Y'],
'kkoSpl' : ['Y'],
'mwkLogType' : ['T']
}
payload['q'] = [title]
# url 주소 생성
url = host + urlencode(payload, doseq=True)
# return url
r = my_request(url)
text = BeautifulSoup(r.text).find("div", class_="tb_list d_song_list songTypeOne").find_all("tr")[1].find("button","btn_icon play" )['onclick']
return p.findall(text)[0][-1]
def save_lyrics(songid, path='lyrics'):
r = requests.get(url)
head = {'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0'}
r = requests.get(url, headers=head)
bs = BeautifulSoup(r.text)
lyrics = BeautifulSoup(str(bs.find("div", id="d_video_summary")).replace("<br/>", "\n")).text
# if not os.path.isdir(f"./lyrics/{path}"):
# os.mkdir(f"./lyrics/{path}")
# else:
# pass
pathlib.Path(f"./lyrics/{path}").mkdir(parents=True, exist_ok=True)
title = bs.find("div", class_="song_name").text.replace("곡명", "").strip()
artist = bs.find("div", class_="artist").text.strip()
f = open(f"./lyrics/{path}/{artist}-{title}.txt", 'w', encoding='utf-8')
f.write(lyrics.strip())
f.close()
분야별 주요 라이브러리 요약
- 데이터 과학: Pandas(데이터 분석), NumPy(수치 계산), SciPy(과학 계산).
- 시각화: Matplotlib, Seaborn.
- 인공지능: TensorFlow, PyTorch, Keras, Scikit-learn.
- 웹 개발: Django, Flask, FastAPI.
- GUI/자동화: PyQt, Tkinter, 시스템 스크립팅.