ABOUT ME

-

  • 파이썬 크롤링(Crawling) 연습 - BeautifulSoup Documentation #1 (html 태그로 접근, 객체 4종류 등)
    코딩 연습/코딩배우기 2020. 11. 7. 12:00

     

    ■ 파이썬 크롤링 BeautifulSoup Documentation 내용 정리 #1

    html의 Element 구조 탐색(검색), 객체 4종류 (Tag, NavigableString, BeautifulSoup, Comment)

    .contents
    .children
    .string
    .parent
    .paretns
    .next_sibling
    .previous_sibling
    .next_element
    .previous_element

     

    BeautifulSoup은 HTML 및 XML 파일에서 데이터를 추출하는 파이썬 라이브러리이다.
    BeautifulSoup version 4.9.2. 기준

    html_doc = '''<html><head><title>BeautifulSoup Document 기준</title></head>
    <body>
    <p class="title"><b>파이썬 크롤링 연습을 위한 포스팅</b></p>
    
    <p class="story">국회 법제사법위원회, 대검찰청 국정감사 유튜브 영상
    <a href="https://youtu.be/_pOicsZstGo" class="sister" id="link1">김봉현 2차 폭로에 충격! 대검 침묵</a>, 
    <a href="https://youtu.be/AZbCWck7or0" class="sister" id="link2">속속 드러나는 검찰 의혹..김종민 "옵티 계좌추적도 안하고 덮었다.</a> 그리고 
    <a href="https://youtu.be/kkfxzYaJhgI" class="sister" id="link3">김종민 가족수사 돌직구에 현타 온 윤석열</a></p>
    <p class="story">추가적으로, ...</p>
    '''
    
    # 위 html_doc HTML코드와 파서(html.parser)를 인자로 받아 BeautifulSoup 실행하면 BeautifulSoup object를 반환한다.
    
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html_doc, 'html.parser')
    print(type(soup))
    # <class 'bs4.BeautifulSoup'>
    
    print(soup.prettify())  
    # prettify()는 파싱 처리 후 트리 형태로 리턴하는 함수
    '''
    <html>
     <head>
      <title>
       BeautifulSoup Document 기준
      </title>
     </head>
     <body>
      <p class="title">
       <b>
        파이썬 크롤링 연습을 위한 포스팅
       </b>
      </p>
      <p class="story">
       국회 법제사법위원회, 대검찰청 국정감사 유튜브 영상
       <a class="sister" href="https://youtu.be/_pOicsZstGo" id="link1">
        김봉현 2차 폭로에 충격! 대검 침묵
       </a>
       ,
       <a class="sister" href="https://youtu.be/AZbCWck7or0" id="link2">
        속속 드러나는 검찰 의혹..김종민 "옵티 계좌추적도 안하고 덮었다.
       </a>
       그리고
       <a class="sister" href="https://youtu.be/kkfxzYaJhgI" id="link3">
        김종민 가족수사 돌직구에 현타 온 윤석열
       </a>
      </p>
      <p class="story">
       추가적으로, ...
      </p>
     </body>
    </html>
    '''
    
    ### html_doc의 Element 구조를 탐색(검색)해보기 ###
    
    print(soup.title)  ## title 태그 코드
    # <title>BeautifulSoup Document 기준</title>
    
    print(soup.title.name)  ## title 태그명
    # title
    
    print(soup.title.string)  ## title 태그의 문자열
    # BeautifulSoup Document 기준
    
    print(soup.title.parent.name)  ## title 태그의 바로 위 태그
    # head
    
    print(soup.p)  ## p 태그 코드 (첫번째 p 태그)
    # <p class="title"><b>파이썬 크롤링 연습을 위한 포스팅</b></p>
    
    print(soup.p['class'])  ## p 태그의 class 속성 값(리스트로 반환)
    # ['title']
    
    print(soup.a)  ## a 태그 코드 (첫번째 a 태그)
    # <a class="sister" href="https://youtu.be/_pOicsZstGo" id="link1">김봉현 2차 폭로에 충격! 대검 침묵</a>
    
    print(soup.find_all('a'))  ## a 태그를 모두 찾아서 리스트로 반환
    # [<a class="sister" href="https://youtu.be/_pOicsZstGo" id="link1">김봉현 2차 폭로에 충격! 대검 침묵</a>, 
    # <a class="sister" href="https://youtu.be/AZbCWck7or0" id="link2">속속 드러나는 검찰 의혹..김종민 "옵티 계좌추적도 안하고 덮었다.</a>, 
    # <a class="sister" href="https://youtu.be/kkfxzYaJhgI" id="link3">김종민 가족수사 돌직구에 현타 온 윤석열</a>]
    
    print(soup.find(id="link3"))  ## id가 link3인 태그 찾아 반환(class='link3'인 경우 find()는 첫번째 하나만 반환함)
    # <a class="sister" href="https://youtu.be/kkfxzYaJhgI" id="link3">김종민 가족수사 돌직구에 현타 온 윤석열</a>
    
    for link in soup.find_all('a'):  ## 반환된 리스트 객체에 대해 for문 적용
        print(link.get('href'))  ## 속성 href의 값 추출
    # https://youtu.be/_pOicsZstGo
    # https://youtu.be/AZbCWck7or0
    # https://youtu.be/kkfxzYaJhgI
    
    print(soup.get_text())  ## 모든 문자열 추출
    # 파이썬 크롤링 연습을 위한 포스팅
    # 국회 법제사법위원회, 대검찰청 국정감사 유튜브 영상
    # 김봉현 2차 폭로에 충격! 대검 침묵, 
    # 속속 드러나는 검찰 의혹..김종민 "옵티 계좌추적도 안하고 덮었다. 그리고 
    # 김종민 가족수사 돌직구에 현타 온 윤석열
    # 추가적으로, ...
    
    
    for tag in soup.find_all('p'):  ## .attrs는 dict로 속성:속성값 반환
        print(tag.attrs)
    # {'class': ['title']}
    # {'class': ['story']}
    #{'class': ['story']}
        
    for tag in soup.find_all('a'):
        print(tag.attrs)
    # {'href': 'https://youtu.be/_pOicsZstGo', 'class': ['sister'], 'id': 'link1'}
    # {'href': 'https://youtu.be/AZbCWck7or0', 'class': ['sister'], 'id': 'link2'}
    # {'href': 'https://youtu.be/kkfxzYaJhgI', 'class': ['sister'], 'id': 'link3'}

    good4me.co.kr

     

    객체 4종류 (Tag, NavigableString, BeautifulSoup, Comment)

    # Tag
    soup = BeautifulSoup('<b class="boldest">볼드체</b>', 'html.parser')
    tag = soup.b
    print(tag)
    # <b class="boldest">볼드체</b>
    
    print(type(tag))
    # <class 'bs4.element.Tag'>
    
    print(tag.name)
    # b
    
    tag.name = 'blockquote'  ## 태그명 변경
    print(tag)
    # <blockquote class="boldest">볼드체</blockquote>
    
    print(tag.attrs)  ## 태그의 속성(attrs)명과 속성값을 dict로 반환
    # {'class': ['boldest']}
    
    print(tag['class'])  ## dict 키로 접근  
    # ['boldest']
    
    tag = BeautifulSoup('<b id="boldest">BOLD</b>', 'html.parser').b
    print(tag['id'])
    # boldest
    
    print(tag.attrs)
    # {'id': 'boldest'}
    
    
    
    ### 다수의 속성값이 있는 경우 ###
    
    css_soup = BeautifulSoup('<p class="body"></p>', 'html.parser')
    print(css_soup.p['class'])
    # ['body']
    
    css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html.parser')
    print(css_soup.p['class'])
    # ['body', 'strikeout']
    
    id_soup = BeautifulSoup('<p id="my id"></p>', 'html.parser')
    print(id_soup.p['id'])
    # my id
    
    
    rel_soup = BeautifulSoup('<p>홈으로 가기[<a rel="index">Home</a>]</p>', 'html.parser')
    print(rel_soup.a['rel'])
    # ['index']
    
    rel_soup.a['rel'] = ['index', 'contents']
    print(rel_soup.p)
    # <p>홈으로 가기[<a rel="index contents">Home</a>]</p>
    
    soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')
    tag = soup.b
    print(tag.string)
    # Extremely bold
    print(type(tag.string))
    # <class 'bs4.element.NavigableString'>
    unicode_string = str(tag.string)
    print(unicode_string)
    # Extremely bold
    print(type(unicode_string))
    # <class 'str'>
    
    tag.string.replace_with('No longer bold')  ## replace_tith()
    print(tag)
    
    
    ### Comments ###
    markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
    soup = BeautifulSoup(markup, 'html.parser')
    comment = soup.b.string
    print(comment)
    # Hey, buddy. Want to buy a used parser?
    print(type(comment))
    # <class 'bs4.element.Comment'>

     

    ### .contents and .children

    markup = '''
    <html>
    <head>
    <title>The Dormouse's story</title>
    </head>
    '''
    soup = BeautifulSoup(markup, 'html.parser')
    head_tag = soup.head
    print(head_tag)
    #<head>
    #<title>The Dormouse's story</title>
    #</head>
    
    print(head_tag.contents)
    # ['\n', <title>The Dormouse's story</title>, '\n']
    ## 줄바꿈 문자까지 대상으로 포함
    print(len(head_tag.contents))
    # 3
    print(head_tag.contents[0])
    # \n
    print(head_tag.contents[1])
    # <title>The Dormouse's story</title>
    
    
    markup = '''<html><head><title>The Dormouse's story</title></head>'''
    soup = BeautifulSoup(markup, 'html.parser')
    head_tag = soup.head
    print(head_tag.contents)
    # [<title>The Dormouse's story</title>]
    print(len(head_tag.contents))
    # 1
    print(type(head_tag.contents))
    # <class 'list'>
    print(head_tag.contents)
    # [<title>The Dormouse's story</title>]
    print(head_tag.contents[0])
    # <title>The Dormouse's story</title>
    print(head_tag.contents[0].contents)
    # ["The Dormouse's story"]
    
    print(soup.contents)
    # [<html><head><title>The Dormouse's story</title></head></html>]
    print(soup.contents[0])
    # <html><head><title>The Dormouse's story</title></head></html>
    print(soup.contents[0].name)
    # html
    print(soup.contents[0].contents)
    # [<head><title>The Dormouse's story</title></head>]
    
    #print(soup.contents[0].contents.contents)  ## list에 .contents 불가
    # AttributeError: 'list' object has no attribute 'contents'
    for h in soup.contents[0].contents:  ## list에 대해 for문으로 처리
        print(h.contents)  ## == head.contents (soup.head.contents)
    # [<title>The Dormouse's story</title>]
    
    print(soup.contents[0].contents[0].contents)  ## == head.contents
    # [<title>The Dormouse's story</title>]
    print('---',soup.contents[0].contents[0].contents[0])  ## == head.contents[0]
    # <title>The Dormouse's story</title>
    print(soup.contents[0].contents[0].contents[0].contents)  ## == title.contents
    # ["The Dormouse's story"]
    
    for child in soup.contents[0].contents[0].contents[0]:
        print(child)  ## == title.contents[0]
    # The Dormouse's story
    
    print(soup.contents[0].contents[0].children)  ## .children은 generator
    # <list_iterator object at 0x0000016F5351EAC8>
    
    for child in soup.contents[0].children:
        print(child)
    # <head><title>The Dormouse's story</title></head>

     

    ### .string

    markup = '''<html><head><title>The Dormouse's story</title></head>'''
    soup = BeautifulSoup(markup, 'html.parser')
    print(soup.string)
    # The Dormouse's story
    
    ## 태그 내에 하나 이상의 문자열이 있으면, .string은 참조하는 것이 불분명하여 None을 반환함
    markup = '''<html><head><title>The Dormouse's story</title></head><body><p>and ...</p>'''
    soup = BeautifulSoup(markup, 'html.parser')
    print(soup.string)
    # None
    
    ## 태그 내에 하나 이상의 문자열이 있으면, .strings 제너레이터를 사용하여 해결함
    for string in soup.strings:
        print(repr(string))
    #"The Dormouse's story"
    #'and ...' 
    
    ## .strings는 공백문자들이 포함되는데, 이를 제거하기 위해서는 .stripped_strings를 사용함
    markup = '''
    <html>
    <head>
    <title>The Dormouse's story</title>
    </head>
    <body>
    <p>  and ...
     so,    
    </p>'''
    soup = BeautifulSoup(markup, 'html.parser')
    for string in soup.strings:
        print(repr(string))
    
    #"The Dormouse's story"
    #'and ...'
    #'\n'
    #'\n'
    #'\n'
    #"The Dormouse's story"
    #'\n'
    #'\n'
    #'\n'
    #'  and ...\n so,    \n'
        
    for string in soup.stripped_strings:
        print(repr(string))
    
    #"The Dormouse's story"
    #'and ...\n so,'

     

    ### .parent and .paretns

    markup = '''<html><head><title>The Dormouse's story</title></head>'''
    soup = BeautifulSoup(markup, 'html.parser')
    
    print(soup.title)
    # <title>The Dormouse's story</title>
    print(soup.title.parent)
    # <head><title>The Dormouse's story</title></head>
    
    print(soup.title.string)
    # The Dormouse's story
    print(soup.title.string.parent)
    # <title>The Dormouse's story</title>
    
    print(soup.html.parent)
    # <html><head><title>The Dormouse's story</title></head></html>
    print(type(soup.html.parent))
    # <class 'bs4.BeautifulSoup'>
    
    print(soup.title)
    # <title>The Dormouse's story</title>
    print(soup.title.parents)
    # <generator object PageElement.parents at 0x0000016F53617318>
    for parent in soup.title.parents:
        print(parent)
    
    #<head><title>The Dormouse's story</title></head>
    #<html><head><title>The Dormouse's story</title></head></html>
    #<html><head><title>The Dormouse's story</title></head></html>
        
    for parent in soup.title.parents:
        print(parent.name)   
    
    #head
    #html
    #[document]

     

    ### .next_sibling and .previous_sibling

    ## 트리 구조에서 동일 레벨 상의 요소를 사용할 수 있음
    sibling_soup = BeautifulSoup("<a><b>text1</b><c>text2</c></b></a>", 'html.parser')
    print(sibling_soup.prettify())
    #<a>
    # <b>
    #  text1
    # </b>
    # <c>
    #  text2
    # </c>
    #</a>
    
    print(sibling_soup.b.next_sibling)
    # <c>text2</c>
    
    print(sibling_soup.c.previous_sibling)
    # <b>text1</b>
    
    print(sibling_soup.b.previous_sibling)
    # None
    print(sibling_soup.c.next_sibling)
    # None
    
    ## 문자열(string)은 sibling 안됨
    print(sibling_soup.b.string)
    # text1
    print(sibling_soup.b.string.next_sibling)
    # None
    
    
    ## sibling 사용 시, 주의사항
    ## 태그와 태그 사이의 개행이나 콤마 등도 요소로 취급함에 주의!
    markup = '''
    <p>Three sisters</p>
    <a href="#" class="sister" id="link1">Elsie</a>,
    <a href="#" class="sister" id="link2">Lacie</a>,
    <a href="#" class="sister" id="link3">Tillie</a>;
    and they lived at the bottom of a well.
    '''
    soup = BeautifulSoup(markup, 'html.parser')
    print(repr(soup.p.next_sibling))
    # '\n'
    print(soup.p.next_sibling.next_sibling)
    # <a class="sister" href="#" id="link1">Elsie</a>
    
    print(repr(soup.a.next_sibling))
    # ',\n'
    print(repr(soup.a.next_sibling.next_sibling))
    # <a class="sister" href="#" id="link2">Lacie</a>
    
    
    ## .next_siblings and previous_siblings
    ## 태그 sibling 전체 다루기
    print(soup.p.next_siblings)
    # <generator object PageElement.next_siblings at 0x0000016F53615C78>
    for sibling in soup.p.next_siblings:
        print(sibling)
    
    #
    #<a class="sister" href="#" id="link1">Elsie</a>
    #,
    #
    #<a class="sister" href="#" id="link2">Lacie</a>
    #,
    #
    #<a class="sister" href="#" id="link3">Tillie</a>
    #;
    #and they lived at the bottom of a well.
    #
    #<a class="sister" href="#" id="link3">Tillie</a>
    #;
    #and they lived at the bottom of a well.
    #
    
    

     

    ### .next_element and .previous_element

    last_a_tag = soup.find("a", id="link3")
    print(last_a_tag)
    # <a class="sister" href="#" id="link3">Tillie</a>
    print(last_a_tag.next_sibling)
    #;
    #and they lived at the bottom of a well.
    print(last_a_tag.next_element)  ## a 태그의 다음 문장이 아닌 그 자체의 단어임 
    # Tillie
    ## 마크 업에서 <a> 태그, "Tillie", </a> 태그, 세미콜론, 나머지 문장 중에서 
    ## 세미콜론은 <a> 태그와 같은 수준에 있지만 "Tillie"라는 단어가 먼저 발견되었음
    
    
    print(repr(last_a_tag.previous_element))
    # ',\n'
    
    for element in last_a_tag.next_elements:  ## next_elements
        print(repr(element))
    #'Tillie'
    #'\nand they lived at the bottom of a well.\n'
    

     

     

    [참고] Beautiful Soup 4.9.0 documentation https://www.crummy.com/software/BeautifulSoup/bs4/doc/

     

Designed by goodthings4me.