💡 AI 인사이트

🤖 AI가 여기에 결과를 출력합니다...

댓글 커뮤니티

쿠팡이벤트

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

검색

    로딩 중이에요... 🐣

    [코담] 웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트

    03 요약정리 | ✅ 저자: 이유정(박사)

    Selenium과 BeautifulSoup을 활용한 카카오맵 크롤링 정리

    카카오맵에서 특정 지역(예: "강남구 카페")을 검색하고, 여러 페이지에 걸쳐 가게 이름, 평점, 주소, 영업시간 등의 정보를 자동으로 수집하는 크롤링 자동화 과정을 정리합니다.


    1. 설치 준비

    필수 패키지 설치

    pip install selenium
    pip install webdriver-manager
    pip install beautifulsoup4
    pip install pandas
    

    2. 폴더 구조 예시

    프로젝트 루트/
    │
    ├── selenium_crawler/
    │   └── kakaomap_scrap.py
    │
    └── kakaomap.ipynb  ← Jupyter Notebook 파일
    

    selenium_crawler 디렉토리 만들고, kakaomap_scrap.py 생성 후 아래 크로릴코드를 넣는다.

    그리고, kakaomap.ipynb 파일 생성후 실행하면 나머지 파일들은 자동으로 생성된다.

    이미지


    3. 크롤링 코드 (kakaomap_scrap.py)

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from webdriver_manager.chrome import ChromeDriverManager
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as ec
    from bs4 import BeautifulSoup
    from time import sleep
    
    def get_items(html, parsed_items):
        soup = BeautifulSoup(html, "html.parser")
        items = soup.select("li.PlaceItem.clickArea")
    
        for item in items:
            item_dict = {}
            item_dict["name"] = item.find("span", {"data-id": "screenOutName"}).text
            item_dict["score"] = item.find("em", {"data-id": "scoreNum"}).text
            item_dict["address"] = item.find("p", {"data-id": "address"}).text
            item_dict["hour"] = item.find("a", {"data-id": "periodTxt"}).text
            parsed_items.append(item_dict)
    
        return parsed_items
    
    def get_data_from_kakaomap():
        try:
            from selenium.webdriver.chrome.options import Options
    
            options = Options()
            options.add_argument("--headless=new")
            options.add_argument("--disable-gpu")
            options.add_argument("--no-sandbox")
    
            service = Service(ChromeDriverManager().install())
            driver = webdriver.Chrome(service=service, options=options)
            driver.set_page_load_timeout(30)
    
            driver.get("https://map.kakao.com/")
            wait = WebDriverWait(driver, 10)
            wait.until(ec.visibility_of_element_located((By.ID, "search.keyword.query")))
    
            search_input = driver.find_element(By.ID, "search.keyword.query")
            search_input.send_keys("강남구 카페")
            search_input.send_keys(Keys.ENTER)
    
            wait.until(ec.element_to_be_clickable((By.ID, "info.search.place.more")))
            driver.execute_script("""
            var element = document.getElementById('dimmedLayer');
            if (element) {
                element.className = 'DimmedLayer HIDDEN';
            }
            """)
    
            sleep(1)
            show_more_btn = driver.find_element(By.ID, "info.search.place.more")
            show_more_btn.click()
    
            wait.until(ec.visibility_of_element_located((By.ID, "info.search.page")))
    
            page_count = 0
            items = []
    
            while page_count <= 5:
                if page_count != 0 and page_count % 5 == 0:
                    next_btn = driver.find_element(By.ID, "info.search.page.next")
                    next_btn.click()
                    wait.until(ec.visibility_of_element_located((By.ID, "info.search.place.list")))
    
                page_count += 1
                page_num = page_count % 5 if page_count % 5 != 0 else 5
                page_btn = driver.find_element(By.ID, f"info.search.page.no{page_num}")
                page_btn.click()
                wait.until(ec.visibility_of_element_located((By.ID, "info.search.place.list")))
    
                place_list = driver.find_element(By.ID, "info.search.place.list")
                shop_list = place_list.get_attribute("innerHTML")
    
                get_items(shop_list, items)
                sleep(2)
    
            driver.quit()
            return items
    
        except Exception as e:
            print("에러 발생:", e)
            raise e
    

    4. Jupyter Notebook에서 실행 (kakaomap.ipynb)

    import sys, os
    sys.path.append(os.getcwd())
    
    from selenium_crawler.kakaomap_scrap import get_data_from_kakaomap as get_data_v2
    
    # 데이터 수집 실행
    data = get_data_v2()
    data[:3]  # 앞에서 3개 미리 보기
    

    주피터실행화면


    5. 크롤링 흐름 요약

    단계 설명
    1 웹드라이버 셋업 및 크롬 브라우저 실행
    2 카카오맵 열고 검색창에 검색어 입력 (ex. 강남구 카페)
    3 더보기 버튼 클릭하여 페이지 버튼 노출
    4 1~6페이지 반복 클릭하며 결과 수집
    5 HTML 수집 후 BeautifulSoup으로 파싱
    6 가게 이름, 평점, 주소, 영업시간 정보 저장

    6. 결과 예시

    {'name': '별다방', 'score': '4.3', 'address': '서울 강남구 테헤란로...', 'hour': '09:00 ~ 22:00'}
    

    7. Pandas로 정리 및 CSV 파일 저장

    pip install pandas
    

    주피터 실행화면

    import pandas as pd
    
    # 리스트를 DataFrame으로 변환
    df = pd.DataFrame(data)
    
    # 컬럼 순서 재정렬 (선택사항)
    df = df[['name', 'score', 'address', 'hour']]
    
    # CSV로 저장
    df.to_csv("강남구_카페_목록.csv", index=False, encoding='utf-8-sig')
    

    CSV 파일은 Excel에서도 바로 열 수 있으며, utf-8-sig 인코딩으로 저장하면 한글이 깨지지 않습니다.


    8. VS Code에서 Jupyter 실행 방법

    1. VS Code 좌측 확장(Extensions)에서 Jupyter 확장 설치
    2. .ipynb 파일을 열면 상단에 실행 버튼이 생김
    3. 커널 선택 후 셀별로 실행 (Shift + Enter)
    4. Python 가상환경을 연결하려면 좌측 하단 Python 인터프리터 클릭하여 venv 선택

    9.오류 해결 팁: TimeoutError 발생 시

    원인

    • Selenium이 내부적으로 사용하는 localhost:포트 접속에 실패한 경우 (크롬 실행 실패)
    • driver.get()에서 카카오맵 페이지 로딩 중 타임아웃 발생

    해결 방법

    1. headless 옵션 추가: 백그라운드 실행 시 에러 방지
    2. 명시적 타임아웃 설정: driver.set_page_load_timeout(30)
    3. 크롬 드라이버 호환성 확인: chromedriver_autoinstaller 활용 가능
    pip install chromedriver-autoinstaller
    
    import chromedriver_autoinstaller
    chromedriver_autoinstaller.install()
    driver = webdriver.Chrome()
    

    10. jupyter 브라우저 실행시

    jupyter notebook --no-browser --port 8888
    

    비밀번호 재설정하기 (초기화)

    jupyter notebook password
    

    참고 링크


    TOP
    preload preload