티스토리 뷰

python/웹 크롤링(python)

고급 HTML 분석

취뽀가자!! 2018. 5. 1. 23:25


1. find()와 findAll()

find()와 findAll()은 BeautifulSoup에서 가장 자주 쓰는 함수입니다. 이 함수를 쓰면 HTML 페이지에서 원하는 태그를 다양한 속성에 따라 쉽게 필터링할 수 있습니다.
두 함수는 거의 비슷하지만 다릅니다.

find(tag, attributes, recursive, text, keyword) findAll(tag, attributes, recursive, text, limit, keyword)
tag 매개변수

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

attributes 매개변수

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

findAll("span",{"green":"red"})
recursive 매개변수

recursive 매개변수는 불리언입니다. 문서에서 얼마나 깊이 찾아 들어가고 싶은지를 지정합니다. recursive가 True 이면 findAll 함수는 매개변수에 일치하는 태그를 찾아 자식, 자식의 자식을 검색합니다. false 이면 문서의 최상위 태그만 찾습니다. 기본적으로 findAll의 recursive는 True로 작동합니다. 일반적으로 이 옵션은 그대로 두는 것이 좋습니다.(원하는 것을 정확히 알거나 성능이 중요한 상황이 아니라면)

text 매개변수

text 매개변수는 태그의 속성이 아니라 텍스트 콘텐츠에 일치한다는 점이 좀 다릅니다. 예를 들어 예제 페이지에서 태그에 둘러싸인 'the prince'가 몇 번 나타났는지 보려면 이렇게 작성하면 됩니다.

nameList=bsObj.findAll(text="the prince") print(len(nameList))
limit 매개변수

limit 매개변수는 물론 findAll에만 쓰입니다. find는 findAll을 호출하면서 limit을 1로 지정한 것과 같습니다. 이 매개변수는 페이지의 항목 처음 몇 개에만 관심이 있을 때 사용합니다. 이 매개변수는 페이지에 나타난 순서대로 찾으며 그 순서가 원하는 바와 일치한다는 보장은 없으므로 주의하십시오.

keyword 매개변수

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

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

⇒ get_text()는 출력 시 태그를 제거 후 텍스트만 출력합니다

※ keyword 매개변수 사용 시 주의할 점
keyword 매개변수는 특정 상황에서 매우 유용할 수 있으나 이 매개변수는 기술적으로는 BeautifulSoup 자체의 기능과 중복되기도 합니다. keyword로 할 수 있는 일은 이 장 후반부에서 설명하는 방법을 써서도 할 수 있습니다.

예를 들어 두 행은 완전히 같습니다.

bsObj.findAll(id="text") bsObj.findAll("",{"id":"text"})

또한 keyword는 가끔 문제를 일으키는데, 가장 흔한 경우는 class 속성으로 요소를 검색할 때 일어나며 이는 class가 파이썬에서 보호된 키워드이기 때문입니다. 즉, class는 파이썬 예약어이므로 변수나 매개변수 이름으로 쓸 수 없습니다(BeautifulSoup.findAll()의 keyword 매개변수와는 상관없습니다.)
예를 들어 다음 행은 class를 비표준적인 방법으로 사용하므로 문법 에러를 일으킵니다.

bsObj.findAll(class="green")

대신 어설프지만. 밑줄을 추가하는 해결책을 쓸 수 있습니다.

bsObj.findAll(class_="green")

혹은 class를 따옴표 안에 쓰는 방법도 있습니다.

bsObj.findAll("",{"class":"green"})

사실 "원하는 속성을 딕셔너리 리스트에 담아서 함수에 전달하면 되지 않나?"라는 의문을 가질 수 있습니다. 
태그 목록을 findAll()에 속성 목록으로 넘기면 or 필터처럼 동작한다는 점. 즉 태그 1, 태그 2, 태그 3 등이 들어간 모든 태그 목록을 선택하게 된다는 점을 기억하십시오. 태그 목록이 길다면 필요 없는 것들도 잔뜩 선택될 것입니다. 이런 면에서 keyword 매개변수는 and 필터처럼 동작하므로 그런 문제가 없습니다.

트리 이동

자식과 자손
일반적으로 BeautifulSoup 함수는 항상 현재 선택된 태그의 자손을 다룹니다. 예를 들어 bsObj.body.h1은 body의 자손인 첫 번째 h1 태그를 선택합니다. body 바깥에 있는 태그에 대해서는 동작하지 않습니다.

bsObj.div.findAll("img")는 div 아래 있는 모든 img를 가져옵니다. 자식 img만 원한다면 children을 이용하면 됩니다.

from urllib.request import urlopen from bs4 import BeautifulSoup html=urlopen("http://www.pythonscraping.com/pages/page3.html") bsObj=BeautifulSoup(html,"html.parser") for child in bsObj.find("table",{"id":"giftList"}).children: print(child) #tr들 출력

 이 코드는 giftList 테이블에 들어 있는 제품 행 목록을 출력합니다. children() 대신 descendants() 함수를 썼다면 테이블에 포함된 태그가 20개 이상 출력됐을 테고, 거기에는 img, span, td 태그 등이 모두 포함됐을 겁니다. 자식과 자손의 구별은 중요합니다.

형제 다루기

BeautifulSoup의 next_siblings() 함수는 테이블에서 데이터를 쉽게 수집할 수 있으며, 특히 테이블에 타이틀 행잉 있을 때 유용합니다.

from urllib.request import urlopen from bs4 import BeautifulSoup html=urlopen("http://www.pythonscraping.com/pages/page3.html") bsObj=BeautifulSoup(html,"html.parser") for sibling in bsObj.find("table",{"id":"giftList"}).tr.next_siblings: print(sibling)

이 코드의 출력 결과는 제품 테이블에서 첫 번째 타이틀 행을 제외한 모든 제품 행입니다. 첫 번째 타이틀 행을 제외한 이유는 두 가지입니다.

첫째, 객체는 자기 자신의 형제가 될 수 없습니다.
둘째, 이 함수는 다음 형제만 가져옵니다.

위 코드에서 tr은 자기의 자신의 형제가 될 수 없기 때문에 제외되었습니다.

next_siblings를 보완하는 previous_siblings 함수도 있습니다. 이 함수는 원하는 형제 태그 목록의 마지막에 있는 태그를 쉽게 선택할 수 있을 때 사용합니다.

물론 next_siblings, previous_siblings와 거의 같은 next_sibling, previous_sibling 함수도 있습니다. 이들 함수는 리스트가 아니라 태그 하나만 반환하다는 점을 배면 똑같이 작동합니다.

부모 다루기

.parent와 .parents를 통해 부모를 다룰 수 있습니다.

from urllib.request import urlopen from bs4 import BeautifulSoup html=urlopen("http://www.pythonscraping.com/pages/page3.html") bsObj=BeautifulSoup(html,"html.parser") print(bsObj.find("img",{"src":"../img/gifts/img1.jpg"}).parent.previous_sibling.get_text())












이 코드는 ../img/gifts/img1.jpg 이미지가 나타내는 객체의 가격(이 경우 $15.00)을 출력합니다.

속성에 접근하기

지금까지 태그에 접근하고 필터링하는 법, 그 안에 들어 있는 콘텐츠에 접근하는 법을 알아봤습니다. 그런데 웹 스크레이핑을 하다 보면 태그의 콘텐츠가 아니라 그 속성에 관심이 있을 때가 매우 잦습니다. 특히 태그가 가리키는 URL이 href 속성에 들어 있는 <a>태그, 타켓 이미지가 src 속성에 들어 있는 <img>태그의 경우, 해당 속성에만 관심이 있기 마련입니다.
다음과 같이 태그 객체에서 속성 목록에 접근할 수 있습니다.

from urllib.request import urlopen from bs4 import BeautifulSoup html=urlopen("http://en.wikipedia.org/wiki/kevin_Bacon") bsObj=BeautifulSoup(html,"html.parser") for link in bsObj.findAll("a"): if 'href' in link.attrs: print(link.attrs['href'])myTag.attrs

결과는 전형적인 파이썬 딕셔너리 객체이므로 이들 속성을 가져오거나 조작하기는 매우 쉽습니다. 

람다 표현식

람다 표현식은 간단히 말해 다른 함수에 변수로 전달되는 함수입니다. 다시 말해, 함수를 f(x, y)처럼 정의하지 않고 f(g(x),y), 또는 f(g(x),h(x)) 같은 형태로도 정의할 수 있다는 뜻입니다.

Beautifulsoup에서는 특정 타입의 함수를 findAll 함수에 매개변수로 넘길 수 있습니다. 이들 함수는 반드시 태그 객체를 매개변수로 받아야 하고, 불리언만 반환할 수 있다는 제약만 있습니다. BeautifulSoup는 모든 태그 객체를 이 함수에서 평가하고, true로 평가된 태그는 반환하며 그렇지 않은 태그는 버립니다.

예를 들어 다음 코드는 속성이 정확이 두 개인 태그를 모두 가져옵니다. 

soup.findAll(lambda tag: len(tag.attrs)==2)

그 결과로 예를 들어 다음과 같이 속성이 두 개인 태그들을 찾아낼 것입니다.

<div class="body" id="content"></div> <span style="color:red" class="title"></span>

BeautifulSoup에서 람다 함수를 쓰면 약간의 코드로 정규 표현식을 대체하는 선택자를 만들 수 있습니다.



※출처(파이썬으로 웹크롤러 만들기)


'python > 웹 크롤링(python)' 카테고리의 다른 글

API에서 얻은 데이터와 웹 스크레이퍼 결합하기!!!  (0) 2018.05.05
크롤링의 시작  (0) 2018.05.02
BeautifulSoup 라이브러리  (0) 2018.05.01
urllib 라이브러리  (0) 2018.04.29
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함