본문 바로가기
코딩 연습/코딩배우기

파이썬 크롤링(Crawling) 연습 - BeautifulSoup 활용 기초

by good4me 2020. 11. 3.

goodthings4me.tistory.com

 

■ BeautifulSoup 활용 기초

O HTML과 XML 파일에서 데이터를 추출해내는 파이썬 라이브러리

O HTMl과 XML의 트리구조를 탐색, 검색, 변경 가능

O 다양한 파서(parser)를 선택하여 이용 가능

O 파서(parser)
  -html.parser : 설치 필요 없음 
  -lxml : 실치 필요, 매우 빠름
  -lxml-xml 또는 xml : 설치 필요, 매우 빠름
  -html5lib : 설치 필요, 웹브라우저 방식으로 파싱(HTML5 생성), 매우 느림

O 활용
  -BeautifulSoup(markup, 'html.parser')
  -BeautifulSoup(markup, 'lxml')
  -BeautifulSoup(markup, 'html.lxml-xml')
  -BeautifulSoup(markup, 'html5lib')
  

 

example = '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Page Title</title>
</head>
<body>
    <h1>Heading 1</h1>
    <p>Paragraph</p>
    <div>
        <a href="www.google.com">google</a>
    </div>
    <div class="class1">
        <p>a</p>
        <a href="www.naver.com">naver</a>
        <p>b</p>
        <p>c</p>
    </div>
    <div id="id1">
        Example page
        <p>g</p>
    </div>
    <h1>Footer</h1>
</body>
</html>
'''

soup = BeautifulSoup(example, 'html.parser')

print(soup.title)
# <title>Page Title</title>  ## title 태그 내역

print(soup.title.name)  ## title 태그 이름
# title

print(soup.title.string)  ## title 태그의 문자열
# Page Title

print(soup.title.parent)  ## title 태그를 포함하고 있는 태그의 내역
#<head>
#<meta charset="utf-8"/>
#<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
#<title>Page Title</title>
#</head>

print(soup.title.parent.name)  ## 부모 태그 이름
# head

print(soup.h1)  ## 첫 번째 h1 태그
# <h1>Heading 1</h1>

print(soup.p)  ## 첫 번째 p 태그
# <p>Paragraph</p>

print(soup.div)  ## 첫 번째 div 태그 내역
#<div>
#<a href="www.google.com">google</a>
#</div>

print(soup.a)  ## 첫 번째 a 태그 내역
# <a href="www.google.com">google</a>

 

good4me.co.kr

 

▷ HTML 태그 검색 - BeautifulSoup의 강력한 메서드

  • find() : 해당 조건에 맞는 하나의 태그를 추출함
  • find_all() : 해당 조건에 맞는 모든 태그를 추출함(ResultSet, list 형태)
  • select() : "CSS 선택자와 같은 형식"으로 선택 가능(list 타입 반환)
## 상기 코드에 이어서

soup_find = soup.find('div')  ## soup.div 와 동일
print(type(soup_find))
# <class 'bs4.element.Tag'>
print(soup_find)  ## 첫 번째 div 태그 내역
# <div>
# <a href="www.google.com">google</a>
# </div>

soup_find_all = soup.find_all('div')  ## div 태그 전체 추출 (list 형태)
print(type(soup_find_all))
# <class 'bs4.element.ResultSet'>
print(soup_find_all)
#[<div>
#<a href="www.google.com">google</a>
#</div>, 
#<div class="class1">
#<p>a</p>
#<a href="www.naver.com">naver</a>
#<p>b</p>
#<p>c</p>
#</div>,
#<div id="id1">
#  Example page
#<p>g</p>
#</div>]

print(type(soup_find_all))
# <class 'bs4.element.ResultSet'>

print(len(soup_find_all))
# 3

print(soup_find_all[1])  ## 리스트 index 1번째 요소 추출
#<div class="class1">
#<p>a</p>
#<a href="www.naver.com">naver</a>
#<p>b</p>
#<p>c</p>
#</div>

find_by_id = soup.find_all('div', {'id':'id1'})  ## 태그와 속성으로 추출(list 타입)
print(find_by_id)
#[<div id="id1">
#        Example page
#        <p>g</p>
#</div>]

find_by_class = soup.find_all('div', {'class':'class1'})  # class 속성으로 추출
print(find_by_class)
#[<div class="class1">
#<p>a</p>
#<a href="www.naver.com">naver</a>
#<p>b</p>
#<p>c</p>
#</div>]


print(soup.a)
# <a href="www.google.com">google</a>

print(soup.a.get('href'))  ## a 태그의 href 속성 값 추출
# www.google.com

print(soup.a['href'])
# www.google.com

print(soup.a.text)  ## a 태그의 문자열 추출
# google

print(soup.a.get_text())
# google

print(soup.find('a'))
# <a href="www.google.com">google</a>

print(soup.find('a').get('href'))
# www.google.com

print(soup.find('a')['href'])
# www.google.com

print(soup.find('a').text)
# google

print(soup.find('a').get_text())
# google

print(soup.find_all('a'))
# [<a href="www.google.com">google</a>, <a href="www.naver.com">naver</a>]

#print(soup.find_all('a').get('href'))  # 에러
# AttributeError: ResultSet object has no attribute 'get'.

for a_tag in soup.find_all('a'):  ## 각 요소별 속성 추출 위해 for문으로 순환
    print(a_tag)
    print(a_tag.get('href'))
    print(a_tag.get_text())

#<a href="www.google.com">google</a>
#www.google.com
#google
#<a href="www.naver.com">naver</a>
#www.naver.com
#naver


for a_tag in soup.find_all('a'):
    print(a_tag)
    print(a_tag['href'])
    print(a_tag.text)

#<a href="www.google.com">google</a>
#www.google.com
#google
#<a href="www.naver.com">naver</a>
#www.naver.com
#naver



## select는 지정 선택자의 모든 내역을 추출함 (list 타입 반환)
sel_id1 = soup.select('div#id1')
print(sel_id1)  ## CSS 선택자 형식으로 추출 id는 #, class는 .
#[<div id="id1">
#        Example page
#        <p>g</p>
#</div>]

print(type(sel_id1))
# <class 'list'>

sel_class1 = soup.select('div.class1')
print(sel_class1)
#[<div class="class1">
#<p>a</p>
#<a href="www.naver.com">naver</a>
#<p>b</p>
#<p>c</p>
#</div>]

print(type(sel_class1))
# <class 'list'>

sel_h1 = soup.select('h1')
print(sel_h1)
# [<h1>Heading 1</h1>, <h1>Footer</h1>]

print(type(sel_h1))
# <class 'list'>

class1_a = soup.select('div.class1 a')  ## class1 태그 내 a 태그 추출 (list)
print(class1_a)
# [<a href="www.naver.com">naver</a>]

print(type(class1_a))
# <class 'list'>

print(class1_a[0].get('href'))  ## list 내 index로 접근하여 href 속성 값 추출
# www.naver.com

 

▷ 다른 예제

national_anthem = '''
<!DOCTYPE html>
<html lang="en">
<head>
    <title>애국가</title>
</head>
<body>
    <div>
        <p id="title">애국가</p>
        <p class="content">
            동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세.<br />
            무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.<br />
        </p>
        <p class="content">
            남산 위에 저 소나무 철갑을 두른 듯 바람서리 불변함은 우리 기상일세.<br />
            무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.<br />
        </p>
        <p class="content">
            가을 하늘 공활한데 높고 구름 없이 밝은 달은 우리 가슴 일편단심일세.<br />
            무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.<br />
        </p>
        <p class="content">
            이 기상과 이 맘으로 충성을 다하여 괴로우나 즐거우나 나라 사랑하세.<br />
            무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.<br />
        </p>                
    </div>
</body>
</html>
'''
                
soup = BeautifulSoup(national_anthem, 'html.parser')
print(soup.find('p', id = 'title').text)
## soup.find('p', {'id' = 'title'}).text 또는 soup.title.text 와 동일
# 애국가

contents = soup.select('p.content')  ## p 태그 class 속성값
for p_tag in contents:
    print(p_tag.get_text())
    

[실행 결과]
#
#동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세.
#무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.
#
#
#남산 위에 저 소나무 철갑을 두른 듯 바람서리 불변함은 우리 기상일세.
#무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.
#
#
#가을 하늘 공활한데 높고 구름 없이 밝은 달은 우리 가슴 일편단심일세.
#무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세.
#
#
#이 기상과 이 맘으로 충성을 다하여 괴로우나 즐거우나 나라 사랑하세.
#무궁화 삼천리 화려 강산 대한 사람 대한으로 길이 보전하세
#
    

 

▷ 인터넷 웹페이지 가져오기

url = 'http://suanlab.com/'
html = urllib.request.urlopen(url)
print(html)
# <http.client.HTTPResponse object at 0x000001914842F978>
html = html.read()
soup = BeautifulSoup(html, 'html.parser')
#print(soup)

labels = soup.find_all('label')
for label in labels:
    print(label.get_text())


[실행 결과]
#[2020-05-20] "인공지능의 보안 위협" 칼럼
#[2020-03-04] "데이터 경제 시대" 칼럼
#[2019-12-25] "마이데이터 시대의 도래 데이터 주권과 새로운 가치" 칼럼
#[2019-09-25] "유튜브 탄생과 크리에이터 시대" 칼럼
#[2019-09-04] "농업으로 들어간 인공지능" 칼럼
#[2019-08-07] "AI시대 지배할 것인가 지배당하며 살 것인가" 칼럼
#[2018-12-30] "파이썬으로 텍스트 분석하기" 책 출판
    

labels = soup.select('#wrapper > section > div > div > div > div > div > label')
## label에서 마우스 우클릭 후 Copy>>Copy selector
for label in labels:
    print(label.get_text())


[실행 결과]
#[2020-05-20] "인공지능의 보안 위협" 칼럼
#[2020-03-04] "데이터 경제 시대" 칼럼
#[2019-12-25] "마이데이터 시대의 도래 데이터 주권과 새로운 가치" 칼럼
#[2019-09-25] "유튜브 탄생과 크리에이터 시대" 칼럼
#[2019-09-04] "농업으로 들어간 인공지능" 칼럼
#[2019-08-07] "AI시대 지배할 것인가 지배당하며 살 것인가" 칼럼
#[2018-12-30] "파이썬으로 텍스트 분석하기" 책 출판

 

▷ 전 세계 웹사이트 순위 스크래핑

from bs4 import BeautifulSoup
import urllib.request
import pandas as pd


url = 'https://www.alexa.com/topsites'
html = urllib.request.urlopen(url).read()
soup = BeautifulSoup(html, 'html.parser')
sites = soup.find_all('div', {'class':'tr site-listing'})

for site in sites:
    rank = site.find('div', class_ = 'td').get_text()
    website = site.select('p > a')[0].get_text()
    infos = site.find_all('div', {'class':'td right'})
    time = infos[0].get_text()
    pageview = infos[1].get_text()
    traffic = infos[2].get_text()
    linking = infos[3].get_text()

    print(rank, website, time, pageview, traffic, linking)


[실행 결과]

1 Google.com 15:25 16.71 0.40% 1,322,590
2 Youtube.com 16:36 9.20 15.00% 1,007,247
3 Tmall.com 7:02 3.77 0.90% 6,224
4 Facebook.com 18:52 8.75 8.60% 2,237,169
5 Baidu.com 9:05 4.55 5.10% 104,502
6 Qq.com 3:38 3.92 2.90% 269,115
7 Sohu.com 3:40 4.61 1.70% 27,065
8 Taobao.com 4:37 3.59 3.30% 25,542
9 360.cn 3:14 4.11 0.40% 15,582
10 Jd.com 3:30 4.39 1.40% 8,785
11 Amazon.com 10:10 9.47 19.70% 368,084
12 Yahoo.com 4:53 4.56 7.90% 323,499
13 Wikipedia.org 3:45 3.02 74.10% 773,868
14 Zoom.us 8:41 3.96 13.40% 3,076
15 Weibo.com 2:58 3.65 2.10% 71,025
16 Sina.com.cn 2:51 3.41 2.50% 46,925
17 Live.com 5:21 5.32 10.40% 42,943
18 Reddit.com 5:31 4.34 30.90% 168,183
19 Netflix.com 4:17 3.19 9.40% 11,000
20 Xinhuanet.com 2:54 5.58 2.50% 34,834
21 Microsoft.com 4:36 3.38 25.80% 240,212
22 Okezone.com 4:00 4.14 7.40% 10,516
23 Vk.com 7:16 3.82 9.30% 127,024
24 Office.com 9:50 9.23 7.00% 5,887
25 Instagram.com 8:45 9.56 13.70% 639,719
26 Csdn.net 3:18 4.93 7.20% 6,556
27 Alipay.com 2:53 3.19 1.40% 3,592
28 Microsoftonline.com 1:14 1.92 2.70% 702
29 Myshopify.com 21:41 14.57 3.10% 128
30 Yahoo.co.jp 8:47 8.21 13.80% 58,916
31 Panda.tv 2:47 5.33 0.80% 372
32 Zhanqi.tv 2:51 5.42 0.80% 352
33 Twitch.tv 6:34 3.41 6.70% 10,022
34 Bongacams.com 2:57 1.62 6.80% 167,978
35 Google.com.hk 3:50 5.37 3.40% 9,726
36 Amazon.in 13:36 11.29 25.80% 6,854
37 Naver.com 14:11 11.20 10.10% 39,418
38 Bing.com 2:46 2.65 6.10% 42,641
39 Apple.com 3:54 3.86 37.50% 178,218
40 Ebay.com 10:01 7.90 16.40% 75,866
41 Aliexpress.com 11:02 9.45 14.80% 19,325
42 Tianya.cn 2:49 5.07 0.90% 5,331
43 Amazon.co.jp 7:45 9.24 17.50% 50,006
44 Stackoverflow.com 6:09 3.87 74.90% 26,660
45 Google.co.in 6:30 12.19 10.00% 17,539
46 Adobe.com 3:57 3.77 24.80% 248,973
47 Livejasmin.com 2:33 1.76 3.20% 11,354
48 Twitter.com 12:57 10.17 11.50% 1,622,153
49 Yandex.ru 6:08 3.82 2.90% 95,164
50 Tribunnews.com 3:40 1.92 61.80% 23,839

 

[참고] 이수안컴퓨터연구소

 

댓글