크롤링한 소스코드를 정제하는 BeatifulSoup를 활용하여 특정 태그에 접근하고자 할 때 사용할 수 있는 .find()
메서드 시리즈를 알아본다.
개요
requests 또는 Selenium 같은 라이브러리로 특정 페이지의 HTML 소스코드를 가져오거나 API로 XML을 받아온 경우 데이터를 추출하기 위해 원하는 요소(element)에 접근이 필요하다. 이 경우에 사용할 수 있는 것이 BeautifulSoup 라이브러리(이하 bs)로 생성한 객체의 메서드 .find()
와 관련 메서드 시리즈다.
.find()
및 관련 메서드 시리즈는 HTML 또는 XML의 특정 요소에 접근하기 위해서 CSS 선택자(CSS Selector)를 사용하는 .select()
와는 다르게 특정 태그 및 태그 속성을 활용하여 단일 종류의 태그에 접근하는데 사용된다.
즉, 특정 사이트의 HTML 소스코드를 최초에 파싱(parsing)하는데 있어서는 .select()
또는 .select_one()
이 더 효율적이지만, 파싱된 객체에서 특정 태그에 접근하고자 할 때는 .find()
및 관련 메서드 시리즈를 사용하는 것이 더 효율적일 수 있다. 왜냐하면 .find()
메서드 시리즈에는 “string” 인자를 사용하여 특정 문자열을 포함하는 태그에 접근할 수 있기 때문이다.
종류
.find()
및 관련 메서드 시리즈는 총 25개로 목록은 다음과 같으며, 이 게시글에서 다룰 메서드는 .find()
와 .findAll()
이다. 목록 중에서 기능 중복 때문에 따로 다루지 않는 스네이크 형식으로 명명된 메서드는 ❌로 표시하였다.
find 👈🏻
findAll 👈🏻
findAllNext
findAllPrevious
findChild
findChildren
findNext
findNextSibling
findNextSiblings
findParent
findParents
findPrevious
findPreviousSibling
findPreviousSiblings
find_all ❌
find_all_next ❌
find_all_previous ❌
find_next ❌
find_next_sibling ❌
find_next_siblings ❌
find_parent ❌
find_parents ❌
find_previous ❌
find_previous_sibling ❌
find_previous_siblings ❌
예시
다음과 같이 라이브러리와 샘플 코드를 준비한다.
1 | from bs4 import BeautifulSoup as bs |
.find()
단일 태그 찾기
다음과 같이 .find()
메서드를 사용하여 “div” 태그 요소에 접근하는 코드와 그 결과를 보자.
1 | bs_source.find("div") |
결과는 단일 BS 객체로 반환되며 해당 객체는 “div” 태그와 해당 태그의 하위 내용을 포함한다. 별도로 조작하고자 한다면 다음과 같이 할 수 있다.
1 | bs_source_sub = bs_source.find("div") |
속성 활용
.find()
메서드는 “attr” 인자를 사용하여 찾고자 하는 태그의 속성을 지정할 수 있다. 다음과 같이 “data” 속성이 “d2”인 “div” 태그 요소에 접근하는 코드와 그 결과를 보자.
1 | bs_source_sub = bs_source.find("div", attrs = {"data": "d2"}) |
그리고 “div” 태그 요소의 하위 태그인 “p” 태그 요소에 접근하는 코드와 그 결과는 다음과 같다.
1 | bs_source_sub.find("p") |
태그 내부의 문자열 활용
태그 내부의 문자열을 활용하여 태그에 접근하기 위해서는 “string”인자를 활용하여 다음과 같이 할 수 있다.
1 | bs_source.find("div", string = "bbb") |
정규표현식 사용
전체 문자열이 아닌 일부 문자열과 매칭이 되는 경우는 기본적으로 지원하지 않으며, 대안으로 정규표현식을 사용할 수 있다. 그리고 여기서는 “string”인자를 사용하긴 하나 re 라이브러리의 compile()
함수를 사용하여 정규식을 작성해야 하며, 다음과 같이 “b” 문자열을 포함하는 태그에 접근하는 코드와 그 결과를 보자.
1 | bs_source.find("div", string = "b") |
복수 태그 찾기
특정 태그 요소에 접근할 때 두 종류 이상의 태그를 찾고자 할 때는 리스트로 묶어서 다음과 같이 할 수 있다.
1 | bs_source.find(["div", "p"]) |
.findAll()
단일 태그 찾기
다음과 같이 .find_all()
메서드를 사용하여 “div” 태그 요소에 접근하는 코드와 그 결과를 보자.
1 | bs_source.findAll("div") |
결과는 리스트로 반환되며 해당 리스트의 각 원소는 BS이며, “div” 태그와 해당 태그의 하위 내용을 포함한다. 별도로 조작하고자 한다면 다음과 같이 할 수 있다.
1 | bs_source_sub = bs_source.findAll("div") |
속성 활용
.findAll()
메서드도 “attr” 인자를 사용하여 찾고자 하는 태그의 속성을 지정할 수 있다. 다음과 같이 “data” 속성이 “d2”인 “div” 태그 요소에 접근하는 코드와 그 결과를 보자.
1 | bs_source_sub = bs_source.findAll("div", attrs = {"data": "d2"}) |
태그 내부의 문자열 활용
.find()
와 마찬가지로 태그 내부의 문자열을 활용하여 태그에 접근하기 위해서는 “string”인자를 활용하여 다음과 같이 할 수 있다.
1 | bs_source.findAll("div", string = "bbb") |
정규표현식 사용
.find()
와 마찬가지로 정규표현식을 사용할 수 있다. 다음과 같이 “b” 문자열을 포함하는 태그에 접근하는 코드와 그 결과를 보자.
1 | bs_source.findAll("div", string = re.compile("b")) |
복수 태그 찾기
특정 태그 요소에 접근할 때 두 종류 이상의 태그를 찾고자 할 때는 리스트로 묶어서 다음과 같이 할 수 있다.
1 | bs_source.findAll(["div", "p"]) |
대상이 되는 태그가 대상이 되는 태그의 하위 태그일지라도 별도로 뽑혀서 나오게 된다. 이를 인지하지 못한 상태에서 파싱을 하는 경우 자칫 중복된 값을 대량으로 생산해낼 수 있으니 주의하자.