■ BeautifulSoup 모듈
- 문자열 추출방식은 각 페이지 마다 다르게 개발된 웹 페이지에 적용하기 어려움
- 홈페이지 내 데이터를 쉽게 추출할 수 있도록 도와주는 파이썬 외부 라이브러리
- DOM Tree 형태로 구조화된 췝 페이지의 특정 요소를 이용해 데이터를 추출하는 방법이 훨씬 유용
=> BeautifulSoup은 DOM Tree로부터 데이터를 추출해 올 수 있는 다영한 기능 제공
■ BeautifulSoup을 이용한 데이터 추출 및 파싱
- BeautifulSoup 모듈과 외장 파서 등이 살치되어 있어야 함.
■ Parser의 종류
html.parser |
BeautifulSoup(markup,"html.parser") BeautifulSoup('<a></p>',"html.parser") → <a></a> 형태로 강제 변경되어 처리됨 |
- 파이썬이 제공하는 기본 파서 - 괜찮은 속도 |
lxml |
BeautifulSoup(markup,"lxml") BeautifulSoup('<a></p>',"lxml") → <html><body><a></a></body></html> 형태로 강제 변경되어 처리됨 |
- 기본값 - 아주 빠름 - 외부 C 라이브러리 의존 |
xml |
BeautifulSoup(markup,"xml") BeautifulSoup('<a><b/>',"xml") → <?xml version="1.0" encoding="utf-8" ?> <a/> 형태로 강제 변경되어 처리됨 |
- 아주 빠름 - XML 해석기 지원 |
html5lib |
BeautifulSoup(markup,"html5lib") BeautifulSoup('<a><b />',"html5lib") → <html><head></head><body><a><b></b></a></body></html> |
- 웹브라우저와 동일한 방식으로 패이지를 분석하고 유효한 HTML5를 생성 - 매우 느림 |
■ BeautifulSoup을 이용한 데이터의 접근 순서와 데이터 검색 방법
① BeautifulSoup 모듈 import
from bs4 import BeautifulSoup
# 기본 사용법
import requests
from bs4 import BeautifulSoup
res = requests.get(url)
# soup 객체 생성
soup = BeautifulSoup(res.text, 'lxml')
② 태그를 이용한 접근
# 태그를 이용한 접근 가능
print(soup.title.text) # .text : 서브 태그 여부에 상관없이 태그를 제외한 값 반환
print(soup.title.name) # .name : 태그의 이름 반환
print(soup.title.string) # .string : 하부 태그가 없는 경우만 태그를 제외한 문자열
print()
print(soup.footer.ul.li.text)
print()
print(soup.footer.ul.li.find_next_sibling().text) # footer>ul>li의 다음 형제 읽어오기
print()
③ 태그와 속성을 이용한 접근
#태그와 속성을 이용한 접근
print(soup.a) # soup 객체에서 첫 번째로 만나는 a요소 출력
print(soup.a['href']) # 만약 이 속성이 존재하지 않으면 KeyError발생
④ find()를 이용한 태그 내의 다영한 속성을 이용한 접근
# find() 함수를 이용한 태그 내의 다양한 속성을 이용한 접근
print(soup.find('a',attrs = {'title':'구글봇'})) # attrs : attributes (속성)
■ 웹페이지 내에서 원하는 태그 찾기
: 웹 브라우저의 개발 툴 모드(개발자 모드)를 이용하여 우너하는 데이터의 태그를 쉽게 검색할 수 있음
브라우저에서 F12를 눌러 개발자 모드 실행 (F12가 안되면 ctrl+shift+I)
개발자 모드에서 이 모양을 클릭하고, 찾고자하는 부분을 선택
■ 검색
□ 태그 : HTML의 해당 태그에 대한 첫 번째 정보 가져옴
□ find() : HTML의 해당 태그에 대한 첫 번째 정보를 가져옴
□ find_all() : HTML의 해당 태그에 대한 모든 정보를 리스트 형식으로 가져옴, limit옵션으로 개수 지정 가능
# find_all()
import requests
from bs4 import BeautifulSoup
soup = BeautifulSoup(res.text,'lxml')
print(soup.find_all('a',limit=2))
#[<a href="#topAsideButton"><span>상단영역 바로가기</span></a>, <a href="#shortcutArea"><span>서비스 메뉴 바로가기</span></a>]
result = soup.find_all('span',attrs={'class':'blind'})
print(len(result))
print(result)
# 3
# [<span class="blind">검색</span>, <span class="blind">입력도구</span>, <span class="blind">자동완성/최근검색어펼치기</span>]
[실습] news.naver.com 에서 언론사목록 가져오기
# 방법 1)
import requests
from bs4 import BeautifulSoup
res = requests.get(url)
soup = BeautifulSoup(res.text,'lxml')
channels = soup.find_all('h4',attrs={'class':'channel'})
print(len(channels)) #157
print(channels[0].text) #KBS11월 06일 21:58
import re
p = re.compile('(?<=<h4 class="channel">)\w+')
broad_list =[]
for channel in channels:
m =p.search(str(channel))
if m:
broad_list.append(m.group())
print(len(broad_list)) # 157
print(broad_list[:5]) # ['KBS', '국제신문', '시사IN', '헬스조선', '월간산']
# 방법 2)
import requests
from bs4 import BeautifulSoup
res = requests.get(url)
soup = BeautifulSoup(res.text,'lxml')
result = soup.find_all('h4',attrs={'class':'channel'})
news_list = [list(tag.children)[0] for tag in result]
print(news_list[:10])
#['데일리안', '한국경제', '아이뉴스24', '헤럴드경제', '아시아경제', '뉴시스', '지디넷코리아', '오마이뉴스', '뉴스1', '주간경향']
□ CSS 속성으로 필터링 (class_ 로 클래스를 직접 사용 혹은 attrs에서 속성=값으로 필터링)
print(soup.find('a',attrs = {'title':'구글봇'})) # attrs : attributes (속성)
□ string으로 검색(해당 값이 있는지 없는지 검사할 때 활용, 정규 표현식과 함께 활용
print(soup.find_all(string='자동완성 끄기'))
# ['자동완성 끄기']
□ select_one(), select() : CSS선택자를 활용하여 원하는 정보를 가져옴(태그를 검색하는 find, find_all과 유사)
import requests
from bs4 import BeautifulSoup
soup = BeautifulSoup(res.text,'html.parser')
# 무역아카데미 홈페이지 '공지사항'
print(soup.select_one("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > div > h3"))
# <h3 class="tit">공지사항</h3>
# tradecampus.com 메인 페이지 공지사항 2번째 내용 가져오기
# ul : unlist -> 순서가 없는 목록
# ul > li : 보통 li태그의 상위 태그에는 ul이 존재함
notice_2 = soup.select("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > ul > li:nth-child(2)")
print(notice_2)
# [<li>
# <span class="ico_badge">오프라인/Live</span>
# <p class="tit_notice">
# <a href="/page/user_academy_service_notice?post_seq=20018">제1기 전자상거래(B2C)수출실무 첫걸음 개강안내(10/27)</a>
# <span class="date">2023-10-24</span>
# </p>
# </li>]
print(len(notice_2)) # 1
print(notice_2[0].text)
# 오프라인/Live
# 제1기 전자상거래(B2C)수출실무 첫걸음 개강안내(10/27)
# 2023-10-24
■ CSS 선택자
: CSS에서 선택자는 스타일을 지정하려는 요소를 선택하는데 사용되는 패턴임
.class |
.intro |
class='intro'를 갖는 모든 요소 선택 |
.class1.class2 |
.name1.name2 |
클래스 속성 내에서 name1과 name2가 모두 설정된 모든 요소 선택 |
.class1 .class2 |
.name1 .name2 |
name1을 가진 요소의 하위 요소 중 name2를 가진 모든 요소 선택 |
#id |
#firstname |
id="firstname"를 갖는 요소 선택 |
element |
p |
모든 <p> 요소 선택 |
element.class |
p.intro |
class='intr'를 갖는 모든 <p>요소 |
element, element |
div, p |
모든 <div> 요소와 모든 <p>요소 선택 |
element element |
div p |
<div> 요소 내의 모든 <p> 요소 선택 |
element>element |
div>p |
상위 요소가 <div> 요소인 모든<p>요소 선택 |
[attribute=value] |
[target="_blank'] |
target = '_blank' 속성을 갖는 모든 요소 선택 |
:nth-child(n) |
p:nth-child(2) |
부모 요소의 두 번째 자식 요소에 해당하는 모든 <p>요소 선택 |
□ 활용
# :nth-child(n) 을 지우면 모든 li의 요소를 가져옴
#notice = soup.select("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > ul > li:nth-child(2)")
notice = soup.select("body > div > div.wrapper.main_page > div.renew_main > div.col-12 > div > div.renew_main_notice > div > ul > li")
print(len(notice))
for n in notice:
print(n.text)
print('-'*55)
■ 태그 내의 문장 가져오기
□ get_text() : 검색 결과에서 태그를 제외한 텍스트만 출력
# text.get('key') text['key']라고 써도 됨. 딕셔너리에서 value값을 가져오는 방식과 동일
# 고객센터 영역 텍스트 가져오기
service = soup.find('div',attrs={'class':'serviceInfo'})
#print(service.text)
print(service.get_text())
□ get('속성') : 태그 내 해당 속성의 값 출력
# 이미지 추출
ele = soup.find('span',attrs={'class':'img'})
print(ele)
print()
print(ele.find('img').get('src'))
print()
img = [i.get('src') for i in ele.find_all('img')]
print(img)
□ string : 검색 결과에서 태그 안에 또 다른 태그가 없는 경우 해당 내용을 출력.
태그 안에 또 다른 태그가 있는 경우에는 None 반환
□ text : 검색 결과에서 하위 자식 태그의 텍스트까지 문자열로 반환
html = """
<html>
<head>
<title>crawl</title>
</head>
<body>
<p class='a' align='center'>text1</p>
<p class='b' align='center'>text2</p>
<p class='c' align='center'>text3</p>
<div>
<img src='/source' width='300' height='200'>
</div>
</body>
</html>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'html.parser')
result = soup.find('body')
print(result.text)
# text1
# text2
# text3
p_tag = result.find('p')
print(p_tag.string) # text1
■ 자식 노드(태그)들을 반복 가능한 객체로 반환
html = """
<html>
<head>
<title>crawl</title>
</head>
<body>
<p class='a' align='center'>text1</p>
<p class='b' align='center'>text2</p>
<p class='c' align='center'>text3</p>
<div>
<img src='/source' width='300' height='200'>
</div>
</body>
</html>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
contents = soup.find('body')
#print(contents) # body 태그 내의 모든 태그 요소들 출력
for child in contents.children:
print(child)
# <p align="center" class="a">text1</p>
# <p align="center" class="b">text2</p>
# <p align="center" class="c">text3</p>
# <div>
# <img height="200" src="/source" width="300"/>
# </div>
■ 자식을 포한한 부모 태그까지 출력
html = """
<html>
<head>
<title>crawl</title>
</head>
<body>
<p class='a' align='center'>text1</p>
<p class='b' align='center'>text2</p>
<p class='c' align='center'>text3</p>
<div>
<img src='/source' width='300' height='200'>
</div>
</body>
</html>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'html.parser')
contents = soup.find('body')
img_tag = contents.find('img')
#img_tag = soup.find('body').find('img')
print(img_tag) # <img height="200" src="/source" width="300"/>
print()
print(img_tag.parent)
# <div>
# <img height="200" src="/source" width="300"/>
# </div>
■ 특정 부모 태그까지 검색해서 올라가는 방법
html = """
<html>
<head>
<title>crawl</title>
</head>
<body>
<p class='a' align='center'>text1</p>
<p class='b' align='center'>text2</p>
<p class='c' align='center'>text3</p>
<div>
<img src='/source' width='300' height='200'>
</div>
</body>
</html>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'html.parser')
contents = soup.find('body')
img_tag = contents.find('img')
print(img_tag) #<img height="200" src="/source" width="300"/>
print()
print(img_tag.find_parent('body'))
# <body>
# <p align="center" class="a">text1</p>
# <p align="center" class="b">text2</p>
# <p align="center" class="c">text3</p>
# <div>
# <img height="200" src="/source" width="300"/>
# </div>
# </body>
■ 형제 태그 검색
- find_next_sibling() : 바로 다음 형제 태그 검색
- find_next_siblings() : 모든 다음 형제 태그 검색
- find_previous_sibling() : 바로 이전 형제 태그 검색
- find_previous_siblings() : 모든 이전 형제 태그 검색
html = """
<html>
<head>
<title>crawl</title>
</head>
<body>
<p class='a' align='center'>text1</p>
<p class='b' align='center'>text2</p>
<p class='c' align='center'>text3</p>
<div>
<img src='/source' width='300' height='200'>
</div>
</body>
</html>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'html.parser')
p_tag = soup.find('p', attrs={'class':'b'})
print(p_tag) # <p align="center" class="b">text2</p>
print()
print(p_tag.find_next_sibling()) # <p align="center" class="c">text3</p>
print()
print(p_tag.find_next_siblings())
# [<p align="center" class="c">text3</p>, <div>
# <img height="200" src="/source" width="300"/>
# </div>]
Reference : https://sesoc.tistory.com/253