CSS 선택자란? 크롤링에서 원하는 요소만 고르는 법
CSS 선택자(Selector)가 무엇인지, 태그·클래스·아이디·자손 선택자 네 가지 기본형과 크롤링에서 쓰는 법을 연습판으로 직접 눌러 보며 설명합니다. 사이트가 바뀌어도 계속 쓸 수 있는 선택자 고르는 법과 XPath와의 차이도 다룹니다.
CSS 선택자(Selector)는 DOM 트리에서 원하는 요소를 가리키는 문법입니다. 원래는 CSS에서 스타일을 적용할 요소를 정하는 데 쓰고, 크롤링에서는 값을 꺼낼 요소를 정하는 데 씁니다.
크롤러가 페이지에서 원하는 글자만 골라낼 수 있는 건, 그 글자가 DOM의 어디에 있는지 선택자로 지정하기 때문입니다.
이 글에서는 선택자의 기본형 네 가지를 연습판에서 직접 입력해 보고, 파이썬 크롤러에서 선택자를 쓰는 방법, 사이트가 바뀌어도 계속 쓸 수 있는 선택자를 고르는 기준까지 살펴봅니다. DOM이 무엇인지 먼저 알고 싶다면 DOM이란? 글부터 읽어 보세요.
CSS 선택자란 무엇인가요?
웹페이지의 가격을 빨간색으로 칠하는 CSS는 이렇게 씁니다.
.price {
color: red;
}여기서 .price가 선택자입니다. "class="price"가 붙은 요소"를 가리키고, 브라우저는 DOM에서 이 조건에 맞는 요소를 모두 찾아 빨간색을 칠합니다.
크롤러도 같은 문법을 씁니다. 다른 점은 찾은 요소에 색을 칠하는 대신 그 안의 글자를 꺼낸다는 것입니다.
soup.select(".price") # class="price" 인 요소를 모두 찾는다그래서 CSS를 몰라도 선택자만 알면 크롤러를 만들 수 있고, 반대로 크롤링을 하면서 익힌 선택자는 CSS를 쓸 때도 그대로 쓸 수 있습니다.
선택자의 기본형 네 가지
아래 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 h3 | article 안에 있는 h3 |
아이디는 한 페이지에 하나만 있어야 하므로 #으로 고르면 요소가 하나만 나옵니다. 클래스는 여러 요소에 같은 이름을 붙일 수 있어서 .으로 고르면 여러 개가 나올 수 있습니다.
기본형은 이어 붙여 조건을 좁힐 수 있습니다.
| 쓰는 법 | 예 | 뜻 |
|---|---|---|
| 태그와 클래스를 붙여 쓰기 | p.price | class="price"인 p |
> 로 잇기 | article > h3 | article의 바로 아래 자식인 h3 |
| 대괄호 | a[href] | href 속성이 있는 a |
| 쉼표로 나열 | h3, .price | h3와 .price를 모두 |
띄어쓰기와 >는 헷갈리기 쉽습니다. 띄어 쓰면 몇 단계 아래에 있든 안에 있기만 하면 고르고, >는 바로 한 단계 아래 자식만 고릅니다.
직접 해 보기: 선택자 연습판
아래 연습판에 선택자를 입력하면 왼쪽 화면과 오른쪽 DOM 트리에서 고른 요소가 함께 강조됩니다. 버튼으로 네 가지 기본형을 차례로 눌러 본 뒤, .book .price처럼 직접 조합해 보세요. 찾는 과정 보기를 누르면 DOM 트리의 요소를 위에서부터 하나씩 선택자와 비교하는 순서가 나옵니다.
h3를 고른 상태에서 사이트 디자인 바꾸기를 눌러 보세요. 화면은 거의 그대로인데 아무것도 선택되지 않습니다. 이 내용은 아래 사이트가 바뀌어도 계속 쓸 수 있는 선택자에서 다시 다룹니다.
크롤링에서는 선택자를 어떻게 쓰나요?
파이썬에서는 BeautifulSoup의 select()와 select_one()에 선택자를 넣습니다. 크롤링 연습용으로 공개된 책방 사이트(books.toscrape.com)에서 책 제목과 가격을 가져와 보겠습니다.
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
...눈여겨볼 점이 세 가지 있습니다.
select()는 목록,select_one()은 하나를 돌려줍니다.select()는 찾은 요소가 없으면 빈 목록[]을,select_one()은None을 돌려줍니다.- 찾은 요소 안에서 다시 고를 수 있습니다.
book.select_one(...)처럼 쓰면 페이지 전체가 아니라 그 책 한 권 안에서만 찾기 때문에, 제목과 가격이 다른 책과 섞이지 않습니다. - 글자는
get_text(), 속성은["이름"]으로 꺼냅니다. 이 사이트는 화면에 긴 제목을 줄여서 보여 주고, 전체 제목은a태그의title속성에 넣어 두었습니다.
requests의 결과를 res.text가 아니라 res.content로 넘긴 데도 이유가 있습니다. 이 사이트는 응답에 글자 인코딩을 따로 알려 주지 않아서, res.text로 받으면 가격이 £51.77처럼 깨집니다. res.content로 넘기면 BeautifulSoup이 HTML 안의 인코딩 정보를 보고 바르게 읽습니다.
선택자를 크롤러에 넣기 전에 브라우저에서 시험하기
선택자가 맞는지는 코드를 돌려 보기 전에 브라우저에서 먼저 확인할 수 있습니다. 개발자 도구의 Console 패널에서 다음처럼 입력하면 그 선택자로 찾은 요소가 모두 나옵니다.
document.querySelectorAll(".price_color")다만 브라우저에서 찾았다고 크롤러에서도 찾는다는 보장은 없습니다. 브라우저는 JavaScript가 고친 뒤의 DOM에서 찾고, requests로 받은 HTML에는 그 내용이 없을 수 있기 때문입니다. 이 차이는 DOM이란? 글에서 다뤘습니다.
사이트가 바뀌어도 계속 쓸 수 있는 선택자
잘 돌던 크롤러가 어느 날 빈 값을 돌려준다면, 대부분 사이트가 HTML 구조를 바꾼 경우입니다. 웹페이지는 사람이 보는 화면을 기준으로 만들기 때문에, 화면만 그대로면 태그나 클래스 이름은 언제든 바뀔 수 있습니다. 앞의 연습판에서 본 것처럼요.
바뀌는 것을 막을 수는 없지만, 오래 쓸 수 있을 가능성이 큰 선택자를 고를 수는 있습니다.
- 순서로 고르지 않습니다.
li:nth-child(3)처럼 "세 번째"를 지정하면 앞에 요소 하나만 추가돼도 다른 요소를 가져옵니다. 빈 값이 아니라 틀린 값이 나오기 때문에 알아차리기도 어렵습니다. - 자동으로 만든 이름은 피합니다.
css-1x9f2k처럼 뜻 없는 글자가 섞인 클래스는 개발 도구가 만든 이름이라, 사이트를 새로 배포할 때마다 바뀔 수 있습니다. - 사람이 뜻을 담아 붙인 이름을 씁니다.
#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, Playwright | lxml, 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을 만들고, 선택자로 고르고, 저장하는 전체 흐름은 웹 크롤링이란? 글에서 볼 수 있습니다. 같은 내용을 단계별로 눌러 보며 따라가고 싶다면 크롤러는 어떻게 원하는 글자만 골라낼까? 강의를 열어 보세요.