2.2.1 find()와 findAll()
find()와 findAll()은 BeautifulSoup에서 가장 자주 쓰는 함수입니다. 이 함수를 쓰면 HTML 페이지에서 원하는 태그를 다양한 속성에 따라 쉽게 필터링할 수 있습니다.
두 함수는 거의 비슷한데, BeautifulSoup 문서의 함수 정의만 봐도 알 수 있습니다.
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)
실제로 이 함수를 쓸 때는 거의 항상 처음 두 매개변수인 tag와 attributes만 쓰게 될 겁니다. 하지만 매개변수 모두 살펴봅시다.
tag 매개변수는 이미 봤습니다. 태그 이름인 문자열을 넘기거나, 태그 이름으로 이루어진 파이썬 리스트를 넘길 수도 있습니다. 예를 들어 다음 코드는 문서의 모든 헤더 태그 리스트를 반환합니다. - 문서의 모든 제목(h1 ~ h6) 태그 리스트를 얻고자 한다면 더 간결한 방법이 있습니다.2 .4절에서 살펴보겠습니다.
.findAll({"h1","h2","h3","h4","h5","h6"})
attributes 매개변수는 속성으로 이루어진 파이썬 딕셔너리를 받고, 그중 하나에 일치하는 태그를 찾습니다. 예를 들어 다음 함수는 HTML 문서에서 녹색과 빨간색 span 태그를 모두 반환합니다.
.findAll("span", {"class":{"green", "red"}})
recursive 매개변수는 불리언입니다. 문서에서 얼마나 깊이 찾아 들어가고 싶은지를 지정합니다. recursive가 True이면 findAll 함수는 매개변수에 일치하는 태그를 찾아 자식, 자식의 자식을 검색합니다. false이면 문서의 최상위 태그만 찾습니다. 기본적으로 findAll은 재귀적으로(recursive가 True) 동작합니다. 일반적으로 이 옵션은 그대로 두는 것이 좋습니다(원하는 것이 무엇인지 정확히 알고 있으며 성능이 중요한 상황이 아니라면).
text 매개변수는 태그의 속성이 아니라 텍스트 콘텐츠에 일치한다는 점이 좀 다릅니다. 예를 들어 예제 페이지에서 태그에 둘러싸인 ‘the prince’가 몇 번 나타났는지 보려면 이전 예제의 .findAll() 함수를 다음과 같이 고치면 됩니다.
nameList = bsObj.findAll(text="the prince")
print(len(nameList))
출력 결과는 7입니다.
limit 매개변수는 물론 findAll에만 쓰입니다. find는 findAll을 호출하면서 limit을 1로 지정한 것과 같습니다. 이 매개변수는 페이지의 항목 처음 몇 개에만 관심이 있을 때 사용합니다. 이 매개변수는 페이지에 나타난 순서대로 찾으며 그 순서가 원하는 바와 일치한다는 보장은 없으므로 주의하십시오.
keyword 매개변수는 특정 속성이 포함된 태그를 선택할 때 사용합니다.
allText = bsObj.findAll(id="text")
print(allText[0].get_text())
[NOTE_ keyword 매개변수를 쓸 때 주의할 점]
keyword 매개변수는 특정 상황에서 매우 유용할 수 있습니다. 하지만 이 매개변수는 기술적으로는 BeautifulSoup 자체의 기능과 중복되기도 합니다. keyword로 할 수 있는 일은 이 장 후반부에서 설명하는 방법을 써서도 할 수 있습니다.
예를 들어 다음 두 행은 완전히 같습니다.
bsObj.findAll(id="text")
bsObj.findAll("", {"id":"text"})
또한 keyword는 가끔 문제를 일으키는데, 가장 흔한 경우는 class 속성으로 요소를 검색할 때 일어나며 이는 class가 파이썬에서 보호된 키워드이기 때문입니다. 즉, class는 파이썬 예약어(keyword)이므로 변수나 매개변수 이름으로 쓸 수 없습니다(BeautifulSoup.findAll()의 keyword 매개변수와는 상관없습니다). - 파이썬 언어 레퍼런스 문서에서 예약어 목록 전체를 볼 수 있습니다(http://bit.ly/2eDDwVR).
예를 들어 다음 행은 class를 비표준적인 방법으로 사용하므로 문법 에러를 일으킵니다.
bsObj.findAll(class="green")
대신 어설프지만, 밑줄을 추가하는 해결책을 쓸 수 있습니다.
bsObj.findAll(class_="green")
혹은 class를 따옴표 안에 쓰는 방법도 있습니다.
bsObj.findAll("", {"class":"green"})
이쯤에서 이런 의문이 생길 수도 있겠군요. “잠깐. 원하는 속성을 딕셔너리 리스트에 담아서 함수에 전달하면 되지 않나?”
태그 목록을 .findAll()에 속성 목록으로 넘기면 or 필터처럼 동작한다는 점, 즉 태그1, 태그2, 태그3 등이 들어간 모든 태그 목록을 선택하게 된다는 점을 기억하십시오. 태그 목록이 길다면 필요 없는 것들도 잔뜩 선택될 것입니다. keyword 매개변수는 and 필터처럼 동작하므로 그런 문제가 없습니다.
최신 콘텐츠