[실무 강의] 초간단 파이썬: 30분 만에 쇼핑몰 베스트셀러 상품 정보 크롤링 & CSV 저장 완벽 마스터 튜토리얼

학습 목표: 데이터 수집의 자동화, 파이썬 크롤링으로 시작하기

현대 비즈니스 환경에서 데이터는 가장 강력한 자산입니다. 특히 경쟁사의 상품 가격, 베스트셀러 목록, 고객 리뷰 등을 실시간으로 파악하는 것은 마케팅 전략 수립에 필수적입니다. 이번 강의에서는 파이썬(Python)을 활용하여 복잡한 과정 없이 단 30분 만에 쇼핑몰의 베스트셀러 상품 정보를 수집하고, 이를 엑셀에서 바로 열어볼 수 있는 CSV 파일로 저장하는 실무 기술을 완벽하게 습득합니다.

단순히 코드를 복사하는 것이 아니라, 웹페이지의 구조를 이해하고 데이터를 추출하는 원리를 배움으로써 어떤 사이트에도 응용할 수 있는 기초 체력을 기르는 것이 본 강의의 핵심 목표입니다.

사전 준비 사항

원활한 실습을 위해 아래의 환경을 미리 구성해 주시기 바랍니다. 2026년 기준 최신 안정화 버전을 권장합니다.

  • 운영체제(OS): Windows 11, macOS Sonoma, 또는 Linux(Ubuntu 24.04 LTS 이상)
  • 개발 도구(IDE): Visual Studio Code (VS Code) 최신 버전
  • 파이썬 버전: Python 3.12.x 이상
  • 필수 라이브러리 설치: 터미널(Terminal) 또는 명령 프롬프트(CMD)에서 아래 명령어를 입력하여 설치합니다.
pip install requests beautifulsoup4 pandas

단계별 실습 과정

1단계: 라이브러리 임포트 및 기본 구조 설계

가장 먼저 우리가 사용할 도구들을 파이썬 스크립트로 불러와야 합니다. requests는 웹페이지에 접속하여 HTML 소스를 가져오는 역할을 하며, BeautifulSoup은 가져온 HTML 소스에서 우리가 원하는 데이터(상품명, 가격 등)를 골라내는 역할을 합니다. pandas는 수집된 데이터를 표 형태로 정리하여 CSV로 저장하는 데 사용됩니다.

import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

# 크롤링할 대상 URL (예시: 가상의 쇼핑몰 베스트셀러 페이지)
TARGET_URL = 'https://example-shopping-mall.com/bestsellers'

2단계: 웹페이지 접속 및 HTML 소스 가져오기

웹 서버에 데이터를 요청할 때는 ‘User-Agent’ 헤더를 설정하는 것이 중요합니다. 이는 서버에게 ‘나는 봇이 아니라 일반 브라우저를 사용하는 사용자다’라는 신호를 보내 차단을 방지하는 역할을 합니다.

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}

def get_html(url):
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        return response.text
    else:
        print(f'접속 실패: {response.status_code}')
        return None

html_content = get_html(TARGET_URL)

3단계: BeautifulSoup을 활용한 상품 정보 추출

이제 가져온 HTML 소스에서 상품명과 가격 데이터를 찾아야 합니다. 브라우저에서 ‘개발자 도구(F12)’를 열어 상품 정보가 어떤 HTML 태그와 클래스명으로 감싸져 있는지 확인해야 합니다. 보통 상품 리스트는 <div><li> 태그에 특정 클래스명이 붙어 있는 구조입니다.

def parse_products(html):
    soup = BeautifulSoup(html, 'html.parser')
    products = []
    
    # 상품 아이템들을 감싸고 있는 컨테이너 선택 (사이트 구조에 따라 변경 필요)
    items = soup.select('.product-item')
    
    for item in items:
        try:
            name = item.select_one('.product-name').get_text(strip=True)
            price = item.select_one('.product-price').get_text(strip=True).replace(',', '').replace('원', '')
            rank = item.select_one('.rank-badge').get_text(strip=True)
            
            products.append({
                '순위': rank,
                '상품명': name,
                '가격': int(price)
            })
        except Exception as e:
            print(f'데이터 추출 중 오류 발생: {e}')
            continue
            
    return products

product_list = parse_products(html_content)

4단계: 데이터 검증 및 정제

수집된 데이터가 리스트 형태(List of Dictionaries)로 잘 저장되었는지 확인합니다. 이때 데이터에 공백이 있거나 불필요한 특수문자가 포함되어 있다면 파이썬의 문자열 함수를 이용해 깔끔하게 정제합니다. 위 코드에서는 strip()replace()를 사용하여 이미 기본적인 정제를 마쳤습니다.

5단계: Pandas를 이용한 CSV 파일 저장

파이썬의 데이터 분석 라이브러리인 Pandas를 사용하면 리스트 형태의 데이터를 단 한 줄의 코드로 엑셀 호환 파일인 CSV로 변환할 수 있습니다. encoding='utf-8-sig' 옵션은 한글 깨짐 현상을 방지하기 위해 반드시 추가해야 합니다.

def save_to_csv(data, filename):
    df = pd.DataFrame(data)
    df.to_csv(filename, index=False, encoding='utf-8-sig')
    print(f'성공적으로 저장되었습니다: {filename}')

if product_list:
    save_to_csv(product_list, 'shopping_bestsellers_2026.csv')

결과 확인 및 주의사항

모든 코드를 실행한 후, 프로젝트 폴더에 shopping_bestsellers_2026.csv 파일이 생성되었는지 확인하십시오. 파일을 열어보면 순위, 상품명, 가격이 깔끔하게 정리된 표를 볼 수 있습니다.

크롤링 시 반드시 지켜야 할 윤리 수칙

  1. 과도한 요청 금지: time.sleep(1)과 같은 함수를 사용하여 서버에 무리가 가지 않도록 요청 간격을 조절하십시오.
  2. Robots.txt 확인: 수집하려는 사이트의 주소 뒤에 /robots.txt를 붙여 크롤링 허용 범위를 반드시 확인하십시오.
  3. 저작권 준수: 수집한 데이터를 상업적으로 재배포하거나 이용할 때는 해당 사이트의 이용 약관 및 저작권법을 준수해야 합니다.

이로써 여러분은 파이썬을 활용한 기초적인 웹 크롤링 프로세스를 완벽히 학습했습니다. 이 기술은 단순한 반복 업무를 줄여줄 뿐만 아니라, 향후 데이터 사이언스나 AI 모델 학습을 위한 데이터셋 구축의 밑거름이 될 것입니다. 실습 중 발생하는 오류는 HTML 구조의 변경 때문인 경우가 많으므로, 항상 개발자 도구를 통해 최신 태그 정보를 확인하는 습관을 들이시기 바랍니다.

댓글 남기기


Warning: getimagesize(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(https://thumbnail.coupangcdn.com/thumbnails/remote/492x492ex/image/vendor_inventory/8bd2/db22dd7eb69ca9c64ef0eea3be990b26004a2d726fd031611d285ba02bee.jpg): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(https://media.vlpt.us/images/bi-sz/post/87ef2dfc-7b73-46b9-aed3-ac60eeb290a9/4.PNG): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(https://media.vlpt.us/images/bi-sz/post/f4016cb0-f9a0-4554-9478-c5826e6c64d2/1.PNG): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Fatal error: Uncaught ErrorException: md5_file(/hosting/apdldk/html/wp-content/litespeed/css/48a0b6ebafb65640528045dc1ac6145d.css.tmp): Failed to open stream: No such file or directory in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php:148 Stack trace: #0 [internal function]: litespeed_exception_handler() #1 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php(148): md5_file() #2 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(845): LiteSpeed\Optimizer->serve() #3 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(338): LiteSpeed\Optimize->_build_hash_url() #4 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(265): LiteSpeed\Optimize->_optimize() #5 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(226): LiteSpeed\Optimize->_finalize() #6 /hosting/apdldk/html/wp-includes/class-wp-hook.php(341): LiteSpeed\Optimize->finalize() #7 /hosting/apdldk/html/wp-includes/plugin.php(205): WP_Hook->apply_filters() #8 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/core.cls.php(464): apply_filters() #9 [internal function]: LiteSpeed\Core->send_headers_force() #10 /hosting/apdldk/html/wp-includes/functions.php(5481): ob_end_flush() #11 /hosting/apdldk/html/wp-includes/class-wp-hook.php(341): wp_ob_end_flush_all() #12 /hosting/apdldk/html/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters() #13 /hosting/apdldk/html/wp-includes/plugin.php(522): WP_Hook->do_action() #14 /hosting/apdldk/html/wp-includes/load.php(1308): do_action() #15 [internal function]: shutdown_action_hook() #16 {main} thrown in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php on line 148