Programming/Python

[혼만파] 3주차_혼자 만들면서 공부하는 파이썬

호호호호히히히히 2025. 7. 18. 14:58
728x90
반응형

 

3주차부터 드디어 정상 진도? 의 궤도에 진입하였습니다.

근데 이제 진도가 +4 챕터가 되어버린...

방학 반납하고 복습 예정입니다. 5주차가 Chapter 13인 것을 보니 예습도 예정이어야 되겠군요...

 

ModuleNotFoundError: No module named 'playwright'

오류가 나는 경우 playwright부터 설치해야합니다.

pip install playwright
python -m playwright install

 

 

ModuleNotFoundError: No module named 'pandas'

오류가 나는 경우 pandas도 설치 해야합니다.

pip install pandas

 

Chapter 07. 종목별 시가총액 데이터 수집하기

 

페이지 열기

from playwright.sync_api import Page  # Playwright의 동기 API에서 Page 객체 임포트

from step_1_2 import run_playwright   # playwright를 실행시키는 함수(직접 구현) 임포트
from step_1_3 import goto_market_cap  # 특정 페이지(시가총액 페이지)로 이동하는 함수 임포트

def fetch_total_page(page: Page) -> int:
    # "페이지 네비게이션"이라는 텍스트를 가진 table 태그를 찾음
    table = page.locator("table", has_text="페이지 네비게이션")
    # 해당 table에서 tbody > tr > td 중 마지막 td 태그를 찾음
    td = table.locator("tbody > tr > td").last
    # 마지막 td 안의 <a> 태그의 href 속성값 추출 (예: '?page=15')
    href = td.locator("a").get_attribute("href")
    # href에서 '=' 이후 숫자만 추출해서 int로 변환 (총 페이지 수)
    return int(href.split("=")[-1])

if __name__ == "__main__":
    # playwright를 실행(브라우저, 페이지 객체 등 반환)
    play, browser, page = run_playwright(slow_mo=1000)
    # 시가총액 페이지로 이동
    goto_market_cap(page)
    # 총 페이지 수 추출
    total_page = fetch_total_page(page)
    print(f"{total_page=}")

    # 브라우저 및 playwright 세션 종료
    browser.close()
    play.stop()

 

시가 총액 페이지 까지 정상적으로 이동하는 지 확인

예제 코드와 달리 네이버 증권 내 시가총액 text링크가 두개라 아래와 같이 약간 수정이 필요합니다.

import json
from pathlib import Path

from playwright.sync_api import Page

from step_1_1 import OUT_DIR  # 이전에 작성한 모듈을 불러옵니다.
from step_1_2 import run_playwright

OUT_1_3 = OUT_DIR / f"{Path(__file__).stem}.json"

def goto_market_cap(page: Page):
    page.goto("https://finance.naver.com")
    page.get_by_role("link", name="국내증시").click()
    # "시가총액"이라는 링크가 여러 개일 때 첫 번째만 클릭
    page.get_by_role("link", name="시가총액", exact=True).first.click()

def parse_table_kospi(page: Page) -> tuple[list, list]:
    tag_table = page.locator("table", has_text="코스피")  # 코스피 시가총액 표
    tag_thead = tag_table.locator("thead > tr > th")      # 헤더 열
    header = tag_thead.all_inner_texts()
    tag_tbody = tag_table.locator("tbody > tr")           # 보디 행
    body = [tr.locator("td").all_inner_texts() for tr in tag_tbody.all()]
    return header, body

if __name__ == "__main__":
    play, browser, page = run_playwright(slow_mo=1000)
    goto_market_cap(page)  # 시가총액 페이지로 이동
    header, body = parse_table_kospi(page)  # 코스피 시가총액 데이터 수집
    dumped = json.dumps(dict(header=header, body=body), ensure_ascii=False, indent=2)
    OUT_1_3.write_text(dumped, encoding="utf-8")  # JSON으로 저장

    browser.close()
    play.stop()

 

csv파일로 저장하기

"""
1. 네이버 증권 페이지에 접근하여 Playwright로 시가총액(코스피) 테이블 크롤링 및 저장
2. JSON 파일에서 표 데이터를 불러옴
3. 불필요한 공백 및 결측값, 빈 열 등을 정재
4. 정제된 데이터를 CSV 파일로 저장
"""
import json
from pathlib import Path

import pandas as pd  # 데이터프레임 처리를 위한 pandas 라이브러리

from step_1_1 import OUT_DIR  # 결과 파일을 저장할 폴더 경로
from step_1_3 import OUT_1_3  # 이전 단계에서 저장한 JSON 파일 경로

def clean_white_space(text: str) -> str:
    # 문자열 내 불필요한 공백(여러 칸, 줄바꿈 등)을 하나의 공백으로 정제
    return " ".join(text.split())

def table_to_dataframe(header: list, body: list) -> pd.DataFrame:
    # body(2차원 리스트)와 header(컬럼명 리스트)로 DataFrame 생성
    df_raw = pd.DataFrame(body, columns=header)
    # 한 열이라도 결측값이 있으면 해당 행 삭제
    df_raw = df_raw.dropna(how="any")
    # 마지막 열(보통 불필요한 빈 열) 제거
    df_raw = df_raw.iloc[:, :-1]
    # 모든 컬럼에 대해 공백 문자 정제 함수 적용
    for col in df_raw.columns:
        df_raw[col] = df_raw[col].apply(clean_white_space)
    return df_raw

if __name__ == "__main__":
    # 1_3단계에서 저장한 JSON 파일을 불러와서 파싱
    parsed = json.loads(OUT_1_3.read_text(encoding="utf8"))
    header, body = parsed["header"], parsed["body"]
    # 표 데이터를 DataFrame으로 변환 및 정제
    df_raw = table_to_dataframe(header, body)
    # DataFrame을 CSV 파일로 저장 (파일명은 현재 .py 파일명 기준)
    df_raw.to_csv(OUT_DIR / f"{Path(__file__).stem}.csv", index=False)

 

csv로 현재 기준 데이터를 제대로 추출하는지 확인

 

증권화면

 

 

크롤링 마지막 페이지 체크하기

if __name__ == "__main__":
    play, browser, page = run_playwright(slow_mo=1000)
    goto_market_cap(page)  # 시가총액 페이지로 이동
    total_page = fetch_total_page(page)  # 총 페이지 개수 추출
    print(f"{total_page=}")

    browser.close()
    play.stop()

첫 페이지부터 읽어서 마지막 페이지까지 페이지네이션 이동하기

from pathlib import Path

import pandas as pd
from playwright.sync_api import Page

from step_1_1 import OUT_DIR  # 이전에 작성한 모듈을 불러옵니다.
from step_1_2 import run_playwright
from step_1_3 import goto_market_cap, parse_table_kospi
from step_1_4 import table_to_dataframe
from step_2_1 import fetch_total_page

OUT_2_2 = OUT_DIR / f"{Path(__file__).stem}.csv"


def goto_page(page: Page, to: int):
    page.goto(f"https://finance.naver.com/sise/sise_market_sum.naver?&page={to}")


def fetch_market_cap(page: Page) -> pd.DataFrame:
    total_page = fetch_total_page(page)  # 총 페이지 개수 추출
    result = []
    for to in range(1, total_page + 1):  # 1부터 total_page까지 반복
        goto_page(page, to)  # 페이지 이동
        header, body = parse_table_kospi(page)  # 코스피 시가총액 표 추출
        df_raw = table_to_dataframe(header, body)  # 데이터 정제 및 DataFrame 객체 생성
        result.append(df_raw)  # DataFrame 객체를 리스트에 임시 저장
    return pd.concat(result)  # DataFrame 객체 결합


if __name__ == "__main__":
    play, browser, page = run_playwright(slow_mo=1000)
    goto_market_cap(page)  # 시가총액 페이지로 이동
    df_result = fetch_market_cap(page)  # 코스피 시가총액 표 추출
    df_result.to_csv(OUT_2_2, index=False)  # CSV 파일로 저장

    browser.close()
    play.stop()

 

단계별로 설명해줘서 좋습니다.

구몬학습처럼 단계별 지속학습이라 생각외로 덜 까먹는 것 같습니다.

 

파이썬으로 뭐 하나 만들 수 있는 날이 올 때 까지!

반응형