BeautifulSoup4 매뉴얼 정리
1. BeautifulSoup4 개요
- BeautifulSoup4(이하 BS4)는 파이썬에서 HTML과 XML을 쉽게 파싱할 수 있도록 도와주는 라이브러리이다. 웹 스크래핑, 데이터 추출, HTML 분석 등에 자주 사용된다.
2. 설치 방법
pip install beautifulsoup4
- 추가적으로 lxml 또는 html5lib 파서를 사용할 수도 있다.
pip install lxml html5lib
3. 기본 사용법
1) HTML 파싱하기
- 파서 옵션으로는 'html.parser', 'lxml', 'html5lib' 등이 있으며 각각의 특성과 성능이 다르다.
from bs4 import BeautifulSoup
html = """<html><head><title>Test Page</title></head>
<body><p class='content'>Hello, World!</p></body></html>"""
soup = BeautifulSoup(html, 'html.parser')
print(soup.title.text) # 출력: Test Page
print(soup.p.text) # 출력: Hello, World!
2) XML 파싱하기
xml = """<data>
<item id='1'>First</item>
<item id='2'>Second</item>
</data>"""
soup = BeautifulSoup(xml, 'xml')
print(soup.find('item', {'id': '2'}).text) # 출력: Second
4. 기본 탐색 메서드
1) find
- 조건에 맞는 첫 번째 태그를 반환한다.
# div 태그 중 class가 'content'인 첫 번째 태그를 반환
tag = soup.find('div', class_='content')
print(tag)
2) find_all
- 조건에 맞는 모든 태그를 리스트로 반환한다.
- 매개변수 설명:
- name: 태그 이름 (예: 'div', 'a', 'p' 등)
- attrs: 속성 딕셔너리 (예: {'class': 'header'})
- recursive: 하위 태그까지 검색 여부 (기본값 True)
- string: 문자열 매칭
- 추가 키워드 인자: HTML 속성과 동일한 이름을 사용하되, 파이썬 예약어와 충돌 시 밑줄(_) 추가 (예: class_='some-class')
# 모든 a 태그 중 href 속성이 있는 태그를 리스트로 반환
tags = soup.find_all('a', href=True)
for t in tags:
print(t)
5. CSS 선택자 사용
1) select(css_selector):
- 조건에 맞는 모든 태그를 리스트로 반환한다.
# 예제 1: 태그 이름으로 선택
soup = BeautifulSoup(html, 'html.parser')
print(soup.select('p')) # 모든 <p> 태그 선택
# 예제 2: 클래스명으로 선택
print(soup.select('.intro')) # 클래스가 'intro'인 모든 태그 선택
# 예제 3: 상위태그 > 하위태그 > 하위태그
print(soup.select('div > p > span')) # div 안에 있는 p, 그 안의 span 태그
# 예제 4: 상위태그.클래스이름 > 하위태그.클래스이름
print(soup.select('p.intro > span.number'))
# 예제 5: 아이디로 선택
print(soup.select('#phone'))
# 예제 6: 아이디 > 태그명.클래스명
print(soup.select('#phone > span.number'))
# 예제 7: 태그명[속성=값] 형태 (속성이 존재하는 태그 선택)
print(soup.select('a[href]'))
2) select_one(css_selector):
- 조건에 맞는 첫 번째 태그를 반환한다.
item = soup.select_one('p.intro')
print(item)
6. 부모/자식/형제 탐색
1) 태그 탐색
soup.body # <body> 태그 전체 반환
soup.body.p # <body> 내부의 첫 번째 <p> 태그 반환
2) 자식 태그 탐색 (내부 탐색):
# 현재 태그의 첫 번째 자식 <section> 탐색
section = tag.find('section')
# 현재 태그 내부의 모든 a 태그 탐색
links = tag.find_all('a')
3) 부모 태그 탐색:
# 첫 번째 부모 태그 (예: div) 탐색
parent_div = tag.find_parent('div')
# 모든 상위 태그를 리스트로 반환
all_parents = tag.find_parents()
4) 형제 태그 탐색 / 문서 내 순서 기반 탐색:
# 바로 다음 형제 태그 탐색
next_item = tag.find_next_sibling('li')
# 바로 이전 형제 태그 탐색
prev_item = tag.find_previous_sibling('li')
# 모든 다음 형제 태그 탐색
next_siblings = tag.find_next_siblings()
# 모든 이전 형제 태그 탐색
prev_siblings = tag.find_previous_siblings()
7. 네비게이션 속성
1) .contents
- 문서 내 순서 기반 탐색:해당 태그의 직접 자식들을 리스트로 반환한다.
children = tag.contents
print(children)
2) .children
- 직접 자식들에 대한 반복자(iterator)를 제공한다.
for child in tag.children:
print(child)
3) .descendants
- 태그의 모든 후손(자식, 자식의 자식 등)에 대한 반복자이다.
for descendant in tag.descendants:
print(descendant)
4) .parent
- 바로 상위 태그(부모)를 반환한다.
print(tag.parent)
5) .next_sibling / .previous_sibling
- 바로 옆의 형제 요소를 반환한다.
print(tag.next_sibling)
print(tag.previous_sibling)
6) .string
- 태그 내의 유일한 자식이 문자열일 경우 그 문자열 반환한다 (여러 자식이 있을 경우 None).
print(tag.string)
7) stripped_strings
- 태그 내의 모든 문자열(공백 제거된 상태)을 이터레이터로 반환한다.
for string in tag.stripped_strings:
print(repr(string))
8. 요소 내용 추출 및 수정
1) 텍스트 추출
1. get_text(separator, strip)
- 태그 내부의 모든 텍스트를 하나의 문자열로 추출한다.
text = soup.get_text(separator='\n', strip=True)
print(text)
2) 수정 메서드
1. append(tag_or_string)
- 현재 태그의 자식으로 새로운 태그나 문자열을 추가한다.
new_tag = soup.new_tag('span')
new_tag.string = "추가된 내용"
tag.append(new_tag)
3) insert(index, tag_or_string)
1. 특정 위치에 새로운 태그나 문자열을 삽입한다.
tag.insert(0, "시작에 삽입된 텍스트")
2. insert_before(tag_or_string)
- 현재 태그 바로 앞에 새로운 태그나 문자열을 삽입한다.
tag.insert_before("앞에 삽입된 내용")
3. replace_with(new_tag_or_string)
- 현재 태그를 새로운 태그나 문자열로 교체
tag.replace_with("교체된 내용")
4. extend(iterable)
- 여러 태그나 문자열을 자식으로 추가
tag.extend(["내용1", "내용2"])
5. clear()
- 현재 태그 내부의 모든 내용을 제거
tag.clear()
6. extract()
- 현재 태그를 문서 트리에서 제거하고, 제거한 태그를 반환
extracted = tag.extract()
7. decompose()
- 현재 태그와 그 자손들을 문서에서 완전히 제거하여 메모리에서도 해제한다.
tag.decompose()
8. unwrap()
- 현재 태그를 제거하고, 해당 태그의 자식들을 상위 노드에 직접 포함한다.
tag.unwrap()
9. NavigableString 및 관련 클래스
1) NavigableString
- 문서 내의 텍스트 데이터를 나타내며, 태그의 .string 속성을 통해 접근할 수 있다.
- 특수 문자열 클래스
- Comment: HTML 주석
- CData: CDATA 섹션
- ProcessingInstruction: 처리 지시문
- 이들은 모두 NavigableString을 상속받아 구현된다.
from bs4 import BeautifulSoup
html = "<p>Hello, <b>World!</b></p>"
soup = BeautifulSoup(html, 'html.parser')
text = soup.p.string # 단, p 태그의 자식이 여러 개이면 None 반환
print(text)
10. 기타 유틸리티
1) BeautifulSoupUnicodeDammit
- 입력 문자열의 인코딩을 감지하고 적절한 디코딩을 수행하는 클래스이다. 보통 내부적으로 사용되지만, 인코딩 문제를 다룰 때 참고할 수 있다.
- BeautifulSoup4의 도구로, 알려지지 않은 인코딩을 알아보는 데 도움을 줄 수 있다. 제공된 마크업을 Unicode로 변환하는 클래스를 구현하고 있다.
- 주요 특징
- 인코딩 감지: HTML/XML 문서에 명시된 인코딩 정보(예: meta 태그)가 없거나 신뢰할 수 없는 경우, 여러 인코딩 후보를 시도하여 올바른 인코딩을 감지한다.
- 유니코드 변환: 감지된 인코딩을 기반으로 바이트 데이터를 유니코드 문자열로 변환한다.
- BOM(Byte Order Mark) 처리: BOM이 포함된 경우 이를 올바르게 처리한다.
- 이 클래스는 Beautiful Soup 내부에서 파싱 전에 문서의 인코딩 문제를 처리할 때 주로 사용되지만, 사용자가 직접 인코딩을 처리해야 하는 상황에서도 유용하게 사용할 수 있다.
- 예시 코드
- 다음은 UnicodeDammit를 사용하여 바이트 문자열을 유니코드 문자열로 변환하는 예제이다.
- 코드 설명
1.바이트 데이터 준비:
- raw_data 변수에 UTF-8 BOM이 포함된 바이트 문자열을 준비한다.
(여기서는 "Hello, world! ✓" 문구를 예제로 사용)
2. UnicodeDammit 객체 생성:
- 생성자에 바이트 데이터를 전달하면, 내부적으로 여러 인코딩 후보를 시도하여 적절한 인코딩을 감지하고
unicode_markup 속성에 유니코드 문자열을 저장한다.
3. 결과 출력:
- dammit.original_encoding: 감지된 인코딩 정보를 보여준다.
- dammit.unicode_markup: 변환된 유니코드 문자열을 출력한다.
from bs4 import UnicodeDammit
# 예제 바이트 데이터 (UTF-8 BOM이 포함된 문자열)
raw_data = b'\xef\xbb\xbfHello, world! \xe2\x9c\x93' # "Hello, world! ✓" (UTF-8)
# UnicodeDammit를 사용하여 인코딩 감지 및 유니코드 변환
dammit = UnicodeDammit(raw_data)
# 감지된 원래 인코딩 출력
print("Detected encoding:", dammit.original_encoding)
# 변환된 유니코드 문자열 출력
print("Unicode markup:", dammit.unicode_markup)
2) SoupStrainer
- 문서를 파싱할 때 특정 태그만 대상으로 제한할 수 있다.
from bs4 import SoupStrainer
only_a_tags = SoupStrainer("a")
soup = BeautifulSoup(html, "html.parser", parse_only=only_a_tags)
11. 참고사항
1) 매개변수:
- 대부분의 메서드들은 name, attrs, recursive, string 등의 매개변수를 받아 검색 조건을 상세하게 지정할 수 있다.
- 예: soup.find_all('div', class_='header')는 <div class="header">인 모든 태그를 찾는다.
2) 키워드 인자 사용:
- HTML 속성 이름과 동일한 이름의 키워드 인자를 전달할 수 있으며, 파이썬 예약어와 충돌하는 경우 뒤에 밑줄(_)을 붙여 사용한다.
- 예: class_='some-class'
3) 검색 방식 선택:
- CSS 선택자 (select / select_one)와 전통적인 메서드 (find, find_all) 방식을 상황에 맞게 적절히 선택하여 사용한다.
12. 설정 옵션 및 설명
1) 파서 종류 선택
- BeautifulSoup은 다양한 파서를 지원
soup = BeautifulSoup(html, 'html.parser') # 기본 파서
soup = BeautifulSoup(html, 'lxml') # 빠르고 강력한 파서
soup = BeautifulSoup(html, 'html5lib') # 가장 관대한 파서
2) prettify(): HTML 예쁘게 출력
print(soup.prettify())
13. 실무 활용 예제
1) 웹페이지에서 특정 데이터 크롤링
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# 특정 클래스의 데이터 추출
data = soup.find('div', class_='target-class').text
print(data)
2) 테이블 데이터 추출
html = """<table>
<tr><th>이름</th><th>나이</th></tr>
<tr><td>홍길동</td><td>30</td></tr>
<tr><td>이몽룡</td><td>25</td></tr>
</table>"""
soup = BeautifulSoup(html, 'html.parser')
rows = soup.find_all('tr')[1:] # 첫 번째 행(헤더) 제외
for row in rows:
cols = row.find_all('td')
name, age = cols[0].text, cols[1].text
print(f"이름: {name}, 나이: {age}")
3) 뉴스 기사 제목 크롤링
url = "https://news.example.com"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
articles = soup.find_all('h2', class_='news-title')
for article in articles:
print(article.text)
- reference :
https://beautiful-soup-4.readthedocs.io/en/latest/
https://tedboy.github.io/bs4_doc/
Beautiful Soup Documentation
tedboy.github.io
'Crawling' 카테고리의 다른 글
requests 라이브러리 기본 메뉴얼 정리 (0) | 2025.02.06 |
---|---|
Selenium 사용 매뉴얼 - 기본 사용 방법 정리 (0) | 2025.02.05 |
댓글