파이썬 크롤링

aiden·2026년 1월 30일

requests 라이브러리

# 라이브러리 설치
!pip install requests

# 파이썬 코드로 http 요청을 보낼 수 있게 해주는 라이브러리
import requests

# http 요청을 보낼 url
url = "https://www.bookspot.store/"

GET

requests 라이브러리의 get 메소드를 알아보자.

# HTTP GET 요청을 보내는 함수(메소드)
requests.get(url)

->이 주소(url)에 접속해서 데이터를 주세요"라고 서버에 요청하는 코드

HTTP GET
웹에서 데이터를 가져올 때 사용하는 요청 방식

구조 설명

response = requests.get(url)

requests - HTTP 요청을 보내는 라이브러리
get - GET 방식으로 요청
url - 접속할 웹 주소
response - 서버가 보내준 응답 객체

response 객체의 속성

response.text        # HTML 코드
response.status_code # 상태 코드 (200이면 성공)
response.headers     # 응답 헤더
response.content     # 바이너리 데이터

내부동작

requests.get(url)
  1. 서버에 HTTP GET 요청을 보냄
  2. 서버가 그 요청을 처리함
  3. 서버가 응답(HTML, JSON 등)을 돌려줌
  4. 그 응답을 response 객체로 받음

파라미터 전달

requests.get("https://example.com/search", params={"q": "python"})

실제 요청 주소:

https://example.com/search?q=python

beautifulsoup 라이브러리

# pip install requests beautifulsoup4
!pip install requests beautifulsoup4

.select()

BeautifulSoup 라이브러리에서 CSS 선택자(CSS Selector)를 사용해 HTML 요소를 선택하는 메서드
유사한 것으로는 .select_one(), .find(), .find_all()이라는 것도 있다.

사용법
형식: soup.select(선택자)
지정한 태그, 속성, id 등을 찾아서 리스트로 반환한다.

선택자별 예시

    1) tag
    soup.select('tag')  
        예시)
        soup.select('div') → 모든 <div> 태그를 찾아서 반환

    2) .class
        예시)
        soup.select('.title') → class="title"인 모든 요소를 찾아서 반환

    3) #id
        예시)
        soup.select('#header') → id="header"인 모든 요소를 찾아서 반환

    4) tag.class
        예시)
        soup.select('p.warning') → <p class="warning">인 모든 요소를 찾아서 반환

    5) 부모태그 > 자식태그
        예시)
        soup.select('div > span') → <div> 안에 <span>인 모든 요소를 찾아서 반환

    6)[attr]
        예시)
        soup.select('[href]') → href 속성이 있는 모든 요소 찾아서 반환

    7) [attr="value"]
        예시)
        soup.select('[type="text"]') → 속성 = 값 인 모든 요소를 찾아서 반환

    8) [attr^="value"]
        예시)
        soup.select('[class^="btn"]') → 속성 값이 ~로 시작하는 모든 요소를 찾아서 반환

    9) [attr$="value"]
        예시)
        soup.select('[href$=".pdf"]') → 속성 값이 ~로 끝나는 모든 요소를 찾아서 반환

    10) [attr*="value"]
        예시)
        soup.select('[class*="active"]') → 속성 값에 ~ 포함하는 모든 요소를 찾아서 반환
# HTML을 분석(파싱)하기 위한 라이브러리
from bs4 import BeautifulSoup

# 파일 경로 지정. 현재 폴더 기준(./)
file_path = './html_css/historyofpython.html'

# HTML 파일 열기
# 'r' → 읽기 모드
# encoding='utf-8' → 한글 깨짐 방지
# f.read() → 파일 전체 내용을 문자열로 읽음
with open(file_path, 'r', encoding='utf-8') as f:
    html_doc = f.read()

# HTML 코드 전체가 들어있는 문자열(str)
type(html_doc)
# html_doc

# head 부분 제거
body = html_doc.split('</head>')[1]
response = BeautifulSoup(body, 'html.parser')
response

# BeautifulSoup을 이용해서 파싱하였으므로 bs4.BeautifulSoup 타입
type(response)

selenium

동적 로딩 문제 (JavaScript Rendering 문제)

import requests
url = "https://finance.naver.com/sise/lastsearch2.naver"
response = requests.get(url)
response

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
    "Accept-Language": "ko-KR,ko;q=0.9,en;q=0.8",
    "Referer": "https://finance.naver.com/"
}
response = requests.get(url, headers = headers)
response

soup = BeautifulSoup(response.text, 'html.parser')
soup.select('tbody > tr')
soup.select('tbody')

결과로 빈 list 반환됨

  1. 정적 초기 로드 (Static Load)
  • requests.get()이 가져오는 부분
  • 서버가 처음 보내주는 기본 HTML 문서
  • 기본적인 HTML 구조, CSS 파일 연결 정보, JavaScript 파일을 불러오라는

👉 실제 데이터가 아닌 데이터를 가져올 준비만 된 상태의 뼈대 HTML

  1. 동적 렌더링 (Dynamic Rendering)
  • 브라우저에서만 발생
  • 브라우저가 JavaScript 파일을 실행
  • JavaScript가 서버에 추가 요청(AJAX, fetch 등)을 보냄
  • 서버에서 받은 데이터를 이용해 HTML을 새로 생성하거나 기존 HTML을 수정
  • 화면에 데이터를 동적으로 삽입

👉 결과로 우리가 눈으로 보는 최종 화면 얻음

❗ requests.get()은 JavaScript를 실행하지 않고 HTML 파일만 가져오므로 response.text에는 자바스크립트 실행 전의 초기 HTML만 들어 있음
실제 데이터는 브라우저가 JS를 실행해야 생성되기 때문에
requests만으로는 보이지 않는 것.

동적 로딩 페이지 크롤링

  1. Selenium 사용(브라우저 자동화, JS 실행 가능)
  2. 네트워크 탭에서 실제 데이터 API 직접 호출
  3. 개발자 도구에서 AJAX 요청 URL 분석 후 직접 requests로 요청

requests는 자바스크립트를 실행하지 않기 때문에, 동적으로 생성되는 데이터는 가져오지 못한다.

# pip install selenium
!pip install selenium
  
from selenium import webdriver
url = "https://finance.naver.com/sise/lastsearch2.naver"
  
driver = webdriver.Chrome() # 또는 Firefox, Edge 등
driver.get(url)

# 페이지 로딩 시간을 충분히 줍니다.
driver.implicitly_wait(10) 

# 자바스크립트가 실행된 최종 HTML 소스를 가져옵니다.
final_html = driver.page_source

# BeautifulSoup으로 분석
soup = BeautifulSoup(final_html, 'html.parser')

driver.quit()
  
# soup.select('tbody > tr')
# BeautifulSoup을 이용해 테이블의 tbody 안에 있는 모든 tr(행)을 선택한다.
# 즉, 네이버 금융 인기검색 종목 표의 각 종목 행을 가져오는 코드.

실습

https://finance.naver.com/sise/lastsearch2.naver 페이지에서

  1. 데이터를 크롤링하고
  2. 데이터프레임으로 만든 후
  3. excel 파일로 저장하시오
# pip install selenium
!pip install selenium

# pip install requests beautifulsoup4
!pip install requests beautifulsoup4
  
from selenium import webdriver
from bs4 import BeautifulSoup
import pandas as pd

url = "https://finance.naver.com/sise/lastsearch2.naver"

driver = webdriver.Chrome() # 또는 Firefox, Edge 등
driver.get(url)

# 페이지 로딩 시간을 충분히 줍니다.
driver.implicitly_wait(10) 

# 자바스크립트가 실행된 최종 HTML 소스를 가져옵니다.
final_html = driver.page_source

# BeautifulSoup으로 분석
soup = BeautifulSoup(final_html, 'html.parser')

# BeautifulSoup을 이용해 테이블의 tbody 안에 있는 모든 tr(행)을 선택한다.
# soup.select('tbody > tr')

header_row = soup.select_one('tr.type1')
headers = [th.get_text(strip=True) for th in header_row.find_all('th')]

print(headers)

# driver.quit()
  
rows = soup.select('tbody > tr')

data = []

for row in rows:
    no_cell = row.select_one('td.no')
    
    if no_cell:   # 순위가 있는 행만
        cols = row.find_all('td')
        row_data = [col.get_text(strip=True) for col in cols]
        data.append(row_data)

print(data[:2])

# 자동으로 행 인덱스 생성
df = pd.DataFrame(data, columns=headers)

print(df)
  
df.to_excel("naver_finance.xlsx", index=False, engine='openpyxl')

엑셀 저장할 때 행 제거하고 싶은 경우

df.to_excel("naver.xlsx", index=False)
``
profile
파인애플 좋아하세요?

0개의 댓글