클론코더

CSS 선택자란? 크롤링에서 원하는 요소만 고르는 법

CSS 선택자(Selector)가 무엇인지, 태그·클래스·아이디·자손 선택자 네 가지 기본형과 크롤링에서 쓰는 법을 연습판으로 직접 눌러 보며 설명합니다. 사이트가 바뀌어도 계속 쓸 수 있는 선택자 고르는 법과 XPath와의 차이도 다룹니다.

클론코더··6분

CSS 선택자(Selector)는 DOM 트리에서 원하는 요소를 가리키는 문법입니다. 원래는 CSS에서 스타일을 적용할 요소를 정하는 데 쓰고, 크롤링에서는 값을 꺼낼 요소를 정하는 데 씁니다.

크롤러가 페이지에서 원하는 글자만 골라낼 수 있는 건, 그 글자가 DOM의 어디에 있는지 선택자로 지정하기 때문입니다.

이 글에서는 선택자의 기본형 네 가지를 연습판에서 직접 입력해 보고, 파이썬 크롤러에서 선택자를 쓰는 방법, 사이트가 바뀌어도 계속 쓸 수 있는 선택자를 고르는 기준까지 살펴봅니다. DOM이 무엇인지 먼저 알고 싶다면 DOM이란? 글부터 읽어 보세요.


CSS 선택자란 무엇인가요?

웹페이지의 가격을 빨간색으로 칠하는 CSS는 이렇게 씁니다.

css
.price {
  color: red;
}

여기서 .price가 선택자입니다. "class="price"가 붙은 요소"를 가리키고, 브라우저는 DOM에서 이 조건에 맞는 요소를 모두 찾아 빨간색을 칠합니다.

크롤러도 같은 문법을 씁니다. 다른 점은 찾은 요소에 색을 칠하는 대신 그 안의 글자를 꺼낸다는 것입니다.

python
soup.select(".price")   # class="price" 인 요소를 모두 찾는다

그래서 CSS를 몰라도 선택자만 알면 크롤러를 만들 수 있고, 반대로 크롤링을 하면서 익힌 선택자는 CSS를 쓸 때도 그대로 쓸 수 있습니다.


선택자의 기본형 네 가지

아래 HTML을 예로 들겠습니다.

html
<h1 id="shop-name">오늘의 책방</h1>
<article class="book">
  <h3>책 제목 1</h3>
  <p class="price">12,000원</p>
</article>
<article class="book">
  <h3>책 제목 2</h3>
  <p class="price">15,000원</p>
</article>

대부분의 요소는 다음 네 가지로 고를 수 있습니다.

기본형쓰는 법예고르는 요소
태그태그 이름 그대로h3책 제목 두 개
클래스점(.) + 클래스 이름.price가격 두 개
아이디샵(#) + 아이디#shop-name책방 이름 하나
자손두 선택자를 띄어 쓴다article h3article 안에 있는 h3

아이디는 한 페이지에 하나만 있어야 하므로 #으로 고르면 요소가 하나만 나옵니다. 클래스는 여러 요소에 같은 이름을 붙일 수 있어서 .으로 고르면 여러 개가 나올 수 있습니다.

기본형은 이어 붙여 조건을 좁힐 수 있습니다.

쓰는 법예뜻
태그와 클래스를 붙여 쓰기p.priceclass="price"인 p
> 로 잇기article > h3article의 바로 아래 자식인 h3
대괄호a[href]href 속성이 있는 a
쉼표로 나열h3, .priceh3와 .price를 모두

띄어쓰기와 >는 헷갈리기 쉽습니다. 띄어 쓰면 몇 단계 아래에 있든 안에 있기만 하면 고르고, >는 바로 한 단계 아래 자식만 고릅니다.


직접 해 보기: 선택자 연습판

아래 연습판에 선택자를 입력하면 왼쪽 화면과 오른쪽 DOM 트리에서 고른 요소가 함께 강조됩니다. 버튼으로 네 가지 기본형을 차례로 눌러 본 뒤, .book .price처럼 직접 조합해 보세요. 찾는 과정 보기를 누르면 DOM 트리의 요소를 위에서부터 하나씩 선택자와 비교하는 순서가 나옵니다.

직접 해 보기선택자 연습판

h3를 고른 상태에서 사이트 디자인 바꾸기를 눌러 보세요. 화면은 거의 그대로인데 아무것도 선택되지 않습니다. 이 내용은 아래 사이트가 바뀌어도 계속 쓸 수 있는 선택자에서 다시 다룹니다.


크롤링에서는 선택자를 어떻게 쓰나요?

파이썬에서는 BeautifulSoup의 select()와 select_one()에 선택자를 넣습니다. 크롤링 연습용으로 공개된 책방 사이트(books.toscrape.com)에서 책 제목과 가격을 가져와 보겠습니다.

books.py
import requests
from bs4 import BeautifulSoup
 
res = requests.get("https://books.toscrape.com/")
soup = BeautifulSoup(res.content, "html.parser")
 
for book in soup.select("article.product_pod"):        # 책 한 권씩
    title = book.select_one("h3 a")["title"]            # 제목 (속성)
    price = book.select_one(".price_color").get_text()  # 가격 (글자)
    print(title, price)
실행 결과
A Light in the Attic £51.77
Tipping the Velvet £53.74
Soumission £50.10
...

눈여겨볼 점이 세 가지 있습니다.

  1. select()는 목록, select_one()은 하나를 돌려줍니다. select()는 찾은 요소가 없으면 빈 목록 []을, select_one()은 None을 돌려줍니다.
  2. 찾은 요소 안에서 다시 고를 수 있습니다. book.select_one(...)처럼 쓰면 페이지 전체가 아니라 그 책 한 권 안에서만 찾기 때문에, 제목과 가격이 다른 책과 섞이지 않습니다.
  3. 글자는 get_text(), 속성은 ["이름"] 으로 꺼냅니다. 이 사이트는 화면에 긴 제목을 줄여서 보여 주고, 전체 제목은 a 태그의 title 속성에 넣어 두었습니다.

requests의 결과를 res.text가 아니라 res.content로 넘긴 데도 이유가 있습니다. 이 사이트는 응답에 글자 인코딩을 따로 알려 주지 않아서, res.text로 받으면 가격이 £51.77처럼 깨집니다. res.content로 넘기면 BeautifulSoup이 HTML 안의 인코딩 정보를 보고 바르게 읽습니다.

선택자를 크롤러에 넣기 전에 브라우저에서 시험하기

선택자가 맞는지는 코드를 돌려 보기 전에 브라우저에서 먼저 확인할 수 있습니다. 개발자 도구의 Console 패널에서 다음처럼 입력하면 그 선택자로 찾은 요소가 모두 나옵니다.

js
document.querySelectorAll(".price_color")

다만 브라우저에서 찾았다고 크롤러에서도 찾는다는 보장은 없습니다. 브라우저는 JavaScript가 고친 뒤의 DOM에서 찾고, requests로 받은 HTML에는 그 내용이 없을 수 있기 때문입니다. 이 차이는 DOM이란? 글에서 다뤘습니다.


사이트가 바뀌어도 계속 쓸 수 있는 선택자

잘 돌던 크롤러가 어느 날 빈 값을 돌려준다면, 대부분 사이트가 HTML 구조를 바꾼 경우입니다. 웹페이지는 사람이 보는 화면을 기준으로 만들기 때문에, 화면만 그대로면 태그나 클래스 이름은 언제든 바뀔 수 있습니다. 앞의 연습판에서 본 것처럼요.

바뀌는 것을 막을 수는 없지만, 오래 쓸 수 있을 가능성이 큰 선택자를 고를 수는 있습니다.

  1. 순서로 고르지 않습니다. li:nth-child(3)처럼 "세 번째"를 지정하면 앞에 요소 하나만 추가돼도 다른 요소를 가져옵니다. 빈 값이 아니라 틀린 값이 나오기 때문에 알아차리기도 어렵습니다.
  2. 자동으로 만든 이름은 피합니다. css-1x9f2k처럼 뜻 없는 글자가 섞인 클래스는 개발 도구가 만든 이름이라, 사이트를 새로 배포할 때마다 바뀔 수 있습니다.
  3. 사람이 뜻을 담아 붙인 이름을 씁니다. #price, .product-title, .price_color 같은 이름은 디자인을 고친 뒤에도 남아 있는 경우가 많습니다.

크롬 개발자 도구에서 요소를 오른쪽 버튼으로 눌러 Copy → Copy selector를 고르면 선택자를 자동으로 만들어 줍니다. 편리하지만, 결과가 #default > div > ... > li:nth-child(1) > article > h3 > a처럼 순서와 긴 경로로 되어 있는 경우가 많습니다. 그대로 쓰기보다는 위 기준으로 줄여서 쓰는 편이 좋습니다.


CSS 선택자와 XPath는 무엇이 다른가요?

크롤링 자료를 찾다 보면 XPath도 자주 보입니다. XPath도 DOM에서 요소를 고르는 문법이지만 생김새가 다릅니다.

CSS 선택자XPath
모양article h3 a//article//h3/a
글자 내용으로 찾기안 된다//a[contains(text(), "Next")]처럼 된다
부모 쪽으로 올라가기안 된다..로 된다
쓰는 곳CSS, JavaScript, BeautifulSoup, Selenium, Playwrightlxml, Scrapy, Selenium, Playwright

BeautifulSoup은 XPath를 지원하지 않고 CSS 선택자만 씁니다. 대부분의 크롤링은 CSS 선택자로 충분하고 더 짧게 쓸 수 있습니다. "다음 페이지"처럼 글자로만 구분되는 요소를 골라야 할 때 XPath를 쓰면 됩니다.


정리

  • CSS 선택자는 DOM 트리에서 원하는 요소를 가리키는 문법입니다. CSS와 크롤러가 같은 문법을 씁니다.
  • 기본형은 태그(h3), 클래스(.price), 아이디(#shop-name), 자손(article h3) 네 가지이고, 이어 붙여 조건을 좁힐 수 있습니다.
  • 파이썬에서는 soup.select()(목록)와 soup.select_one()(하나)에 선택자를 넣고, get_text()와 ["속성"]으로 값을 꺼냅니다.
  • 순서나 자동으로 만든 이름 대신 사람이 뜻을 담아 붙인 이름으로 고르면 사이트가 바뀌어도 오래 쓸 수 있습니다.

크롤러가 페이지를 받아 오고, DOM을 만들고, 선택자로 고르고, 저장하는 전체 흐름은 웹 크롤링이란? 글에서 볼 수 있습니다. 같은 내용을 단계별로 눌러 보며 따라가고 싶다면 크롤러는 어떻게 원하는 글자만 골라낼까? 강의를 열어 보세요.