[Web] 웹 스크레이핑 기본 및 예외처리

업데이트:

개요

png

이번 포스팅에서는 웹 크롤링을 위한 html의 기본 구조와 에러(error)의 예외처리 방법에 대해 알아보자.


1. HTML의 기본 구조

예제 html 파일로 다음 url을 사용해보자.

http://pythonscraping.com/pages/page1.html

해당 페이지를 열어 개발자도구(Ctrl+Shift+I)를 보면,

png

위와 같이 객체의 구조를 확인할 수 있다.

from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('http://pythonscraping.com/pages/page1.html')
bs = BeautifulSoup(html, 'html.parser')
print(bs)
<html>
<head>
<title>A Useful Page</title>
</head>
<body>
<h1>An Interesting Title</h1>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</body>
</html>

BeautifulSoup라이브러리로 태그들을 다음과 같이 추출해 낼 수 있다.

단, 각 태그들은 페이지에서 처음에 나타난 태그만 보여준다는 점을 유의하자.

print(bs.head, '\n')
print(bs.title, '\n')
print(bs.h1, '\n')
print(bs.div, '\n')
print(bs.body, '\n')
print(bs.body.h1)
<head>
<title>A Useful Page</title>
</head> 

<title>A Useful Page</title> 

<h1>An Interesting Title</h1> 

<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div> 

<body>
<h1>An Interesting Title</h1>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</body> 

<h1>An Interesting Title</h1>

bs.h1이나 bs.body.h1이나 같은 결과를 보여주는 것처럼 하위 태그로 바로 이동할 수 있는게 정말 좋은 듯하다.


2. 에러 예외처리

2-1. url열때의 error

크롤링을 할때 예외처리를 빈틈없이 파악해 놓는 것이 무적의 크롤러를 만드는 비결이 아닐까 싶다.

url에러는 크게 두가지가 있다.

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

쉽게 말하면 첫번째는 404에러 처럼 서버접속 이후에 발생하는 반면, 두번째는 아에 서버(또는 페이지)에 접속조차 실패한 경우이다.

from urllib.request import HTTPError
from urllib.request import URLError

def open_url(url):
    try:
        html1 = urlopen(url)
    except HTTPError as e:
        print('page not found')
    except URLError as e:
        print('The server could not be found!')
    else:
        print('success!')
open_url('http://pythonscraping.com/pages/error.html')
page not found
open_url('http://pythonscrapingxcczcxzcx.com')
The server could not be found

2-1. 파싱할때의 에러

페이지를 여는데 성공했다고 하더라도, 내용물을 파싱하거나 태그를 가져올때 에러가 발생할 수 있다.

BeautifulSoup는 태그가 존재하지 않을때 None 객체를 반환하는데 이에 다시 태그를 시도하면 AttributeError가 일어나는 것이다.

print(bs.notexist)
print(bs.notexist.something)
None
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-58-dffc4b8ac5b6> in <module>
      1 print(bs.notexist)
----> 2 print(bs.notexist.something)

AttributeError: 'NoneType' object has no attribute 'something'

이렇게 말이다.


3. 예외처리를 이용한 title 가져오기

따라서 위 두가지 경우를 고려하여 html파일의 title을 가져오는 함수를 정의해보자.

from urllib.request import HTTPError
from urllib.request import URLError
from urllib.request import urlopen
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('http://pythonscraping.com/pages/page1.html')
if title == None:
    print('Title could not be found')
else:
    print(title)
<h1>An Interesting Title</h1>

url을 열때 문제가 발생하면 html=None이 되고 Noneread()하려고 할때 AttributeError가 발생할 것이므로 이 경우도 고려가 된 것이다.

웹 크롤러를 만들때 코드의 전반적 패턴에 대해 먼저 생각하는 시간을 갖는 게 중요하다.

댓글남기기