BeautifulSoup4 기본 사용방법 정리
1. 개요
- BeautifulSoup4는 HTML 및 XML 문서를 파싱하고 탐색하기 위한 Python 라이브러리이다.
- 주요 특징:
- 복잡한 HTML/XML 문서에서 원하는 데이터를 쉽게 추출
- 다양한 파서를 지원 (내장 html.parser, lxml, html5lib 등)
- DOM 트리 내의 태그, 속성, 텍스트 등에 대해 풍부한 탐색 기능 제공
pip install beautifulsoup4
2. BeautifulSoup 객체 생성
- 문서 전체를 파싱하기 위해 BeautifulSoup 객체를 생성한다.
2.1 생성자 문법
from bs4 import BeautifulSoup
soup = BeautifulSoup(markup, features, builder, parse_only, from_encoding, exclude_encodings, element_classes)
2.2 주요 파라미터
- markup:
- 파싱할 HTML 또는 XML 문자열이다.
- features:
- 사용할 파서(parser)를 지정한다. 예: 'html.parser', 'lxml', 'html5lib' 등
- builder:
- 파서 빌더를 직접 지정할 때 사용한다. (일반 사용에서는 생략)
- parse_only:
- SoupStrainer 객체를 전달하여 필요한 부분만 파싱할 수 있다.
from bs4 import BeautifulSoup, SoupStrainer
only_links = SoupStrainer("a")
soup = BeautifulSoup(html_doc, "html.parser", parse_only=only_links)
for link in soup:
print(link.get('href'))
- from_encoding:
- 입력 문자열의 인코딩을 지정한다.
- exclude_encodings:
- 제외할 인코딩 목록을 지정한다.
- element_classes:
- 기본 Tag 클래스나 NavigableString 클래스를 재정의할 수 있다.
- multi_valued_attributes:
- 다중값 속성(예, class)의 처리 방법 설정이다. (기본적으로 HTML 표준에 따름)
2.3 파서 종류 및 특징
- html.parser
- 기본 제공 (속도는 보통이며, 내장되어 있음)
- lxml
- 매우 빠르고 유연하지만, 외부 C 라이브러리 의존
- HTML: BeautifulSoup(markup, "lxml")
- XML: BeautifulSoup(markup, "lxml-xml")
- html5lib
- 웹 브라우저와 유사한 방식으로 HTML 파싱, 매우 관대하지만 느림
- 사용법: BeautifulSoup(markup, "html5lib")
soup = BeautifulSoup(html_doc, "lxml")
파서 | 설명 | 설치 필요 여부 | 특징 |
"lxml" | 가장 빠르고 강력한 파서 | pip install lxml 필요 | 속도가 빠르고, HTML과 XML 모두 지원 |
"html.parser" | Python 내장 HTML 파서 | 기본 제공 | 속도가 느리지만 추가 설치 필요 없음 |
"html5lib" | 웹 브라우저처럼 HTML 해석 | pip install html5lib 필요 | 가장 정확한 HTML 파싱 가능, 속도는 느림 |
"xml" | lxml을 사용한 XML 파싱 | pip install lxml 필요 | XML을 엄격하게 파싱 |
"lxml-xml" | lxml 기반 XML 파서 (동일) | pip install lxml 필요 | "xml"과 동일 |
"html5lib" | 웹 브라우저 스타일의 HTML 파싱 | pip install html5lib 필요 | 구조가 엉망인 HTML도 정리 가능 |
2.4 예제
html_doc = """
<html>
<head>
<title>샘플 페이지</title>
</head>
<body>
<p class="content">본문 내용</p>
<a href="https://example.com">예제 링크</a>
</body>
</html>
"""
# 내장 파서를 사용하여 객체 생성
soup = BeautifulSoup(html_doc, 'html.parser')
3. 검색(탐색) 메소드
- BeautifulSoup는 DOM 트리 내에서 태그와 내용을 찾기 위한 다양한 메소드를 제공한다.
3.1 find() 메소드
- 설명:
- 조건에 맞는 첫 번째 태그 객체를 반환한다.
- 문법:
tag = soup.find(name, attrs, recursive, text, **kwargs)
- 주요 파라미터:
- name: 찾고자 하는 태그 이름 (문자열 또는 리스트, 정규표현식 등)
- attrs: 딕셔너리 형태의 속성 필터 (예: {"class": "content"})
- recursive: 기본값 True. 하위 모든 태그를 검색할지 여부
- text: 태그 내부 텍스트에 대한 검색 조건 (문자열 또는 정규표현식)
- kwargs: 추가적인 속성 이름과 값 필터 (예: id="main")
- 예제:
title_tag = soup.find('title')
print(title_tag.text) # 출력: 샘플 페이지
3.2 find_all() 메소드
- 설명:
- 조건에 맞는 모든 태그 객체를 리스트로 반환한다.
- 문법:
tags = soup.find_all(name, attrs, recursive, text, limit, **kwargs)
- 추가 파라미터:
- limit: 검색 결과 개수를 제한 (정수)
- 예제:
paragraphs = soup.find_all('p')
for p in paragraphs:
print(p.get_text())
3.3 select() 메소드 (CSS 선택자)
- 설명:
- CSS 선택자를 사용하여 태그를 검색한다.
- 문법:
elements = soup.select(selector, namespaces, limit, **kwargs)
- 주요 파라미터:
- selector: 유효한 CSS 선택자 문자열
- limit: 반환할 요소의 최대 개수
- 예제:
links = soup.select('a[href]')
for link in links:
print(link['href'])
3.4 select_one() 메소드
- 설명:
- CSS 선택자로 검색한 결과 중 첫 번째 요소만 반환한다.
- 예제:
first_link = soup.select_one('a')
print(first_link['href'])
4. 탐색 및 네비게이션
BeautifulSoup는 문서 구조를 따라 부모, 자식, 형제 노드를 탐색할 수 있는 속성과 메소드를 제공한다.
4.1 자식 및 하위 노드
- .contents:
- 해당 태그의 직접적인 자식 요소들을 리스트 형태로 반환합니다. 텍스트 노드도 포함된다
- .children:
- 해당 태그의 직접적인 자식 요소들을 반복자(iterator) 형태로 반환한다. contents와 비슷하지만 메모리를 더 효율적으로 사용할 수 있다.
- .descendants:
- 해당 태그 아래의 모든 하위 요소들을 반복자 형태로 반환한다. 직접적인 자식뿐만 아니라 자식의 자식까지 모든 하위 요소를 포함한다.
- 예제:
for child in soup.body.children:
print(child)
4.2 부모/상위 노드 탐색 메소드
- .parent:
- 해당 태그의 직접적인 부모 요소를 반환한다.
- .parents:
- 해당 태그로부터 최상위 요소까지의 모든 부모 요소들을 반복자 형태로 반환한다.
- .find_parent():
- 조건에 맞는 가장 가까운 상위 태그 하나를 찾아 반환한다.
- .find_parents():
- 조건에 맞는 모든 상위 태그들을 반복자 형태로 반환한다.
- 예제:
p_tag = soup.find('p')
print(p_tag.parent.name) # 예: body
4.3 형제 노드
- .next_sibling: 현재 태그의 바로 다음 형제 요소를 반환한다.
- .next_siblings: 현재 태그 이후의 모든 형제 요소들을 반복자 형태로 반환한다.
- .previous_sibling: 현재 태그의 바로 이전 형제 요소를 반환한다.
- .previous_siblings: 현재 태그 이전의 모든 형제 요소들을 반복자 형태로 반환한다.
- find_next_sibling(): 조건에 맞는 다음 형제 요소를 찾아 반환한다.
- find_previous_sibling(): 조건에 맞는 이전 형제 요소를 찾아 반환한다.
4.4 순차적 요소 탐색 메소드
- find_next(): 현재 요소 이후에서 조건에 맞는 첫 번째 요소를 찾아 반환한다.
- find_all_next(): 현재 요소 이후의 모든 요소 중 조건에 맞는 모든 요소를 찾아 반환한다.
- find_previous(): 현재 요소 이전에서 조건에 맞는 첫 번째 요소를 찾아 반환한다.
- find_all_previous(): 현재 요소 이전의 모든 요소 중 조건에 맞는 모든 요소를 찾아 반환한다.
5. 텍스트 추출 메소드
- 문서 내 텍스트 정보를 추출하는 데 유용한 메소드들입니다.
5.1 .string
- 설명:
- 태그 내부에 단 하나의 NavigableString만 있는 경우 해당 문자열을 반환한다.
- 예제:
title_tag = soup.find('title')
print(title_tag.string) # 출력: 샘플 페이지
5.2 .strings와 .stripped_strings
- .strings:
- 태그 내부의 모든 문자열을 iterator로 반환한다.
- .stripped_strings:
- 공백이 제거된 문자열들을 iterator로 반환합니다.
- 예제:
for string in soup.stripped_strings:
print(string)
5.3 .get_text()
- 설명:
- 태그 내부의 모든 텍스트를 하나의 문자열로 반환한다.
- 주요 파라미터:
- separator: 텍스트를 연결할 때 사용할 구분자 (기본값: 빈 문자열)
- strip: 각 텍스트 양 끝의 공백을 제거할지 여부 (기본값: False)
- types: 반환할 문자열 타입 지정
- 예제:
body_text = soup.body.get_text(separator="\n", strip=True)
print(body_text)
6. DOM 수정 메소드
- 파싱된 문서의 DOM 트리를 수정하는 메소드들이다
6.1 새 태그 및 문자열 추가
6.1.1. .append() / .extend(): 및 .insert()
- .append(tag) / .extend(tag):
- 현재 태그의 자식 목록 끝에 새 태그 또는 문자열을 추가한다.
- .insert(index, tag):
- 특정 인덱스 위치에 새 태그를 삽입한다.
- 예제:
new_tag = soup.new_tag("div", **{"class": "new-div"})
new_tag.string = "새로운 내용"
soup.body.append(new_tag)
6.2 태그 제거
6.2.1. .clear()
- 설명:
- 태그 내부의 모든 자식들을 제거한다.
- 예제:
p_tag = soup.find('p')
p_tag.clear()
6.2.2. .decompose():
- 해당 태그와 그 자손들을 완전히 삭제하며, 메모리에서 해제한다.
6.2.3. .extract():
- 해당 태그를 DOM 트리에서 분리하여 반환한다.
- 예제:
unwanted = soup.find('script')
if unwanted:
unwanted.decompose()
6.3 태그 교체 및 래핑
6.3.1. .replace_with()
- 설명:
- 현재 태그를 다른 태그나 문자열로 교체한다.
- 예제:
old_tag = soup.find('a')
new_tag = soup.new_tag('span')
new_tag.string = "링크 대신 텍스트"
old_tag.replace_with(new_tag)
6.3.2. .wrap(new_tag):
- 현재 태그를 주어진 새 태그로 감싼다.
6.3.3.unwrap():
- 현재 태그의 부모 태그를 제거하고 내부 내용만 남긴다.
- 예제:
p_tag = soup.find('p')
wrapper = soup.new_tag('div', **{'class': 'wrapper'})
p_tag.wrap(wrapper)
# 나중에 감싸고 있는 div를 제거하고 p 태그만 남기기
wrapper.unwrap()
7. 출력 및 인코딩 메소드
- 문서를 문자열로 변환하거나 인코딩할 때 사용한다.
7.1 .prettify()
- 설명:
- 들여쓰기가 적용된 읽기 쉬운 HTML 문자열을 반환한다.
- 예제:
print(soup.prettify())
7.2 .encode()와 .decode(), .decode_contents()
- .encode(encoding="utf-8"):
- HTML 문자열을 지정한 인코딩의 바이트 문자열로 변환한다.
- .decode_contents():
- 태그 내부의 HTML 코드를 문자열로 반환한다.
- .decode():
- 해당 태그 전체의 HTML을 문자열로 반환한다.
- 예제:
html_bytes = soup.encode("utf-8")
inner_html = soup.body.decode_contents()
8. 태그 속성과 기타
- BeautifulSoup 객체의 태그에 직접 접근할 수 있는 속성과 유틸리티 메소드들이다.
8.1 .name
- 설명:
- 태그의 이름을 문자열로 반환한다.
- 예제:
tag = soup.find('p')
print(tag.name) # 출력: p
8.2 .attrs와 .get()
- .attrs:
- 태그의 모든 속성을 딕셔너리로 반환한다.
- .get(attribute, default=None):
- 지정한 속성의 값을 반환하며, 없을 경우 default 값을 반환한다.
- 예제:
a_tag = soup.find('a')
print(a_tag.attrs)
print(a_tag.get('href', '없음'))
9. webdriver-manager 라이브러리 사용법
1) 기본 설치 및 사용 예제
pip install webdriver-manager
2) 간단한 사용 예제 (Chrome의 경우)
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
# ChromeDriverManager()를 호출한 후 install() 메소드로 드라이버 경로를 받아 Selenium에 전달
driver = webdriver.Chrome(ChromeDriverManager().install())
3) 지원되는 브라우저와 해당 클래스
브라우저 | 클래스명 | 모듈 경로 |
Chrome | ChromeDriverManager | webdriver_manager.chrome |
Firefox | GeckoDriverManager | webdriver_manager.firefox |
Edge | EdgeChromiumDriverManager | webdriver_manager.microsoft |
Opera | OperaDriverManager | webdriver_manager.opera |
Internet Explorer | IEDriverManager | webdriver_manager. |
4) Manager 클래스의 생성자(init) 파라미터
파라미터 | 타입 | 기본값 | 설명 | 적용 매니저 클래스 |
version | str | "latest" | 사용할 드라이버 버전을 지정합니다. "latest"로 지정하면 최신 버전을 자동으로 선택하며, 특정 버전(예: "2.46")을 지정할 수도 있습니다. | 모든 Manager 클래스 |
cache_valid_range | int | 1 (일) | 로컬에 다운로드된 드라이버가 유효한 기간(일 수)을 지정합니다. 이 기간 내면 캐시된 파일을 재사용하며, 만료되면 새로 다운로드합니다. | 모든 Manager 클래스 |
path | str | OS별 기본 경로 (예: ~/.wdm) | 드라이버 바이너리가 저장될 로컬 디렉터리를 지정합니다. 필요에 따라 사용자 정의 경로를 설정할 수 있습니다. | 모든 Manager 클래스 |
url | str | 브라우저별 기본 URL | 드라이버를 다운로드할 기본 URL입니다. 일반적으로 기본값을 그대로 사용하지만, 미러 서버를 사용하거나 커스터마이징이 필요한 경우 변경할 수 있습니다. | ChromeDriverManager, OperaDriverManager 등 |
예: Chrome의 경우 "https://chromedriver.storage.googleapis.com" | ||||
latest_release_url | str | "LATEST_RELEASE" (주로 ChromeDriverManager 전용) | 최신 드라이버 버전을 확인하기 위한 엔드포인트입니다. 대부분 기본값을 그대로 사용합니다. | ChromeDriverManager |
log_level | int 또는 str | logging.INFO | 내부 로깅 메시지의 출력 레벨을 지정합니다. 예를 들어, 디버그 정보를 원하면 logging.DEBUG를 사용할 수 있습니다. | 모든 Manager 클래스 |
proxy | str | None | 드라이버 다운로드 시 프록시 서버를 사용할 경우 프록시 주소(예: "http://proxy.example.com:8080")를 지정합니다. | 모든 Ma |
5) 주요 메소드 및 내부 헬퍼 메소드
1. install()
- 용도:
- 로컬 캐시에 드라이버가 없거나 cache_valid_range에 따라 만료된 경우, 지정된 버전 드라이버를 다운로드한다.
- 다운로드가 완료되면 드라이버 실행 파일의 로컬 경로를 반환한다.
- 시그니처:
install() -> str
- 사용 예:
from webdriver_manager.chrome import ChromeDriverManager
driver_path = ChromeDriverManager().install()
# Selenium WebDriver에 전달
driver = webdriver.Chrome(driver_path)
6) 상세 사용 예제
1. 특정 버전의 ChromeDriver 사용
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
# 특정 버전 "2.46"의 ChromeDriver 사용
driver_path = ChromeDriverManager(version="2.46").install()
driver = webdriver.Chrome(driver_path)
2. 커스텀 저장 경로 및 캐시 유효기간 변경
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
# 드라이버를 "/custom/path/to/driver"에 저장하고, 캐시 유효기간을 7일로 설정
driver_path = ChromeDriverManager(path="/custom/path/to/driver", cache_valid_range=7).install()
driver = webdriver.Chrome(driver_path)
3. 프록시 서버를 통한 드라이버 다운로드
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
# 프록시를 사용하여 드라이버 다운로드 (프록시 주소 예시)
driver_path = ChromeDriverManager(proxy="http://proxy.example.com:8080").install()
driver = webdriver.Chrome(driver_path)
4. Firefox(Gecko) 드라이버 사용
from selenium import webdriver
from webdriver_manager.firefox import GeckoDriverManager
# 기본 설정으로 최신 geckodriver 다운로드
driver_path = GeckoDriverManager().install()
driver = webdriver.Firefox(executable_path=driver_path)
10. 기타 정리
1) find_element(), find_elements() 사용 방법
1. 개요
- find_element()
- 역할: DOM(Document Object Model)에서 첫 번째로 일치하는 요소를 찾는다.
- 반환값: 해당 요소(WebElement)를 반환한다.
- 예외: 일치하는 요소가 없으면 NoSuchElementException 예외가 발생한다.
- find_elements()
- 역할: DOM에서 모든 일치하는 요소를 찾는다.
- 반환값: 일치하는 요소들의 리스트(List[WebElement])를 반환한다.
- 특징: 일치하는 요소가 없을 경우 빈 리스트([])를 반환한다.
2. 사용 방법
- Locator(검색 기준) 사용하기
- 두 메서드는 검색 기준(Locator)을 필요로 하며, 일반적으로 Selenium의 By 클래스를 사용하여 지정한다. 대표적인 Locator는 다음과 같다:
- By.ID: 요소의 id 속성을 기준으로 찾기
- By.NAME: 요소의 name 속성을 기준으로 찾기
- By.CLASS_NAME: 요소의 클래스 이름을 기준으로 찾기
- By.TAG_NAME: 요소의 태그 이름을 기준으로 찾기
- By.LINK_TEXT: 링크 텍스트를 기준으로 찾기
- By.PARTIAL_LINK_TEXT: 부분 링크 텍스트를 기준으로 찾기
- By.CSS_SELECTOR: CSS 선택자를 기준으로 찾기
- By.XPATH: XPath 식을 기준으로 찾기
- CSS_SELECTOR 사용방법
1. 엘리먼트 선택 : 이름만 정의한다.
driver.find_elements(By.CSS_SELECTOR, 'a')
2. 클래스 선택 : .으로 정의한다.
driver.find_elements(By.CSS_SELECTOR, '.button')
3. ID 선택 : #으로 정의한다.
driver.find_element(By.CSS_SELECTOR, '#header')
4. 속성 선택 : [속성=값]으로 정의한다.
driver.find_element(By.CSS_SELECTOR, '[name="username"]')
5. 자식 선택 : 공백으로 구분하여 정의한다.
driver.find_elements(By.CSS_SELECTOR, 'ul li')
6. 가상 클래스 및 가상 엘리먼트 선택 : 가상 클래스 또는 가상 엘리먼트를 사용한다.
driver.find_element(By.CSS_SELECTOR, 'a:hover')
- 기본 문법
- find_element() 기본 문법:
element = driver.find_element(By.<LOCATOR_TYPE>, "<value>")
- find_elements() 기본 문법:
elements = driver.find_elements(By.<LOCATOR_TYPE>, "<value>")
3. 사용 예제
- 아래는 Python과 Selenium을 사용한 예제 코드이다.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
# 드라이버 초기화 (예: Chrome)
driver = webdriver.Chrome()
# 웹 페이지 열기
driver.get("https://example.com")
# 3.1. find_element() 예제: ID로 요소 찾기
try:
element = driver.find_element(By.ID, "exampleID")
print("찾은 요소 텍스트:", element.text)
except NoSuchElementException:
print("해당 ID를 가진 요소가 없습니다.")
# 3.2. find_elements() 예제: 클래스 이름으로 여러 요소 찾기
elements = driver.find_elements(By.CLASS_NAME, "exampleClass")
if elements:
for idx, el in enumerate(elements, start=1):
print(f"요소 {idx}의 텍스트:", el.text)
else:
print("해당 클래스 이름을 가진 요소가 없습니다.")
# 작업 후 드라이버 종료
driver.quit()
4. 주의사항 및 팁
- 예외 처리:
- find_element()는 일치하는 요소가 없을 경우 예외(NoSuchElementException)를 발생시키므로, try-except 블록으로 감싸서 예외를 처리하는 것이 좋다.
- find_elements()는 예외를 발생시키지 않고 빈 리스트를 반환하므로, 반환된 리스트의 길이를 체크하는 방식으로 요소 존재 여부를 판단할 수 있다.
- 페이지 로딩 시간 고려:
- 요소가 동적으로 로드되는 경우, WebDriverWait과 expected_conditions를 사용하여 요소가 로드될 때까지 대기할 수 있다.
- 정확한 Locator 선택:
- 가능한 한 고유한 Locator(ID, CSS Selector 등)를 사용하여 의도한 요소를 정확하게 찾아야 한다.
- XPath나 CSS Selector를 사용할 때는 복잡한 구조로 인한 성능 저하에 유의해야 한다.
2) 무한 스크롤 제어
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep
url = 'https://quotes.toscrape.com/scroll'
options = webdriver.ChromeOptions()
options.add_argument('--start-maximized')
options.add_experimental_option('detach', True)
driver = webdriver.Chrome(options=options)
driver.get(url)
def scroll_to_bottom():
driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
sleep(1)
old_height = 0
new_height = driver.execute_script('return document.body.scrollHeight')
while new_height != old_height:
scroll_to_bottom()
old_height = new_height
new_height = driver.execute_script('return document.body.scrollHeight')
quotes = driver.find_elements(By.CSS_SELECTOR, 'div.quote')
print(len(quotes))
3) 동적 페이지에서 특정 요소가 로드될 때까지 기다리는 방법
- WebDriverWait를 사용하면 요소가 로드될 때까지 기다릴 수 있다.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
url = 'https://quotes.toscrape.com/js-delayed'
options = webdriver.ChromeOptions()
options.add_argument('--start-maximized')
options.add_experimental_option('detach', True)
driver = webdriver.Chrome(options=options)
driver.implicitly_wait(3)
driver.get(url)
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.CSS_SELECTOR, 'div.quote'))
)
sleep(2)
quotes = driver.find_elements(By.CSS_SELECTOR, 'div.quote')
print(len(quotes))
4) 복잡한 사용자 상호작용을 자동화하기 위한 메소드 - ActionChains
- 주요 기능:
1. 마우스 동작:
- move_to_element(element): 지정된 요소로 마우스를 이동한다.
- move_to_element_with_offset(element, x_offset, y_offset): 지정한 요소를 기준으로 x, y 오프셋만큼 떨어진 위치로 마우스를 이동한다.
- move_by_offset(x_offset, y_offset): 현재 마우스 위치에서 x, y 오프셋만큼 상대적으로 이동한다.
- click(element): 지정된 요소를 클릭한다.
- click_and_hold(on_element=None): 지정한 요소(또는 현재 마우스 위치)를 클릭한 상태(버튼 누름)로 유지한다.
- release(on_element=None): 이전에 클릭한 상태(누르고 있던 마우스 버튼)를 해제한다. (보통 드래그 앤 드롭의 끝부분에서 사용)
- double_click(element): 지정된 요소를 더블 클릭한다.
- context_click(element): 지정된 요소에서 우클릭(컨텍스트 메뉴)을 수행한다.
- drag_and_drop(source, target): source 요소를 target 요소로 드래그 앤 드롭한다.
- drag_and_drop_by_offset(source, x_offset, y_offset): 소스 요소를 클릭한 상태에서 지정한 x, y 오프셋만큼 이동한 후 마우스 버튼을 해제한다.
2. 키보드 동작:
- send_keys(*keys): 지정된 키를 입력한다.
- key_down(key): 지정된 키를 누른다.
- key_up(key): 지정된 키를 뗀다.
3. 기타 동작:
- pause(seconds): 지정된 시간(초)만큼 대기한다.
- perform(): 체인으로 묶인 모든 동작을 실행한다.
- reset_actions(): 현재 체인에 저장된 모든 동작을 초기화한다. (동작 재사용 전 리셋 시 유용)
- 프로젝트 코드
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
url = 'https://quotes.toscrape.com/js'
options = webdriver.ChromeOptions()
options.add_argument('--start-maximized')
options.add_experimental_option('detach', True)
driver = webdriver.Chrome(options=options)
actions = ActionChains(driver)
driver.get(url)
next_button = driver.find_element(By.CSS_SELECTOR, 'li.next a')
actions.move_to_element(next_button).perform()
next_button.click()
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Chrome()
driver.get("https://example.com")
# 요소 선택 예시
element_a = driver.find_element("id", "start")
element_b = driver.find_element("id", "target")
actions = ActionChains(driver)
# 1. 마우스를 요소 A 위로 이동 후 클릭 후 드래그 앤 드롭
actions.move_to_element(element_a) \
.click_and_hold() \
.move_to_element(element_b) \
.release() \
.pause(0.5)
# 2. 키보드 입력: SHIFT 키 누른 채 "Hello" 입력 후 SHIFT 해제
actions.move_to_element(element_a) \
.click() \
.key_down(Keys.SHIFT) \
.send_keys("hello") \
.key_up(Keys.SHIFT) \
.pause(0.5)
# 3. 마우스 상대 이동 및 우클릭
actions.move_by_offset(100, 50) \
.context_click() \
.pause(0.5)
# 모든 동작 실행
actions.perform()
5) 모든 스크립트가 종료되더라도 크라우저 창이 자동으로 닫히지 않게 하는 방법
- 용도:
- 주로 디버깅 시에 브라우저 상태를 직접 확인하거나, 테스트 후에도 브라우저를 유지하고 싶을 때 사용된다.
options.add_experimental_option('detach', True)
6) 쿠키 수락 배너의 수락 자동 클릭
- 보통 Selenium을 사용하여 웹 페이지에 표시되는 쿠키 수락 배너에서 "수락" 버튼을 자동으로 클릭하는 사용자 정의 함수이다. 이 함수는 웹 자동화 스크립트에서 불필요한 쿠키 배너를 제거하고 테스트를 진행할 수 있도록 도와준다.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
def accept_cookie(
driver,
locator=(By.CSS_SELECTOR, "button.cookie-accept"), # 쿠키 수락 버튼을 찾기 위한 로케이터 (기본값: CSS 선택자)
timeout=10, # 버튼이 나타날 때까지 대기할 최대 시간(초)
poll_frequency=0.5 # 요소를 다시 검사할 간격(초)
):
"""
웹 페이지의 쿠키 배너에서 수락 버튼을 찾아 클릭하는 함수입니다.
Parameters:
- driver: Selenium WebDriver 인스턴스.
- locator: 쿠키 수락 버튼을 찾기 위한 튜플 형식의 로케이터 (예: (By.ID, "accept-cookies") 또는 (By.CSS_SELECTOR, ".cookie-accept-button")).
- timeout: 요소가 나타날 때까지 대기할 최대 시간(초).
- poll_frequency: 요소의 상태를 확인하는 간격(초).
Returns:
- True: 버튼 클릭에 성공한 경우.
- False: 버튼을 찾지 못하거나 클릭에 실패한 경우.
"""
try:
# WebDriverWait를 통해 지정된 시간 동안 요소가 클릭 가능해질 때까지 대기
accept_button = WebDriverWait(driver, timeout, poll_frequency=poll_frequency).until(
EC.element_to_be_clickable(locator)
)
accept_button.click() # 쿠키 수락 버튼 클릭
print("쿠키 수락 버튼 클릭 성공!")
return True
except TimeoutException:
print("쿠키 수락 버튼을 찾지 못했습니다.")
return False
7) 암묵적 대기(implicit wait)와 명시적 대기(Explicit Wait)
1. 개요
- 암묵적 대기 (Implicit Wait):
- 모든 요소 검색(find_element/find_elements)에 대해 전역적으로 적용되는 대기 시간이다.
- 명시적 대기 (Explicit Wait):
- 특정 조건(예: 요소의 존재, 클릭 가능 등)이 충족될 때까지 개별적으로 대기하는 방식이다.
2. 암묵적 대기 (Implicit Wait)
- 개념
- 정의:
- WebDriver가 요소를 찾을 때, 지정한 시간(초) 동안 DOM을 주기적으로 폴링하여 요소가 나타나는지 확인한다.
- 특징:
- 전역 설정: 한 번 설정하면 이후 모든 요소 검색에 적용된다.
- 자동 폴링: 요소를 찾는 동안 설정한 시간 동안 주기적으로 검사합니다.
- 사용법
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # 최대 10초까지 요소 검색 시 대기
driver.get("https://example.com")
# 요소 검색 시, 최대 10초 대기 후 요소 반환
element = driver.find_element("id", "some-element-id")
- 장단점
- 장점:
- 사용법이 간단하며 전역적으로 설정되어 코드 작성이 간편하다.
- 모든 요소 검색에 동일한 대기 시간이 적용되어 일관성을 유지합니다.
- 단점:
- 전역 설정이므로, 모든 요소 검색에 동일한 대기 시간이 적용되어 불필요한 대기 시간이 발생할 수 있다.
- 특정 조건(예: 요소가 클릭 가능해질 때까지 등)에 대한 세밀한 제어가 어렵다.
3. 명시적 대기 (Explicit Wait)
- 개념
- 정의:
- 특정 조건이 만족될 때까지 대기하는 방법으로, WebDriverWait와 expected_conditions를 함께 사용한다.
- 특징:
- 세밀한 제어: 요소의 존재, 가시성, 클릭 가능 여부 등 구체적인 조건을 지정할 수 있다.
- 지역적 적용: 필요한 부분에서만 개별적으로 대기를 적용할 수 있습니다.
- 사용법
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
# 최대 10초 동안 특정 조건(여기서는 ID가 "myElement"인 요소의 존재)을 대기
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "myElement"))
)
- 주요 Expected Conditions 예시
- presence_of_element_located(locator):
- DOM에 요소가 존재하는지 확인한다.
- visibility_of_element_located(locator):
- 요소가 화면에 표시되고 가시적인지 확인한다.
- element_to_be_clickable(locator):
- 요소가 클릭 가능한 상태인지 확인한다.
- text_to_be_present_in_element(locator, text):
- 요소에 특정 텍스트가 포함되어 있는지 확인한다.
- 장단점
- 장점:
- 조건을 구체적으로 지정할 수 있어, 요소의 상태에 따른 대기 시간을 정확하게 제어할 수 있다.
- 특정 상황에만 대기를 적용할 수 있으므로 불필요한 대기를 줄일 수 있다.
- 단점:
- 코드가 다소 복잡해질 수 있으며, 각 요소마다 별도의 대기 설정이 필요하다.
- 전역 대기가 아니므로, 모든 요소에 일괄 적용하기는 어렵다.
4. 암묵적 대기 vs 명시적 대기 비교
파라미터 | 타입 | 기본값 | 설명 | 적용 매니저 클래스 |
version | str | "latest" | 사용할 드라이버 버전을 지정합니다. "latest"로 지정하면 최신 버전을 자동으로 선택하며, 특정 버전(예: "2.46")을 지정할 수도 있습니다. | 모든 Manager 클래스 |
cache_valid_range | int | 1 (일) | 로컬에 다운로드된 드라이버가 유효한 기간(일 수)을 지정합니다. 이 기간 내면 캐시된 파일을 재사용하며, 만료되면 새로 다운로드합니다. | 모든 Manager 클래스 |
path | str | OS별 기본 경로 (예: ~/.wdm) | 드라이버 바이너리가 저장될 로컬 디렉터리를 지정합니다. 필요에 따라 사용자 정의 경로를 설정할 수 있습니다. | 모든 Manager 클래스 |
url | str | 브라우저별 기본 URL | 드라이버를 다운로드할 기본 URL입니다. 일반적으로 기본값을 그대로 사용하지만, 미러 서버를 사용하거나 커스터마이징이 필요한 경우 변경할 수 있습니다. | ChromeDriverManager, OperaDriverManager 등 |
예: Chrome의 경우 "https://chromedriver.storage.googleapis.com" | ||||
latest_release_url | str | "LATEST_RELEASE" (주로 ChromeDriverManager 전용) | 최신 드라이버 버전을 확인하기 위한 엔드포인트입니다. 대부분 기본값을 그대로 사용합니다. | ChromeDriverManager |
log_level | int 또는 str | logging.INFO | 내부 로깅 메시지의 출력 레벨을 지정합니다. 예를 들어, 디버그 정보를 원하면 logging.DEBUG를 사용할 수 있습니다. | 모든 Manager 클래스 |
proxy | str | None | 드라이버 다운로드 시 프록시 서버를 사용할 경우 프록시 주소(예: "http://proxy.example.com:8080")를 지정합니다. | 모든 Ma |
5. 결론
- 암묵적 대기:
- 단순하고 전역적인 요소 검색에 적합하지만, 세밀한 조건 제어가 어렵다.
- 명시적 대기:
- 특정 조건에 맞는 요소 대기 및 제어가 가능하여 복잡한 상황에서 유리하지만, 코드가 복잡해질 수 있다.
- reference :
https://beautiful-soup-4.readthedocs.io/en/latest/#quick-start
'Crawling' 카테고리의 다른 글
[udemy] Web Scraping with BeautifulSoup, Selenium, Scrapy and Scrapy-Playwright. 4 Project-like Exercises + 4 Real Life Projects 학습 정리 (0) | 2025.02.26 |
---|---|
[udemy] Web Scraping with Python: BeautifulSoup, Requests & Selenium 학습 정리 (0) | 2025.02.23 |
BeautifulSoup4 매뉴얼 정리 (0) | 2025.02.06 |
requests 라이브러리 기본 메뉴얼 정리 (0) | 2025.02.06 |
Selenium 사용 매뉴얼 - 기본 사용 방법 정리 (0) | 2025.02.05 |
댓글