Data Analysis

BeautifulSoup 모듈

yeonny_do 2023. 11. 7. 00:02

■  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

url='https://ko.wikipedia.org/wiki/%EC%9B%B9_%ED%81%AC%EB%A1%A4%EB%9F%AC'

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

res = requests.get('http://www.naver.com')
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

res =requests.get('http://www.tradecampus.com')
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