requests & BeautifulSoup & Selenium 에 대해 / requests로 웹 스크래핑 하기 (1)

seul_velog·2023년 9월 7일
0

웹 데이터를 가져오기 위한 절차

  1. 타겟 웹사이트 조사: 크롤링하려는 웹사이트의 구조와 URL 패턴을 파악
  2. Request: 웹사이트의 데이터를 가져오기 위해 해당 웹사이트에 요청
  3. HTML 응답 파싱: 받아온 HTML 응답을 파싱하여 필요한 데이터를 추출
  4. 데이터 저장: 추출한 데이터를 원하는 형식으로 저장 (CSV, JSON 등)
  5. 반복: 모든 원하는 데이터를 크롤링할 때까지 2-4단계를 반복




Requests

웹사이트에 접근하여 데이터를 가져오기 위한 라이브러리이다.

  • Python에서 HTTP 요청을 보내기 위한 인기 있는 라이브러리이다. 웹 상의 데이터를 가져오거나 API와 상호작용하는 등의 다양한 작업을 수행할 때 사용된다.




BeautifulSoup

HTML 및 XML 문서를 파싱하고 원하는 데이터를 추출하기 위한 Python 라이브러리이다.

장점

  • HTML과 XML 파싱
    BeautifulSoup는 웹페이지의 HTML 또는 XML을 쉽게 파싱하고, 원하는 정보를 추출할 수 있도록 해준다.

  • 유연성
    다양한 파서와 호환된다. 예를 들면, Python의 기본 HTML 파서, lxml, xml 파서 등을 사용할 수 있다.

  • 간단한 문법
    태그 이름이나 속성을 기반으로 쉽게 원하는 요소에 접근할 수 있다.
    빠른 실행: 정적인 웹 페이지의 내용을 빠르게 가져올 수 있다.

한계

  • 정적 데이터만 처리
    BeautifulSoup 자체는 JavaScript로 생성되는 동적인 데이터나 내용을 처리할 수 없다.

  • 웹 상호작용 부재
    로그인, 스크롤 다운, 버튼 클릭 등의 웹 상호작용 기능이 없다.


Requests와 BeautifulSoup

  • 목적의 차이
    Requests: 웹페이지의 내용을 가져오는 것에 중점을 둔다. 즉, HTTP 요청을 보내고 그 응답을 받는 기능을 제공한다.
    BeautifulSoup: 가져온 웹페이지의 내용을 파싱하고 분석하는 것에 중점을 둔다.

  • 기능의 차이
    Requests: 웹 상의 데이터를 가져오는 기능만 수행하며, 가져온 데이터의 구조나 내용을 분석하는 기능은 제공하지 않는다.
    BeautifulSoup: HTML 또는 XML 데이터를 분석하여 원하는 정보를 추출할 수 있게 도와주는 도구이다.

  • Requests와 BeautifulSoup
    이 라이브러리들은 자주 함께 사용된다. Requests를 사용하여 웹페이지의 내용을 가져오고, BeautifulSoup을 사용하여 해당 내용을 파싱하고 분석하는 방식으로 진행되기 때문이다.


설치하기

pip install beautifulsoup4

사용 예

from bs4 import BeautifulSoup
import requests

response = requests.get('https://www.example.com/')
soup = BeautifulSoup(response.content, 'html.parser')

# h1 태그 모두 찾기
h1_tags = soup.find_all('h1')
for tag in h1_tags:
    print(tag.text)




Selenium

웹 브라우저를 자동화하기 위한 도구이다. JavaScript로 렌더링된 페이지나 사용자 상호작용을 모방할 때 유용하다.


장점

  • 동적 데이터 처리
    현대의 많은 웹사이트들은 JavaScript로 동적으로 컨텐츠를 불러오기 때문에 기본적인 HTTP 요청만으로는 완전한 페이지의 내용을 가져오기 어렵다.
    Selenium은 웹 브라우저를 실제로 제어하기 때문에, JavaScript로 로드되는 동적인 웹페이지의 내용도 가져올 수 있다.

  • 웹 상호작용 가능
    사용자와 같이 로그인, 스크롤, 버튼 클릭, 폼 제출 등의 상호작용을 자동화하여 수행할 수 있다.

  • 다양한 웹 브라우저 지원
    Chrome, Firefox, Safari 등 다양한 웹 브라우저와 호환된다.

  • 다양한 언어 지원
    Python 뿐만 아니라 Java, C#, Ruby 등 다양한 프로그래밍 언어에서도 사용할 수 있다.

한계

  • 속도
    HTTP 요청을 직접 보내는 방식에 비해, Selenium은 웹 브라우저를 띄우고(심지어 headless 모드라도) 페이지를 완전히 불러올 때까지 기다리기 때문에 상대적으로 느리다.

  • 메모리 사용량
    실제 웹 브라우저를 사용하기 때문에 크롤링하는 동안 상대적으로 많은 메모리를 소비할 수 있다.

  • 설정 필요
    웹드라이버 등의 추가적인 설정이 필요하다. 웹 드라이버(e.g., chromedriver)와 브라우저의 버전이 호환되어야 한다.


설치하기

pip install selenium
웹 드라이버(ChromeDriver 등)도 설치

사용 예

from selenium import webdriver

# 웹드라이버 설정 (ex. Chrome)
driver = webdriver.Chrome(executable_path='/path/to/chromedriver')

driver.get('https://www.example.com/')

# h1 태그 모두 찾기
h1_tags = driver.find_elements_by_tag_name('h1')
for tag in h1_tags:
    print(tag.text)

driver.close()




BeautifulSoup vs Selenium

속도
BeautifulSoup은 Selenium보다 빠르다. 왜냐하면 실제 브라우저를 사용하지 않기 때문이다.

용도
정적인 페이지는 BeautifulSoup만으로도 충분하지만, JavaScript를 통해 동적으로 로드되는 페이지를 크롤링하려면 Selenium이 필요하다.

복잡성
BeautifulSoup은 사용이 간단하지만, Selenium은 브라우저를 제어하기 때문에 초기 설정이 필요하고, 코드가 다소 복잡해질 수 있다.


웹페이지의 종류와 필요한 데이터, 작업의 복잡성에 따라 적절한 도구를 선택해야 한다. 정적인 웹페이지의 경우 BeautifulSoup가 적합하며, 동적인 웹페이지나 웹 상호작용이 필요한 경우에는 Selenium이 더 적합할 것이다. 🤔




파서(parser)

주어진 데이터를 읽고 이해하여 원하는 형태나 구조로 변환하는 도구나 알고리즘을 의미한다. 즉, 파서는 입력 데이터의 구조를 분석하고 해당 데이터를 특정한 형태의 데이터 구조로 변환하는 역할을 한다.

  • 웹 크롤링과 관련하여 가장 흔히 언급되는 파서는 HTML 또는 XML 문서를 파싱하는 도구이다. 이러한 문서는 특정한 구조와 규칙을 가지고 있기 때문에, 이러한 구조를 해석하고 원하는 데이터를 추출하기 위해 파서가 필요하다.

  • BeautifulSoup 라이브러리에서는 다양한 파서를 지원한다. (html.parser, lxml, htl5lib)

📌 파서 예시

주어진 데이터

<html>
    <body>
        <h1>Welcome to My Website</h1>
        <p>This is a paragraph.</p>
        <ul>
            <li>Item 1</li>
            <li>Item 2</li>
        </ul>
    </body>
</html>

파서가 변환한 결과 (BeautifulSoup을 사용)

from bs4 import BeautifulSoup

data = """
<html>
    <body>
        <h1>Welcome to My Website</h1>
        <p>This is a paragraph.</p>
        <ul>
            <li>Item 1</li>
            <li>Item 2</li>
        </ul>
    </body>
</html>
"""

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

# h1 태그의 텍스트 가져오기
print(soup.h1.text)  # 출력: Welcome to My Website

# 모든 li 태그의 텍스트 가져오기
for li in soup.find_all('li'):
    print(li.text)

# 출력:
# Item 1
# Item 2

파서 알고리즘
실제 파서의 알고리즘은 꽤 복잡하다. 아주 기본적인 개념은 아래와 같다.👇

  1. 토크나이징 (Tokenizing)
    입력 데이터(HTML)를 읽어와서 "토큰"으로 나눈다. 예를 들어, <h1>, Welcome to My Website, </h1> 등과 같이 데이터를 구성하는 단위로 나누는 것이다.

  2. 구문 분석 (Parsing)
    토크나이징된 결과를 사용하여 데이터의 계층적 구조를 나타내는 트리(주로 DOM 트리라고 함)를 생성한다. 이 트리는 데이터의 구조와 관계를 반영한다.

  3. 트리 변환 (Tree Transformation)
    구문 분석된 트리를 사용하여 사용자가 원하는 형태나 구조의 데이터를 추출한다.

BeautifulSoup나 lxml 같은 라이브러리는 위의 과정을 내부적으로 처리하며, 사용자에게는 간단한 API를 제공하여 원하는 데이터를 쉽게 추출할 수 있도록 해준다. 😀




pandas

Python에서 데이터 분석을 위한 가장 인기 있는 라이브러리 중 하나로, pandas는 크고 복잡한 데이터를 쉽게 조작하고 분석할 수 있게 해준다.

DataFrame
pandas의 핵심 데이터 구조로, 엑셀 스프레드시트나 SQL 테이블 같은 테이블 형태의 데이터를 다룰 수 있게 해준다. 각각의 컬럼은 서로 다른 타입을 가질 수 있다.😀

데이터 가공
누락된 데이터 처리, 데이터 형태 변환, 데이터 병합 및 조인 등의 복잡한 데이터 변환 작업을 손쉽게 할 수 있다.

읽기 및 쓰기
다양한 파일 형식 (CSV, Excel, SQL, Parquet 등)에서 데이터를 읽거나 쓸 수 있다.

시계열 데이터 처리
날짜 및 시간 인덱싱, 데이터 리샘플링, 시간대 변환 등의 시계열 데이터 관련 작업을 간편하게 처리할 수 있다.

성능
내부적으로 C 언어로 작성된 부분들이 있어 대규모 데이터셋에 대한 연산도 빠르게 처리한다.

통합성
pandas는 Python의 다른 데이터 분석 라이브러리 (예: numpy, scipy, matplotlib, seaborn, scikit-learn 등)와 잘 통합되어 있다고한다. 🧐

사용예시

import pandas as pd

# 간단한 데이터 생성
data = {
    "이름": ["홍길동", "seul", "kim"],
    "나이": [25, 30, 22],
    "도시": ["서울", "부산", "대전"]
}

df = pd.DataFrame(data)

# CSV 파일로 저장
df.to_csv("data.csv", index=False) 

이때 index가 True일 경우, False일 경우를 모두 저장해서 비교해보기도 했다. 😊




requests로 웹 스크래핑 하기

웹 스크래핑을 연습해보기 위해 requests를 사용하여 코드를 작성해 보았다.

import requests
import pandas as pd
import pprint

pp = pprint.PrettyPrinter(depth=4)

# 상품 모델명 리스트
productKeyword = [
    "123",
    "1234",
    "12345",
    "123456",
    "1234567",
]

base_url = "https://path..."

results = []

for keyword in productKeyword:
    # 해당 상품 모델명에 대한 요청 URL 생성
    url = base_url + keyword

    # API에 요청
    response = requests.get(url)

    # 응답을 JSON 형태로 파싱
    data = response.json()

    # 상품 리스트를 추출
    products = data['list']

    results.append(
        {"title": f"검색된 모델명: {keyword} ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨", "price": "", "link": ""})

    for product in products:

        # 각 상품에서 원하는 정보를 추출
        title = product['name']
        price = format(int(product['price']), ',')

        # title과 keyword의 각 부분을 소문자로 변환하여 대소문자를 구분하지 않고 
        # title에 keyword의 모든 부분들이 포함되어 있는지 확인
        if not all(part.lower() in title.lower() for part in keyword.split()):
            continue

        # 상품 타입에 따라 링크 생성 방식을 다르게 함 (받아온 api에 맞춰서 변경)
        if product['type'] == "상품" and 'pid' in product:
            link = f"https://path.../products/{product['pid']}?q={keyword}&ref=..."
        elif product['type'] == "광고" and 'webUrl' in product:
            link = product['webUrl']
        else:
            continue  # 링크를 생성할 수 없는 경우 다음 상품으로 건너뜀

        results.append({"title": title, "price": price, "link": link})

pp.pprint(results)

# 결과를 DataFrame으로 변환하고 CSV 파일로 저장
df = pd.DataFrame(results)
df.to_csv("data/bunjang_products.csv", index=True)

✍️

  • pp = pprint.PrettyPrinter(depth=4)
    여기서 pprint.PrettyPrinter()는 Python의 pprint 모듈에서 제공하는 PrettyPrinter 클래스의 인스턴스를 생성한다. 이 클래스는 복잡한 데이터 구조를 보기 좋게 출력하는 데 사용된다.
    depth=4 매개변수는 출력할 데이터 구조의 깊이를 4로 제한하라는 의미이다. 즉, 데이터 구조의 깊이가 4를 초과하면 그 이후 내용은 "..."로 표시된다.
    pp.pprint(results)를 호출하면 results의 내용이 예쁘게 출력된다.👍

  • response = requests.get(url)
    이 코드는 Python의 requests 모듈을 사용하여 특정 URL에 HTTP GET 요청을 보낸다.
    예를 들어, url이 "https://api.example.com/data" 라면 해당 URL로 데이터를 요청하는 것과 같다.
    요청의 응답(예: 웹 서버에서 반환되는 데이터)은 response 변수에 저장된다.

  • price = format(int(product['price']), ',')
    product['price'] : 여기서 product는 딕셔너리이며, 'price'라는 키의 값을 가져오며, 이 값은 문자열로 가격을 나타낸다.
    int(product['price']) : 위에서 얻은 가격 문자열을 정수로 변환한다.
    format( ..., ',') : 정수로 변환된 가격을 다시 문자열로 변환하지만, 천의 자리마다 쉼표(,)를 포함시켜 보기 좋게 만든다. 예를 들어, 1234567이라는 정수는 "1,234,567"이라는 문자열로 변환된다.


✍️ 이경우에는 BeautifulSoup이나 Selenium을 별도로 사용하지 않고도 쉽게 가능했다.

직접적인 API 접근

  • 명확하게 정의된 데이터를 포함한 API에 직접 접근하여 필요한 정보를 요청하고 있다. API는 일반적으로 JSON, XML 등의 구조화된 데이터 형식으로 응답을 제공하는데, 이러한 데이터는 파싱하기 용이하므로 별도의 HTML 파싱 도구가 필요하지 않았다.
  • 이 코드는 이미 구조화된 API 응답을 처리하기 때문에 동적 콘텐츠에 대한 고려가 필요하지 않았다.

🤔 이를 바탕으로 가장 빠르고 쉽게 크롤링 할 수 있는 순서는 ... ✍️
1) 네트워크탭에서 통신 api 확인하고 requests로 처리하기
2) BeautifulSoup 활용하기
3) selenium 활용하기 (마치 사용자가 직접 동작하듯이 작동해서 가장 느림)





reference)

img

profile
기억보단 기록을 ✨

0개의 댓글