Beautiful Soup의 실행
Beautiful Soup에서 가장 널리 쓰이는 객체는 BeautifulSoup 객체이다.
다음 예제를 보자.
from urllib.request import urlopen
from bs4 import BeautifulSoup
html=urlopen("https://pythonscraping.com/pages/page1.html")
bs=BeautifulSoup(html.read(), 'html.parser')
print(bs.h1)
출력 결과는 다음과 같다.
<h1>An Interesting Title</h1>
하나씩 살펴보자.
웹 페이지 주소로 BeautifulSoup 객체 생성하기
urlopen 함수는 웹 페이지 코드를 반환해준다. 따라서 html이라는 변수에는 현재 웹 페이지 코드가 들어있다.
BeautifulSoup 로 객체를 생성한다. 이때 두 가지 매개변수가 들어간다.
첫 번째 매개변수가 되는 것은 객체의 근간이 되는 HTML 텍스트이다.
html.read()를 출력해 보면 웹 페이지 코드를 볼 수 있다.
두 번째 매개변수는 BeautifulSoup가 객체를 만들 때 쓰는 구문 분석기인데, 이 구문 분석기는 우리가 직접 지정할 수 있다.
상기 예제에서 사용한 html.parser는 파이썬3와 함께 설치되므로 따로 설치할 필요 없다.
특별히 다른 분석기가 필요한 경우가 아니라면 이를 계속 사용할 것이다.
lxml 또는 html5lib 도 분석기로 사용할 수 있다. 이것들은 상황에 따라 parser보다 더 좋은 기능을 수행할 수 있지만 별도로 설치해야 한다.
분석기를 거쳐 생성된 BeautifulSoup 객체의 구조는 다음과 같다.
html : <html><head>...</head><body>...</body></html>
- head : <head><title>A Useful Page</title></head>
-- title : <title>A Useful Page</title>
-body : ......
이런 식이다.
상기 예제에서 추출한 <h1> 태그는 객체에서 두 단계만큼 중첩되어있다. 하지만 우리가 객체에서 가져올 때는 h1 태그를 직접 가져왔다. 사실 다음 중 무엇을 사용해도 결과는 같다.
bs.html.body.h1
bs.body.h1
bs.html.h1
웹사이트에서의 크롤링 차단
크롤링을 차단하여 406 에러가 나올 수도 있다.
이때는 유저 정보를 직접 지정하여 크롤링 차단을 우회해야한다.
기본 코드와 우회 코드를 차례대로 보여주겠다.
from urllib.request import urlopen
from bs4 import BeautifulSoup
html=urlopen("https://pythonscraping.com/pages/page1.html")
bs=BeautifulSoup(html.read(), 'html.parser')
import urllib.request
import urllib.parse
from bs4 import BeautifulSoup
headers_info = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'}#유저 정보 직접 지정
html=urllib.request.Request('https://hushinn.tistory.com/manage/newpost/112', headers=headers_info)
bs=BeautifulSoup(urllib.request.urlopen(html).read() , 'html.parser')
예외처리(중요!!)
웹은 엉망징창이다! 데이터 형식은 제대로 지켜지지 않고 웹사이트는 자주 다운되고 닫는 태그도 종종 빠져있다...
웻 스크레이퍼가 예기치 못한 데이터 형식에 부딛혀 멈춰버리는 경우가 매우 많다!
따라서 우리는 웹 스크래이퍼를 만들 때 우리가 목표로 하는 요소에 어떻게 접근할 것인가 뿐 아니라 예상치 못한 오류를 만났을 때 이를 어떻게 처리할 지에 대해서도 깊이 고민해야한다.
앞선 예제를 상기하며 발생할 수 있는 오류에 대해 알아보고, 처리 방법에 대해서도 알아보자.
html=urlopen("https://pythonscraping.com/pages/page1.html")
이 행에서 문제가 생길 수 있는 부분은 크게 두 가지이다.
- 페이지를 찾을 수 없거나, URL 해석에서 에러가 생긴 경우.
- 서버를 찾을 수 없는 경우.
예외처리를 따로 해주지 않는 경우, 이러한 문제를 마주쳤을때 웹 스크레이퍼는 멈춰버리고 만다.
첫 번째 상황에서는 HTTP 에러를 반환할 것이다.
이 에러는 "404 Page Not Found", "500 Internal Server Error" 등이다.
이런 모든 경우에 urlopen 함수는 HTTPError를 일으킨다. 이 예외는 다음과 같이 처리한다.
from urllib.request import urlopen
from urllib.request import HTTPError
from bs4 import BeautifulSoup
try:
html=urlopen("https://pythonscraping.com/pages/page1.html")
except HTTPError as e:
print(e)
#null을 반환하거나, break문을 실행하거나...
else:
#프로그램을 계속 실행한다.
이렇게 코드를 짜주면 HTTP에러 코드가 반환되었을 때, 프로그램은 에러를 출력하고 else문은 실행하지 않을 것이다.
두 번째 상황은 서버를 전혀 찾을 수 없을때 발생한다.
웹사이트가 다운됐거나, URL에 오타가있을때 urlopen은 URLError 예외를 일으킨다.
이것도 캐치하려면 다음과 같은 코드를 사용한다.
from urllib.request import urlopen
from urllib.request import HTTPError
from urllib.request import URLError
from bs4 import BeautifulSoup
try:
html=urlopen("https://pythonscraping.com/pages/page1.html")
except HTTPError as e:
print(e)
except URLError as e:
print('The server could not be found!')
else:
print('It Worked!')
물론 페이지를 서버에서 성공적으로 가져왔어도 페이지 콘텐츠가 예상과 달라 에러가 발생할 수 있다.
따라서 BeautifulSoup 객체에 들어있는 태그에 접근할 때마다 그 태그가 실제 존재하는지 체크하는 편이 좋다.
존재하지 않는 태그에 접근을 시도하면 BeautifulSoup는 None 객체를 반환한다.
여기서 None 객체 자체에 태그가 있다고 가정하고 접근하려 하면 AttributeError가 일어난다.
따라서 애초에 태그에 접근할 때 특정 태그가 존재하는 지를 사전에 검사하며 진행하는 것이 좋다.
예를 들어 앞선 예시의 bs 객체에 nonExistiongTag 라는 태그가 존재할 것이며, 그 하위엔 anotherTag 라는 태그가 존재할 것이라고 예상했다고 가정하자. 그렇다면 어떻게 접근하는 것이 좋을까?
다음 코드와 같이 작성하면 된다.
try:
target_content=bs.nonExistingTag.anotherTag
except AttributeError as e:
print("Tag was not found")#nonExistingTag 가 존재하지 않는 경우
else:
if badContent == None:
print("Tag was not found")#anotherTag 가 존재하지 않는 경우
else:
print(badContent)#정상 상황!!
이렇게 가능한 에러를 모두 체크하고 처리하는 일이 지겹다고 느낄 수 있으나, 코드를 조금만 수정하면 좀 더 쉽게 읽을 수 있다.
다음 코드를 보자.
from urllib.request import urlopen
from urllib.request import HTTPError
from urllib.request import URLError
from bs4 import BeautifulSoup
def GetTitle(url):
try:
html=urlopen(url)
except HTTPError as e:
return None
except URLError as e:
return None
try:
bs=BeautifulSoup(html.read(), 'html.parser')
title=bs.body.h1
except AttributeError as e:
return None
return title
title = GetTitle('https://pythonscraping.com/pages/page1.html')
if title == None:
print('Title could not be found')
else:
print(title)
이 예제에서는 GetTitle 함수에 링크를 전달할 경우 타이틀이 있으면 타이틀을, 타이틀이 없으면 none값을 받을 수 있다.
우리는 웹사이트의 타이틀에 접근해야 할 경우 이 함수에 링크를 전달한 뒤, 결과값이 None인지만 검사하면 바로 타이틀을 취급할 수 있다.
이렇듯 기능을 캡슐화하여 재사용하는 방식으로 스크레이퍼를 제작해야 한다.
에러 처리가 번거롭다고 하여 에러 상황을 배제한 스크레이퍼를 제작하면 결국 크롤링에 문제가 생기고 말 것이다.
'📁study archive > web-crawling' 카테고리의 다른 글
HTML 분석 (0) | 2022.06.16 |
---|
댓글