부트캠프

멋쟁이사자처럼 부트캠프 그로스마케팅 4기 28일차_260417

Yuuma 2026. 4. 17. 20:22
[Day12] 동적 크롤링 실습 | Selenium + 네이버 블로그 크롤링

동적 크롤링 실습
Selenium + 네이버 블로그 크롤링

💡 오늘 한 줄 요약 정적 크롤링으로 안 되는 곳은 Selenium으로 브라우저를 직접 조종해서 데이터를 뽑는다. 그리고 네이버 블로그는 iframe 진입이 핵심!

1. 정적 크롤링 vs 동적 크롤링

크롤링 방식은 크게 두 가지다. 정적 크롤링은 HTML 파일만 다운로드하면 바로 데이터를 얻을 수 있는 경우, 동적 크롤링은 브라우저를 실제로 실행해야 데이터가 보이는 경우에 사용한다.

정적 vs 동적 크롤링 비교

정적 크롤링과 동적 크롤링 비교

동적 크롤링이 필요한 대표적인 상황

  • 🔐 로그인을 해야 데이터가 보이는 경우 (광고 플랫폼, CRM)
  • 📜 스크롤을 내려야 데이터가 불러와지는 경우 (무한스크롤)
  • 🖱️ 버튼 클릭을 해야 다음 페이지가 나오는 경우
  • ⚙️ JavaScript가 실행되어야 데이터가 렌더링되는 경우
💡 크롤링을 할 줄 알면, 수작업으로 며칠 걸리던 데이터 수집을 몇 분 안에 끝낼 수 있다.

2. 그로스 마케터에게 크롤링이 중요한 이유

업무 상황크롤링 활용 방법
경쟁사 가격 모니터링경쟁사 쇼핑몰에서 가격 자동 수집
키워드 리서치검색 결과 상위 노출 콘텐츠 자동 수집 및 분석
리뷰 분석앱스토어, 쇼핑몰 리뷰 대량 수집 후 감성 분석
광고 소재 리서치경쟁사 광고 문구, 랜딩 페이지 자동 수집
트렌드 파악SNS 해시태그, 커뮤니티 인기 게시글 수집

3. Selenium이 하는 일

Selenium은 브라우저(크롬 등)를 코드로 조종하는 도구다. 사람이 손으로 하는 모든 브라우저 조작을 자동화할 수 있다.

Selenium 동작 흐름

Selenium 5단계 기본 흐름

할 수 있는 일예시
브라우저 열기 / 닫기webdriver.Chrome(...) / driver.quit()
특정 URL로 이동driver.get("https://...")
요소 찾기find_element(By.CSS_SELECTOR, ...)
클릭하기element.click()
텍스트 입력element.send_keys("검색어")
데이터 추출element.text

4. 선택자(Selector): CSS Selector를 쓰는 이유

Selenium이 데이터를 수집하려면 HTML에서 원하는 요소의 위치를 알아야 한다. 이때 사용하는 것이 선택자(Selector)다.

CSS Selector 문법 정리

CSS Selector 핵심 문법 — 이것만 알면 80%는 해결 가능

이유설명
직관적이다HTML 구조와 거의 동일하게 생겨서 읽기 쉽다
짧고 간결하다XPath보다 훨씬 짧게 쓸 수 있다
범용적이다Selenium, BeautifulSoup, JavaScript 모두 지원
크롬에서 바로 복사개발자도구 → 요소 우클릭 → "Copy selector" 기능
📌 선택자 찾는 법 (크롬): F12 → 수집할 요소 위에서 우클릭 → "검사(Inspect)" → 파란색 하이라이트된 HTML에서 class, id 확인

5. 코랩 실습 환경 준비

구글 코랩에서 Selenium을 사용하려면 아래 설치 과정이 필요하다.

🖥️ Shell (Colab 셀 실행) Bash
!pip install -q selenium
!apt-get update -qq
!apt-get install -y libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libasound2 libnss3 libxss1 libgtk-3-0

!wget -q https://storage.googleapis.com/chrome-for-testing-public/148.0.7766.3/linux64/chrome-linux64.zip
!wget -q https://storage.googleapis.com/chrome-for-testing-public/148.0.7766.3/linux64/chromedriver-linux64.zip
!unzip -qo chrome-linux64.zip
!unzip -qo chromedriver-linux64.zip
!chmod +x /content/chrome-linux64/chrome
!chmod +x /content/chromedriver-linux64/chromedriver
🐍 selenium_setup.py Python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options

options = Options()
options.binary_location = "/content/chrome-linux64/chrome"
options.add_argument("--headless=new")   # 창 없이 실행
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")

service = Service("/content/chromedriver-linux64/chromedriver")
driver = webdriver.Chrome(service=service, options=options)

6. 실습 코드 — quotes.toscrape.com

실습 사이트: https://quotes.toscrape.com/ — 명언과 저자가 정리된 크롤링 연습용 사이트

🖥️ 실습 1 & 2. 요소 하나 / 여러 개 가져오기
목표: find_element(단수) vs find_elements(복수) 차이 이해
메서드반환값없을 때언제 쓰나
find_element요소 1개❌ 에러 발생특정 요소 하나를 정확히 찾을 때
find_elements요소 리스트✅ 빈 리스트 반환여러 개를 한 번에 수집할 때
🐍 crawling2.py Python
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome(service=service, options=options)
driver.get("https://quotes.toscrape.com/")

# 1페이지 명언 수집
# div.quote 안의 span.text만 타겟팅해서 명언 텍스트 리스트로 가져옴
quote = driver.find_elements(By.CSS_SELECTOR, "div.quote span.text")
for q in quote:
    print(q.text)
    print("--------------")

# 다음 버튼 찾기
# li.next a = 하단 Next 버튼 링크
next_button = driver.find_element(By.CSS_SELECTOR, "li.next a")

# 다음 버튼 누르기 → 2페이지로 이동
next_button.click()
print("<2페이지>")

# 2페이지 명언 수집
# 페이지가 바뀐 후 동일한 셀렉터로 다시 요소를 가져옴
quote = driver.find_elements(By.CSS_SELECTOR, "div.quote span.text")
for q2 in quote:
    print(q2.text)
    print("--------------")
💡 1페이지 수집 후 Next 버튼을 찾아 .click() → 2페이지로 이동해서 다시 수집
📋 실습 3 & 4. 명언 + 저자 함께 가져오기 (블록 단위 수집)
목표: 부모 블록(div.quote)을 먼저 수집 → 각 블록 안에서 자식 요소 추출 → DataFrame 저장
💡 핵심 패턴: 부모 블록 전체를 리스트로 받은 뒤, 각 블록 안에서 다시 find_element로 자식을 찾는다. 이렇게 해야 명언과 저자가 쌍으로 매칭된다.
🐍 crawling1.py Python
from selenium import webdriver
from selenium.webdriver.common.by import By
import pandas as pd

driver = webdriver.Chrome(service=service, options=options)
driver.get("https://quotes.toscrape.com/")

# div.quote CSS 셀렉터에 해당하는 모든 요소를 리스트로 가져옴
# 각 quote 블록(명언 하나 = div.quote 하나)이 blocks 리스트에 담김
blocks = driver.find_elements(By.CSS_SELECTOR, "div.quote")

# 딕셔너리들을 담을 빈 리스트 초기화
quotes_box = []

# blocks 리스트에서 요소(div.quote)를 하나씩 꺼내 반복
for block in blocks:
    # 현재 block 안에서 span.text 요소를 찾아 텍스트(명언) 추출
    text = block.find_element(By.CSS_SELECTOR, "span.text").text
    # 현재 block 안에서 small.author 요소를 찾아 텍스트(저자명) 추출
    author = block.find_element(By.CSS_SELECTOR, "small.author").text

    # 추출한 저자, 명언을 딕셔너리로 묶음
    quotes_dict = {
        "저자": author,
        "명언": text
    }

    # 딕셔너리를 리스트에 추가 (반복마다 한 명언씩 쌓임)
    quotes_box.append(quotes_dict)

# 최종적으로 quotes_box = [{"저자": ..., "명언": ...}, {"저자": ..., "명언": ...}, ...]
# pd.DataFrame(quotes_box) 하면 바로 표 형태로 변환 가능
💡 quotes_box 리스트에 딕셔너리를 쌓은 뒤 pd.DataFrame(quotes_box)으로 바로 표 변환 가능
🖱️ 실습 5. Next 버튼 클릭 — 태그 수집 (수동 3페이지)
목표: .click()으로 페이지 이동, 태그를 여러 페이지에서 모으기
🐍 crawling3.py Python
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome(service=service, options=options)
driver.get("https://quotes.toscrape.com/")

tag_list = []

#1페이지 태그값 모으기
tags = driver.find_elements(By.CSS_SELECTOR, "div.tags a.tag")
for tag in tags:
  print(tag.text)
  tag_list.append(tag.text)

#다음으로 넘어가기
next_button = driver.find_element(By.CSS_SELECTOR, "li.next a")
next_button.click()
print("<2페이지>")

#2페이지 태그값 모으기
tags = driver.find_elements(By.CSS_SELECTOR, "div.tags a.tag")
for tag in tags:
  print(tag.text)
  tag_list.append(tag.text)

#다음으로 넘어가기
next_button = driver.find_element(By.CSS_SELECTOR, "li.next a")
next_button.click()
print("<3페이지>")

#3페이지 태그값 모으기
tags = driver.find_elements(By.CSS_SELECTOR, "div.tags a.tag")
for tag in tags:
  print(tag.text)
  tag_list.append(tag.text)

print(tag_list)

#중복 값 제거
print(len(tag_list))        # 중복 제거 전 개수
print(len(set(tag_list)))   # 중복 제거 후 개수
💡 중복 제거: set(tag_list)으로 고유값만 남길 수 있다
🔄 실습 6 (심화). for 반복문으로 N페이지 자동 수집
목표: 수동 복붙 대신 for문 + break로 마지막 페이지 처리까지 자동화
🐍 crawling4.py Python
import time

driver = webdriver.Chrome(service=service, options=options)
driver.get("https://quotes.toscrape.com/")

# 태그값들을 담아둘 빈 바구니(리스트)
all_tages = []

for page in range(1,4): #1,2,3 → 총 3번 반복
  print(page, "-----------------")
  
  # 1) 현재 페이지에서 태그값 찾기
  # "div.tags 안에 있는 a.tag" 요소들을 전부 가져와서 tags에 저장
  tags = driver.find_elements(By.CSS_SELECTOR, "div.tags a.tag")
  for tag in tags:  # tags 리스트에서 하나씩 꺼내기
    print(tag.text)  # 태그 텍스트 출력

    # 태그값을 바구니에 하나씩 담기
    tag_list.append(tag.text)

  if page == 3:   # page가 3이 되면 (마지막 페이지)
    break   # 더 이상 next 버튼 누르지 않고 for문 종료

  # 2) next 버튼 찾고 누르기 → 다음 페이지로 이동
  next_button = driver.find_element(By.CSS_SELECTOR, "li.next a")
  next_button.click()
  print("------------------다음 페이지------------------")
  
  # 페이지가 완전히 로딩될 때까지 2초 기다리기
  # (너무 빨리 실행하면 태그를 못 찾을 수 있음)
  time.sleep(2)
💡 if page == 3: break — 마지막 페이지에서 Next 버튼이 없는데 클릭하면 에러나므로 break로 미리 탈출

7. 실전 실습 — 네이버 블로그 크롤링

네이버 블로그 크롤링 전체 흐름

네이버 블로그 크롤링 전체 파이프라인

STEP 1. 네이버 검색 API로 블로그 링크 수집

🐍 crawling5.py Python
import requests
import pandas as pd
from google.colab import userdata

client_id = userdata.get("NAVER_CLIENT_ID")
client_secret = userdata.get("NAVER_CLIENT_SECRET")

url = "https://openapi.naver.com/v1/search/blog.json"

headers = {
    "X-Naver-Client-Id": client_id,
    "X-Naver-Client-Secret": client_secret
}

params = {
    "query": "방화수류정",
    "display": 10
}

response = requests.get(url, headers=headers, params=params)
data = response.json()

links = []
for item in data["items"]:
    links.append(item["link"])

df_links = pd.DataFrame(links, columns=["url"])
df_links
💡 네이버 개발자센터에서 Client ID / Secret 발급 필요. 코랩은 userdata.get()으로 안전하게 불러옴

STEP 2. Selenium으로 블로그 본문 크롤링

⚠️ 네이버 블로그 iframe 주의!
네이버 블로그 본문은 <iframe name="mainFrame"> 안에 있다. 반드시 driver.switch_to.frame("mainFrame")으로 진입한 뒤에 요소를 찾아야 하고, 수집 후엔 driver.switch_to.default_content()로 복귀해야 다음 URL을 처리할 수 있다.
🐍 crawling6.py Python
from selenium import webdriver
from selenium.webdriver.common.by import By
import pandas as pd

driver = webdriver.Chrome(service=service, options=options)

title_list = []
content_list = []

for url in df_links["url"]:
    driver.get(url)
    driver.switch_to.frame("mainFrame")

    title = driver.find_element(By.CSS_SELECTOR, "div.se-title-text span").text
    content = driver.find_element(By.CSS_SELECTOR, "div.se-main-container").text

    # print(title, content)

    title_list.append(title)
    content_list.append(content)

    driver.switch_to.default_content()

df_links["title"] = title_list
df_links["content"] = content_list
df_links["content"] = df_links["content"].str.replace("\n", " ")
df_links
💡 switch_to.frame() → 수집 → switch_to.default_content() 세트를 for문 안에서 반복

최종 저장

🐍 save.py Python
df_links["title"] = title_list
df_links["content"] = content_list
df_links["content"] = df_links["content"].str.replace("\n", " ")

df_links.to_csv("result.csv", index=False, encoding="utf-8-sig")
# utf-8-sig: 한글 깨짐 없이 엑셀에서도 바로 열 수 있음

📋 오늘 배운 것 총정리

개념핵심 내용
동적 크롤링 필요 상황로그인 필요, 무한스크롤, JS 렌더링
Selenium 기본 흐름브라우저 열기 → URL 이동 → 요소 찾기 → 수집 → 종료
CSS Selector.클래스, #id, 부모 자식 — 개발자도구에서 Copy selector로 바로 복사
find_element vs find_elements단수(없으면 에러) vs 복수(없으면 빈 리스트)
페이지 이동.click() + time.sleep() 필수 세트
네이버 블로그 특이사항iframe → switch_to.frame("mainFrame") 먼저!
데이터 저장df.to_csv("파일명.csv", encoding="utf-8-sig")