BS4 활용하기

Intro


이 포스트는 첫 웹 스크레이핑 포스트에 의존합니다. 이전 포스트를 먼저 보시는걸 추천합니다.


BS4 라이브러리를 제대로 활용하는 법을 알아보기 전에 한가지 짚고 넘어가자.
웹은 엉망진창이다. 전 시간에도 말했듯이 데이터 형식과 태그들은 대부분 웹 개발자 마음대로 작성되어 있다. 이런 웹 환경에서 웹 스크레이퍼가 예기치 못한 데이터 형식에 부딪혀 에러를 일으키고는 죽어버리기 일쑤다.

이런 상황에 대비하여 데이터를 긁어오지는 못하더라도 최소한 죽지는 않도록 예외처리를 해 줄 필요가 있다.


예외처리


html = urlopen(“http://www.scraping.com/pages/page1.html”)

여기서 문제가 생길 수 있는 부분은 크게 두 가지 이다.

  • 페이지를 찾을 수 없거나, URL 해석에서 에러가 생긴경우
  • 서버를 찾을 수 없는 경우

첫 번째 상황에서는 HTTP 에러가 반환된다. 404 Page Not Found 라던가 500 Internal Server Error같은 것들 이다. 이런 에러들은 다음과 같이 처리한다.

from urllib.request import urlopen
from urllib.request import HTTPError
from bs4 import BeautifulSoup

try:
  html = urlopen("http://www.scraping.com/pages/error.html")
except HTTPError as e:
  print(e)
  # null을 반환하거나 break 문을 실행하거나 하는 방법을 사용한다.
else:
  # 프로그램을 계속 실행한다. except에서 return이나 break를 사용했다면
  # 이 else문은 필요없다.

스크레이퍼를 만들 때는 코드의 전반적 패턴에 대해 생각해야 예외도 처리하고 읽기도 쉽게 만들 수 있다.
getSiteHtml이나 getTitle같은 범용 함수를 만들고 여기에 예외 처리를 빡세게 만들어두면 재사용하기 쉽고 믿을 수 있는 웹 스크레이퍼를 만들 수 있다.


고오급 분석


BS4를 통해 웹사이트를 분석할 때 CSS 코드는 아주 큰 도움이 된다.

<span class=”green”></span>

그리고

<span class=”red”></span>

이 경우에 클래스를 이용해 쉽게 이 태그들을 구분할 수 있다.
초록색만 수집하고 빨간색은 건너뛸 수도 있다.

find() 와 findAll()


이 두 함수를 통해 HTML 페이지에서 원하는 태그를 다양한 속성에 따라 쉽게 필터링할 수 있다. 함수의 정의는 다음과같다.

findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)

실제로 이 함수를 쓸 때는 거의 항상 처음 두 매개변수인 tag와 attributes만 쓰게 된다. 그래도 각각의 매개변수 모두 알아보자.

tag


tag 매개변수는 이미 봤다. 태그 이름인 문자열을 넘기거나, 태그 이름으로 이루어진 리스트를 넘길 수도 있다.

attributes


attributes 매개변수는 속성으로 이루어진 딕셔너리를 받고, 그중 하나에 일치하는 태그를 찾는다. 예를 들어 다음 함수는 HTML 문서에서 초록색과 빨간색 태그를 모두 반환한다.

.findAll(“span”, {“class”:{“green”, “red”}})

recursive


recursive 매개변수는 불리언타입. 문서에서 얼마나 깊이 찾아 들어가고 싶은지를 정한다. True이면 findAll()함수는 일치하는 태그를 찾아 자식의 자식의 자식까지 파고들어간다. False이면 최상위 태그만 찾는다. 기본값은 True이다.

limit


limit 매개변수는 findAll()에서만 쓰인다. limit을 1로 지정하면 find()함수와 같다. 페이지의 항목 처음 몇 개에만 관심이 있을 때 사용한다.

keyword


keyword 매개변수는 특정 속성이 포함된 태그를 선택할 때 사용한다.

allText = bsObj.findAll(id=”text”)
print(allText[0].get_text())


정규 표현식


정규표현식에 익숙하다면 이것이 얼마나 강력한지 잘 알것이다. 수십줄, 심하면 백몇줄의 복잡한 검색과 필터링 함수를 단 한 줄의 정규 표현식으로 끝낼 수 있다는 건 정말 매력적이다.

간단한 예시로

  • 글자 a를 최소한 한 번 써라
  • 그 뒤에 b를 정확히 다섯 개 써라
  • 그 뒤에 c를 짝수 번 써라
  • 마지막에 d가 있어도 되고 없어도 된다

이 규칙을 따르는 문자열은 다음과같은 정규표현식으로 표현할 수 있다.

aa*bbbbb(cc)*(d | )

이 한줄이면 끝날 것을 조건문으로 필터링하는걸 상상해보면..끔찍

한번 배워두면 아주 유용하기때문에 익혀 두는걸 추천한다.

정규표현식을 step by step 배울수있는 추천 사이트.


다음포스트 - 크롤링 시작하기