데이터 분석 기본_웹 크롤링(selenium)
필요 라이브러리 설치 및 불러오기
1. 크롬에서 주피터 노트북 실행
-아나콘다 프롬프트 → jupyter notebook --browser=chrome
-주피터와 드라이버의 브라우저 달라도 실행 가능(ex.주피터는 엣지, 드라이버는 크롬)
2. 라이브러리 불러오기
import selenium
from selenium import webdriver # 웹 브라우저 자동화
from selenium.webdriver.common.by import By # 요소 찾을 때 검색 방법 지정하는 기준 스위치
import chromedriver_autoinstaller # 자동으로 크롬드라이버 설치
from tqdm import tqdm # for문 진행사항 파악
import time # 시간 관련 작업 (ex.2초 지연)
import os # 컴퓨터의 운영체제 제어(폴더 생성, 파일 경로 확인, 파일 삭제 등)
import sys # 파이썬 인터프리터 제어(프로그램 강제 종료, 시스템 경로 확인 등)
import warnings
warnings.filterwarnings('ignore') # 워닝 무시
3. 크롬 드라이버 설치 및 불러오기
-크롬 드라이버: 파이썬이 크롬 브라우저를 통제할 수 있도록 도와주는 프로그램
chrome_path = chromedriver_autoinstaller.install(cwd=True)
# >>> 현재 작업중인 폴더에 144(크롬드라이버 버전)라는 폴더 생성
※크롬과 크롬 드라이버의 버전 맞춰줘야 함
나의 경우 크롬 드라이버는 144.x, 크롬은 143.x → chrome://settings/help에서 크롬 다시시작
실습용 티스토리 크롤링
1. 티스토리 1
1) 창 띄우기
-webdriver.브라우저명(): 해당 브라우저를 내 파이썬 코드로 제어하겠다고 선언
→ 스크롤, 버튼 클릭, 글자 입력 등 제어 가능
-time.sleep(n): n초간 정지(로딩 시간 확보)
driver = webdriver.Chrome() # 크롬 브라우저 사용
driver.get('https://ai-dev.tistory.com/1')
time.sleep(2)
2) 글 제목, 글쓴이, 글 쓴 시간 크롤링
-driver: 앞서 실행한 브라우저에서 찾겠다
-find_element(): 조건에 맞는 요소 하나만 찾음
※find_elements(): 조건에 맞는 요소 여러 개 찾음(리스트로 반환) → p태그/li태그에 주로 사용
-By.CSS_SELECTOR: CSS 선택자 방식 사용하겠다고 선언
# 글 제목
selector = '#content > div > div.hgroup > h1'
title = driver.find_element(By.CSS_SELECTOR, selector).text
title
# >>> '크롤링의 세계에 오신 것을 환영합니다.'
# 글쓴이
author = driver.find_element(By.CSS_SELECTOR, ".author").text
author
# >>> '로스카츠'
※id 선택자 대신 클래스 선택자 사용(클래스가 명확한(unique한) 것은 클래스 사용 용이)
# 글 작성 시간
selector = ".date"
time = driver.find_element(By.CSS_SELECTOR, selector).text
time
# >>> '2021. 1. 21. 14:06'
# 글 내용
selector = ".tt_article_useless_p_margin.contents_style"
text = driver.find_element(By.CSS_SELECTOR, selector).text
text
# >>> 'Hello, world!'
※클래스 선택자 이용시 띄어쓰기를 "."으로 바꿔줘야 함

3) 댓글 글쓴이, 댓글 내용, 댓글 날짜 리스트로 저장 후 데이터프레임으로 변환
# 잘못된 예시(class의 비유일성)
selector = ".tt_desc"
review = driver.find_element(By.CSS_SELECTOR, selector).text
review
# >>> '로스카츠 님의 블로그입니다.' (댓글이 안나옴)
# calss는 여러개가 있을 수 있으므로(동명이인 개념) 더 자세한 정보(copy selector 등) 필요
# 댓글 글쓴이
reviewer_list = []
for i in range(1, 17):
selector = f"#entry1Comment > div > div > div > div.tt-area-reply > ul > li:nth-child({i}) \
> div > div.tt-box-content > div.tt-box-meta"
reviewer = driver.find_element(By.CSS_SELECTOR, selector).text
reviewer_list.append(reviewer)
reviewer_list
# 댓글 내용
review_list = []
for i in range(1, 17): # 1부터 시작
# 대댓글은 무시해야 하므로 댓글은 16개라고 봐야 함
selector = f"#entry1Comment > div > div > div > div.tt-area-reply > ul > li:nth-child({i})\
> div > div.tt-box-content > div.tt-wrap-desc > p"
review_raw = driver.find_element(By.CSS_SELECTOR, selector).text
review_list.append(review_raw)
review_list
# 댓글 날짜
review_time_list = []
for i in range(1, 17):
selector = f"#entry1Comment > div > div > div > div.tt-area-reply > ul > li:nth-child({i}) \
> div > div.tt-box-content > div.tt-wrap-info > span.tt_date"
review_time = driver.find_element(By.CSS_SELECTOR, selector).text
review_time_list.append(review_time)
review_time_list
df = pd.DataFrame({'댓글':review_list, '댓글러':reviewer_list, '댓글 시간':review_time_list})
df

2. 티스토리 2
1) 창 띄우기
driver = webdriver.Chrome()
driver.get('https://ai-dev.tistory.com/2')
time.sleep(2)
2) 본문 내용 수집
# 본문 내용 크롤링
selector = "#article-view p"
# 한 칸 띄어쓰기: id 하위 자손(몇 칸 아래인지는 모름)
p_list = driver.find_elements(By.CSS_SELECTOR, selector)
p_list
# 리스트 컴프리헨션 사용하여 p_list에 들어있는 p태그들을 제거하고 text만 추출
p_list = [p.text for p in p_list]
p_list
# 리스트 컴프리헨션 사용하여 공백 요소 제거
p_list = [i for i in p_list if i != ' '] # 공백이 아닐 때만 리스트에 넣어 출력
p_list



3) 표 데이터 추출
# selector 구성 확인
# selector = "#article-view > div.tt_article_useless_p_margin.contents_style > div > table > tbody > tr:nth-child(1) > td:nth-child(1)"
# tbody-tr-rd 순 구성
# 표 데이터 추출
selector = "tbody tr td"
td_raw = driver.find_elements(By.CSS_SELECTOR, selector)
td_raw
# 표에 포함된 td 데이터만 크롤링
td_list = []
for td in td_raw[:18]: # 표가 3*6이므로 18개 데이터 존재
td_list.append(td.text)
td_list
# 리스트 컴프리헨션으로 3개씩 나누기
rows = [td_list[i:i+3] for i in range(0, len(td_list), 3)]
rows
4) 데이터프레임으로 변환
pd.DataFrame(rows[1:], columns=rows[0])
# rows[1:]: [상품 색상 가격]은 제외하겠다
※중괄호{}는 리스트를 데이터프레임으로 변환할 때 필요(변수명으로 넣을 때는 필요 x)

네이버 검색 블로그 크롤링
1. 창 띄우기
chrome_path = chromedriver_autoinstaller.install(cwd=True)
driver = webdriver.Chrome()
driver.get('https://www.naver.com/')
time.sleep(2)
2. 검색어 입력 후 옵션 선택
-.clear(): 입력창 안에 기존 텍스트 모두 삭제
-.send_keys(): 글자 입력
-submit(): 입력한 양식을 서버로 전송(<form> 태그 안에 들어있는 요소에 사용)
# 네이버 검색창에 "강남 맛집" 검색
query_txt = "강남 맛집"
selector = "#query"
element = driver.find_element(By.CSS_SELECTOR, selector)
element.clear()
element.send_keys(query_txt)
element.submit()
time.sleep(1)
-By.LINK_TEXT: 링크가 걸려있는 텍스트(<a>태그) 중에서 찾겠다
※By.PARTIAL_LINK: 글자의 일부분만 포함되어 있어도 찾음
-.click(): 해당 요소를 마우스로 클릭
※submit vs. click
| .submit() | .click() | |
| 대상 | 오직 'form'과 연결된 요소 | 클릭 가능한 모든 요소(버튼, 링크 등) |
| 작동 원리 | 엔터 키를 쳐서 양식 제출 | 마우스 왼쪽 클릭 |
| 범용성 | 특정 양식 전송 시에만 사용 | 매우 高 |
# '블로그' 클릭
driver.find_element(By.LINK_TEXT, "블로그").click()
time.sleep(1)
# '옵션' 클릭
driver.find_element(By.LINK_TEXT, "옵션").click()
time.sleep(1)
# 검색기간 '3개월' 클릭
driver.find_element(By.LINK_TEXT, '3개월').click()
3. 글 개수 확인 후 스크롤
-네이버 블로그에서 한번에 화면에 보여지는 글의 개수는 30개(더 많은 크롤링을 위해서는 스크롤 必)
def blog_cnt():
selector = ".sds-comps-vertical-layout.sds-comps-full-layout.p8YLe6TZKiwO531wrWYT"
# 글 제목 셀렉터
blog_cnt = len(driver.find_elements(By.CSS_SELECTOR, selector))
print(blog_cnt)
blog_cnt()
# >>> 30
-.execute_script(JavaScript 문자열): 현재 페이지를 스크롤 다운(JavaScript 강제 주입)
-(0, 9999): 제일 처음부터 마지막까지
→ 한번 실행할 때마다 로딩된 페이지 중 가장 마지막 글까지 내려감
driver.execute_script("window.scrollTo(0, 99999)")
n = 3 # 스크롤 할 횟수
i = 0 # 0, 1, 2
while i < n:
driver.execute_script("window.scrollTo(0, 99999)")
time.sleep(1)
i = i+1
blog_cnt
# >>> 120 (기존 30개 + 스크롤 90개)
4. 블로그 제목, url 수집

# 글 제목 수집 후 리스트로 저장
selector = ".sds-comps-text.sds-comps-text-ellipsis.sds-comps-text-ellipsis-1.sds-comps-text-type-headline1.sds-comps-text-weight-sm"
titles_raw = driver.find_elements(By.CSS_SELECTOR, selector)
title_list = []
for title_raw in titles_raw:
title = title_raw.text
title_list.append(title)
print(len(title_list))
title_list

-.get_attribute("속성명"): 속성값 가져옴
# 글 url 수집 후 리스트로 저장
selector = ".fender-ui_228e3bd1.Fup4JAiE19ubRE2YcX5w"
urls_raw = driver.find_elements(By.CSS_SELECTOR, selector)
url_list = []
for url_raw in urls_raw:
url = url_raw.get_attribute('href') # 링크 주소 가져오기
url_list.append(url)
print(len(url_list))
url_list

-index=Fale: 데이터프레임 옆에 자동으로 붙는 행 번호(0, 1, 2, ...) 제외
# 데이터프레임 저장
df_title_url = pd.DataFrame({"글제목": title_list, "url":url_list })
# csv로 저장
df_title_url.to_csv("title_url.csv", encoding='utf-8-sig', index=False)
5. 각 url 별 내용 수집
1) 첫 번째 블로그 창 띄우기
df = pd.read_csv('title_url.csv')
i = 0
url = df['url'][i]
driver = webdriver.Chrome()
driver.get(url)
time.sleep(1)
2) 제목, 글쓴이, 날짜 수집
-driver.switch_to: 현재의 시선을 다른 곳으로 돌림
-.frame(): 해당 액자 안으로 들어감
-<body> 안 <iframe>: 프레임 안 프레임
※네이버 블로그처럼 프레임을 사용하는 사이트를 크롤링할 때 iframe 안으로 들어가지 않으면 요소 찾을 수 x
driver.switch_to.frame("mainFrame")
# 게시글(제목, 글쓴이, 날짜 등) 크롤링 후 저장할 공간 생성
target_info = {}
# 여러 형식이 있으므로 딕셔너리로 생성
# → 추후 여러 리스트를 모아 최종 딕셔너리를 만들 것임
# 제목 크롤링
selector = ".se-fs-.se-ff-nanummaruburi"
title = driver.find_element(By.CSS_SELECTOR, selector).text
target_info['title'] = title
# 글쓴이 크롤링
selector = ".nick"
nickname = driver.find_element(By.CSS_SELECTOR, selector).text
target_info['nickname'] = nickname
# 날짜 크롤링
selector = ".se_publishDate.pcol2"
datetime = driver.find_element(By.CSS_SELECTOR, selector).text
target_info['datetime'] = datetime
3) 글 내용 수집
# 반복문으로 모든 문단 텍스트 수집 후 딕셔너리에 저장
selector = ".se-component.se-text.se-l-default"
text_raw = driver.find_elements(By.CSS_SELECTOR, selector)
# find_elements임에 주의(text로 하나만 추출하려면 인덱싱 필요)
for review in text_raw:
review_str = review_str + review.text.replace("\n", " ")
target_info['review'] = review_str
4) 전체 n개 블로그 글 수집
-driver.close(): 브라우저 종료(창 닫기)
-try~except: 예상치 못한 에러에 대응
total_dict = {} # 전체 블로그 글 수집할 딕셔너리 선언
for i in range(0, 3):
try:
driver = webdriver.Chrome()
driver.get(df_url['url'][i]) # i가 바뀌면서 블로그 글도 바뀜
time.sleep(1)
driver.switch_to.frame('mainFrame')
# 한 개 블로그(제목, 글쓴이, 날짜, 내용) 크롤링 후 저장할 딕셔너리
target_info = {}
# 제목
selector = ".se-module.se-module-text.se-title-text"
title= driver.find_element(By.CSS_SELECTOR, selector).text
target_info['title'] = title
# 글쓴이
selector = ".nick"
nickname = driver.find_element(By.CSS_SELECTOR, selector).text
target_info['nickname'] = nickname
# 날짜
selector = ".se_publishDate.pcol2"
datetime = driver.find_element(By.CSS_SELECTOR, selector).text
target_info['datetime'] = datetime
# 내용
review_str = ""
selector = ".se-component.se-text.se-l-default"
text_raw = driver.find_elements(By.CSS_SELECTOR, selector)
for review in text_raw:
review_str = review_str + review.text.replace("\n", " ")
target_info['review'] = review_str
print(target_info)
total_dict[i] = target_info
print(i+1, "번째 데이터가 수집되었습니다.", "\n")
time.sleep(2)
driver.close()
except:
print(i+1, "번째 블로그는 건너 뜁니다.")
driver.close()
continue
-.from_dict(): 딕셔너리를 데이터프레임으로 변환
-'index': 행렬 변환
import pandas as pd
result_df = pd.DataFrame.from_dict(total_dict, 'index')
result_df

#부트캠프후기 #멀티캠퍼스부트캠프 # 데이터마케팅부트캠프
'부트캠프 > 멀티캠퍼스_퍼포먼스 마케팅과 데이터 분석' 카테고리의 다른 글
| [멀티캠퍼스 부트캠프 7주차(1)] 데이터 분석 심화_검정의 이해 (0) | 2026.02.20 |
|---|---|
| [멀티캠퍼스 부트캠프 6주차(2)] 데이터 분석 기본_시각화 (0) | 2026.02.12 |
| [멀티캠퍼스 부트캠프 5주차(2)] 데이터 분석 기본_웹 크롤링(requests, beautifulsoup) (0) | 2026.02.08 |
| [멀티캠퍼스 부트캠프 5주차(1)] 데이터 분석 기본_웹 통신 개념 (0) | 2026.02.03 |
| [멀티캠퍼스 부트캠프 4주차(2)] 데이터 분석 기본_집계와 시각화 (0) | 2026.02.02 |