Crawling

BeautifulSoup4 기본 사용방법 정리

bluebamus 2025. 2. 22.

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

 

댓글