혼자 만들면서 공부하는 파이썬 커리큘럼
혼자 만들면서 공부하는 파이썬 - 예스24
“파이썬을 배워서 어디에 활용할 수 있을지 궁금했나요?”나의 신용카드 내역 분석, 우리 아파트 실거래가 시각화해보기, 맛집 지도 웹 앱 만들기 등 생활 속 15가지 파이썬 프로젝트를 직접 완
www.yes24.com
https://www.yes24.com/product/goods/142258696
4주차(25.07.21 - 07.27) Chapter 07 시가총액 분석
네이버 증권 - 시가총액
시가총액 : 네이버페이 증권
관심종목의 실시간 주가를 가장 빠르게 확인하는 곳
finance.naver.com
https://finance.naver.com/sise/sise_market_sum.naver
기본미션
▶ Ch.07(07-3) 시가 총액 데이터 시각화 하기
추가미션
▶ Ch.07 미니 프로젝트(p.246) ETF 데이터 시각화 하기
네이버 증권 - ETF
네이버페이 증권
국내 해외 증시 지수, 시장지표, 뉴스, 증권사 리서치 등 제공
finance.naver.com
https://finance.naver.com/sise/etf.naver
[ETF 데이터 크롤링]
from pathlib import Path
from playwright.sync_api import Page
from step_1_1 import OUT_DIR # 이전에 작성한 모듈을 불러옵니다.
from step_1_2 import run_playwright
from step_1_4 import table_to_dataframe
def goto_market_etf(page: Page):
page.goto("https://finance.naver.com")
page.get_by_role("link", name="국내증시").click()
page.get_by_role("link", name="ETF").click()
def parse_table_etf(page: Page) -> tuple[list, list]:
page.wait_for_selector("table.type_1.type_etf tbody#etfItemTable tr")
tag_table = page.locator("table", has_text="ETF 주요시세정보")
# 헤더 추출
tag_header_row = tag_table.locator("tr").first
tag_thead = tag_header_row.locator("th")
header = tag_thead.all_inner_texts()
# 데이터 행 추출
tag_tbody = tag_table.locator("tbody > tr")
body = []
for tag_tr in tag_tbody.all():
tag_tds = tag_tr.locator("td")
# 각 셀의 텍스트를 inner_text()로 하나씩 추출
texts = [td.inner_text().strip() for td in tag_tds.all()]
if not texts or all(t == "" for t in texts):
continue
# 전일비 이미지 alt 처리
tag_img = tag_tr.locator("td > img")
if tag_img.count() > 0:
alt_text = tag_img.get_attribute("alt")
texts[2] = f"{alt_text} {texts[2]}"
# 누락 보정 (필요 시)
if len(texts) < len(header):
texts += [''] * (len(header) - len(texts))
body.append(texts)
# 디버깅
if body and len(header) != len(body[0]):
print(f"[⚠️ Warning] 헤더 개수({len(header)}) ≠ 데이터 열 개수({len(body[0])})")
print("헤더:", header)
print("예시 데이터 행:", body[0])
return header, body
if __name__ == "__main__":
play, browser, page = run_playwright(slow_mo=1000)
goto_market_etf(page) # ETF 페이지로 이동
header, body = parse_table_etf(page) # ETF 데이터 수집
df_raw = table_to_dataframe(header, body) # 데이터 정제 및 DataFrame 객체 생성
df_raw.to_csv(OUT_DIR / f"{Path(__file__).stem}.csv", index=False) # CSV로 저장
browser.close()
play.stop()
[ETF 상위 50% 트리맵 만들기 전체 코드]
from pathlib import Path
from playwright.sync_api import sync_playwright, Page
import pandas as pd
import plotly.express as px
# 출력 경로
OUT_DIR = Path(__file__).parent / "output"
OUT_DIR.mkdir(exist_ok=True)
CSV_PATH = OUT_DIR / "etf_data.csv"
TREEMAP_DATA_PATH = OUT_DIR / "etf_top_50pct.csv"
TREEMAP_IMG_PATH = OUT_DIR / "etf_treemap_50pct.png"
def run_playwright(slow_mo: int = 1000):
play = sync_playwright().start()
browser = play.chromium.launch(headless=False, slow_mo=slow_mo)
page = browser.new_page()
return play, browser, page
def goto_market_etf(page: Page):
page.goto("https://finance.naver.com")
page.get_by_role("link", name="국내증시").click()
page.get_by_role("link", name="ETF").click()
def parse_table_etf(page: Page) -> tuple[list[str], list[list[str]]]:
page.goto("https://finance.naver.com/sise/etf.naver")
page.wait_for_selector("table.type_1.type_etf tbody#etfItemTable tr")
tag_table = page.locator("table", has_text="ETF 주요시세정보")
tag_header_row = tag_table.locator("tr").first
header = tag_header_row.locator("th").all_inner_texts()
tag_tbody = tag_table.locator("tbody > tr")
body = []
for tag_tr in tag_tbody.all():
tag_tds = tag_tr.locator("td")
if tag_tds.count() == 0:
continue
texts = [td.inner_text().strip() for td in tag_tds.all()]
if not texts or all(t == "" for t in texts):
continue
tag_img = tag_tr.locator("td > img")
if tag_img.count() > 0:
alt_text = tag_img.get_attribute("alt")
texts[2] = f"{alt_text} {texts[2]}"
if len(texts) < len(header):
texts += [''] * (len(header) - len(texts))
body.append(texts)
return header, body
def table_to_dataframe(header: list[str], body: list[list[str]]) -> pd.DataFrame:
df = pd.DataFrame(body, columns=header)
return df
def top_ETF_company(df_raw: pd.DataFrame, prop: float) -> pd.DataFrame:
df_raw = df_raw.copy()
df_raw["시가총액"] = df_raw["시가총액(억)"].str.replace(",", "").replace("", "0").astype(int)
df_raw["조단위"] = df_raw["시가총액"] / 10_000 # 억 → 조
df_raw = df_raw.sort_values("시가총액", ascending=False)
df_raw["누적비율"] = df_raw["시가총액"].cumsum() / df_raw["시가총액"].sum()
df_sliced = df_raw[df_raw["누적비율"] <= prop]
return df_sliced[["종목명", "시가총액", "조단위", "누적비율"]]
def draw_treemap(df_top: pd.DataFrame):
fig = px.treemap(
df_top,
path=["종목명"],
values="조단위",
)
fig.update_traces(
marker=dict(
cornerradius=5,
colorscale="Plasma",
pad=dict(t=10, r=10, b=10, l=10),
),
texttemplate="<b>%{label}</b><br>%{value:,.2f}조원",
textfont_size=30,
)
fig.update_layout(margin=dict(t=0, r=0, b=0, l=0))
fig.write_image(str(TREEMAP_IMG_PATH), width=1600, height=900, scale=2)
print(f"✅ 트리맵 이미지 저장 완료: {TREEMAP_IMG_PATH}")
if __name__ == "__main__":
play, browser, page = run_playwright(slow_mo=500)
goto_market_etf(page)
header, body = parse_table_etf(page)
df_raw = table_to_dataframe(header, body)
df_raw.to_csv(CSV_PATH, index=False, encoding="utf-8-sig")
print(f"✅ CSV 저장 완료: {CSV_PATH}")
# 트리맵 데이터 준비 및 시각화
df_top = top_ETF_company(df_raw, 0.5)
df_top.to_csv(TREEMAP_DATA_PATH, index=False, encoding="utf-8-sig")
print(f"✅ 상위 50% ETF CSV 저장 완료: {TREEMAP_DATA_PATH}")
draw_treemap(df_top)
browser.close()
play.stop()
'혼공학습단(한빛미디어)' 카테고리의 다른 글
[혼공단14기] 혼자 만들면서 공부하는 파이썬 - 6주차 (3) | 2025.08.11 |
---|---|
[혼공단14기] 혼자 만들면서 공부하는 파이썬 - 5주차 (4) | 2025.08.04 |
[혼공단14기] 혼자 만들면서 공부하는 파이썬 - 3주차 (2) | 2025.07.19 |
[혼공단14기] 혼자 만들면서 공부하는 파이썬 - 2주차 (2) | 2025.07.12 |
[혼공단14기] 혼자 만들면서 공부하는 파이썬 - 1주차 (1) | 2025.07.05 |