본문 바로가기
Data & AI/Python Coding

Selenium으로 구현한 파일 자동 다운로드 크롤러

by sumanggu 2025. 7. 18.

안녕하세요!

혹시 이런 경험 있으신가요?

매번 같은 사이트에서 게시글을 하나씩 클릭해서 첨부파일을 받고,
다시 목록으로 돌아가서 다음 게시글을 클릭하고,
또 저장하고… 또 클릭하고…

하루 이틀도 아니고, 반복될수록 너무 지겹죠.
그래서 저는 이 지겨운 루틴을 벗어나기 위해 Selenium으로 웹 자동화 크롤러를 만들어봤습니다.
한 번의 실행만으로 검색 → 다운로드 → 폴더 정리까지 자동으로 해주는 Selenium 기반 크롤러입니다.


💡여기서 크롤링(Crawling)이란?

간단히 말하면, 사람이 웹사이트를 누비며 정보를 확인하고 저장하던 일을 컴퓨터가 대신 해주는 것입니다.
예를 들어 뉴스 기사, 쇼핑몰 상품 정보, 공공 데이터 등 웹에 공개된 대부분의 정보는 크롤링을 통해 자동으로 수집할 수 있어요.

크롤링은 방식에 따라 크게 두 가지로 나뉘는데요:

📌 1. 정적 크롤링 (Static Crawling)

HTML 소스만으로도 정보가 보이는 사이트

이런 사이트는 requests와 BeautifulSoup만으로도 충분합니다.
빠르고 가볍죠.
하지만 요즘 대부분의 사이트는 이 방법만으론 부족합니다.

📌  2. 동적 크롤링 (Dynamic Crawling)

버튼을 눌러야 목록이 뜨거나, 스크롤을 내려야 계속 로딩되는 사이트

이럴 땐 실제 사람이 마우스와 키보드로 조작하듯 동작해야 해서 Selenium이라는 도구를 사용합니다.

💡Selenium은 웹 브라우저를 자동으로 조작할 수 있게 해주는 Python 라이브러리입니다.
로그인, 검색, 스크롤, 파일 다운로드처럼 사람이 하던 동작을 코드로 대신 수행할 수 있어요.

Selenium은 Chrome뿐 아니라 Firefox, Edge, Safari 등 다양한 브라우저를 지원하지만,
설정이 가장 간단하고 안정적인 Chrome이 실무에서 가장 널리 사용됩니다.
저도 이번 프로젝트에서 Chrome을 사용했습니다.

💡 한 가지 더! Selenium이 브라우저를 직접 조작하는 건 아니에요.
브라우저와 코드를 연결해주는 중간다리, 바로 크롬 드라이버(Chromedriver)가 필요합니다.

저는 매번 드라이버를 수동 설치하지 않기 위해
webdriver-manager를 사용해 자동 설치되도록 설정했습니다:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

이렇게 설정해두면 내 크롬 버전에 맞는 드라이버가 자동으로 설치되고,
실행 시 호환성 문제도 줄어들어 훨씬 편리해요!


✨ 제가 만든 크롤러는 이런 흐름으로 동작합니다!

    (1) 사이트 진입 및 목록 로딩
    (2) 특정 키워드가 포함된 게시글 제목만 수집
    (3) 각 게시글 제목으로 검색
    (4) 검색 결과 클릭 → 첨부 파일 다운로드
    (5) 파일명을 기준으로 폴더별 정리

위 흐름을 실제로 구현하면서 Selenium의 다양한 기능을 활용했는데요.
그 중 몇 가지만 정리하여 소개해드리겠습니다.

1. 브라우저 다운로드 경로 지정

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--start-maximized")
options.add_experimental_option("prefs", {
    "download.default_directory": str(download_dir),
    "download.prompt_for_download": False,
    "download.directory_upgrade": True,
    "safebrowsing.enabled": True
})

→ 매번 저장 경로 묻지 않고, 자동 지정 폴더로 저장됩니다!

2. 검색어 입력 + 버튼 클릭 자동화

# target_text는 검색할 text

search_input.clear()
search_input.send_keys(target_text)
search_button.click()

→ 사람이 입력하고 누르던 걸, 코드가 대신 해주는 거죠!

3. 팝업 감지

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

try:
    close_button = WebDriverWait(driver, 3).until(
        EC.presence_of_element_located((By.XPATH, '//button[contains(text(), "닫기")]'))
    )
    if close_button.is_displayed():
        close_button.click()
        time.sleep(1)
        continue
except:
    pass

→ 간혹 첨부파일이 없을 때 뜨는 팝업,
    하나하나 마우스로 닫던 걸 자동으로 감지해서 닫고 다음으로 넘어가도록 했습니다.

4. 자동 스크롤 + 게시글 제목 수집

def collect_titles_stop_after_same_count(driver, scroll_box_selector, pause=0.7, max_same=3, max_steps=100):
    scroll_box = driver.find_element(By.CSS_SELECTOR, scroll_box_selector)
    collected = []
    seen = set()

    initial_divs = driver.find_elements(By.CSS_SELECTOR, "div.slick-link")
    for div in initial_divs:
        try:
            title = div.text.strip()
            if "표준공사코드" in title and title not in seen:
                collected.append(title)
                seen.add(title)
        except StaleElementReferenceException:
            continue

    same_count = 0
    previous_count = len(collected)

    for step in range(1, max_steps + 1):
        driver.execute_script("arguments[0].scrollTop += 500;", scroll_box)
        time.sleep(pause)

        try:
            titles = [
                div.text.strip()
                for div in driver.find_elements(By.CSS_SELECTOR, "div.slick-link")
                if "표준공사코드" in div.text.strip()
            ]
            new_titles = [t for t in titles if t not in seen]

            for title in new_titles:
                collected.append(title)
                seen.add(title)

            current_count = len(collected)

            if current_count == previous_count:
                same_count += 1
            else:
                same_count = 0
                previous_count = current_count

            if same_count >= max_same:
                break

        except StaleElementReferenceException:
    return collected

→ 스크롤을 내려야 게시글이 계속 보이는 구조라면?
    자동으로 스크롤이 내려가면서 새로 뜨는 제목을 계속 수집합니다.
    단, 더 이상 새로운 글이 안 나오면 멈춰요.


이 크롤러를 통해 반복되던 수작업을 자동화하고, 파일 정리까지 한 번에 해결할 수 있었습니다.

해당 흐름의 크롤러는 이런 업무들에 활용할 수 있겠죠?

  - 매주 올라오는 자료 정리 (공공 데이터, 입찰 공고 등)
  - 제품 업데이트 수집, 매뉴얼 다운로드
  - 게시판 순회 모니터링

이렇게 실제 업무에서 반복되는 웹 탐색과 다운로드 작업이 있다면, Selenium을 활용해 자동화해보는 것도 추천드립니다!👍

읽어주셔서 감사합니다. ㅇ_<☆