Web Scraping 기초 (TIL 8)

석형원·2024년 4월 3일

TIL

목록 보기
8/52

✏️ 오늘 학습한 내용

1. BeautifulSoup
2. 원하는 요소 찾기
3. 동적 웹 사이트


🔎 BeautifulSoup

  • HTML에서 원하는 요소만 가져오려면?

    • HTML Parser의 사용
      • BeautifulSoup4
  • 라이브러리 설치

    • pip install bs4
  • BeautifulSoup

import requests
# bs4안의 BeautifulSoup Class 사용
from bs4 import BeautifulSoup

res = requests.get("http://www.example.com")

# BeautifulSoup객체를 만들기
# 첫번째 인자로는 response의 body를 텍스트로 전달합니다.
# 두번째 인자로는 어떤 형식으로 분석을 할 것인지 명시 : "html"
soup = BeautifulSoup(res.text, "html.parser")

# .prettify()를 활용하면 분석된 HTML을 보기 편하게 반환
print(soup.prettify())

# => soup로 분석을 진행하고, prettify()로 분석한 것을 가시화

# title, head, body 부분을 별도로 추출 가능
soup.title
soup.head
soup.body

# <h1> 태그로 감싸진 요소 하나 찾기
soup.find("h1")

# <h1> 태그로 감싸진 요소 모두 찾기
soup.find_all("h1")

# 태그 이름 가져오기
h1.name

# 태그 내용 가져오기
h1.text

🔎 원하는 요소 가져오기

  • 기본적인 Scraping 방법
    • html의 contents기반으로 Scraping하기
      => 좋은 행위는 아님, 웹사이트의 구조가 바뀌는 경우가 많음
      ( 그러나 직관적이기에 이번 경우엔 이 방식을 사용 )

1) 책 이름 모으기

import requests
from bs4 import BeautifulSoup

res = requests.get("https://books.toscrape.com/catalogue/category/books/travel_2/index.html")

soup = BeautifulSoup(res.text,"html.parser")

# h3태그가 감싼 모든 요소를 호출
book_list = soup.find_all("h3")

# book_list에서 a태그 속 contents를 text로 변환
for book in book_list : 
    print(book.a.text)

# 확인해보니 contents에는 정보의 소실이 있다는 것을 발견
# title 내부에 정확한 책 이름이 있다는 것을 확인
# a 태그의 title에 접근
for book in book_list : 
    print(book.a["title"])

2) Tag의 id와 class 활용

태그는 자신의 이름 뿐만 아니라 고유한 속성을 가질 수 있음
이 중, idclass는 Locator로서, 특정 태그를 지칭하는데 사용

<p>This element has only tagname</p>
<p id="target">This element has tagname and id</p>
<p class="targets">This element has tagname and class</p>
  • tagname : 태그의 이름
  • id : 하나의 고유 태그를 가리키는 라벨
  • class : 여러 태그를 묶는 라벨
import requests
from bs4 import BeautifulSoup

res = requests.get("http://example.python-scraping.com/")

soup = BeautifulSoup(res.text,"html.parser")

## id가 results인 div 태그
soup.find("div",id="results")

# class가 "page-header"인 div 태그
find_result = soup.find("div","page-header")

# find_result의 h1의 contents를 호출
# python의 strip() 함수는 개행, 역슬래시, 공백 등을 모두 잘라준다.
find_result.h1.text.strip()
  • strip()

    python의 strip() 함수는 개행, 역슬래시, 공백 등을 모두 제거

3) Hashcode 질문 가져오기

  • 주의사항

    1. 과도한 요청을 보내지 않는다.
    2. 받아온 정보 활용에 유의할 것.
  • 질문의 제목이 li 태그 속 class가 question-list-item인 경우에 들어있다는 것을 확인

import requests
from bs4 import BeautifulSoup

# 이 user_agent를 설정하면, Mac의 Chrome환경의 접속자로 여겨짐

user_agent = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}

# 본래 user_agent의 자리에 header가 들어감, key-value의 형태이기에 같은 형식의 user_agent로 교체
res = requests.get(" https://hashcode.co.kr/", user_agent)

soup = BeautifulSoup(res.text,"html.parser")
 
questions = soup.find("li","question-list-item")

# 타고 타고 들어가서 제목 찾기
questions.find("div","question").find("div","top").h4.text

# 전부 찾기
for question in questions :
	print(question.find("div","question").find("div","top").h4.text)
  • 페이지네이션(Pagination)

    페이지네이션은 많은 정보를 인덱스로 구분하는 기법입니다.

    https://hashcode.co.kr/의 경우 Query String을 통해서 이를 구분

    https://hashcode.co.kr/?page={i}

# Pagination이 되어있는 질문 리스트의 제목을 모두 가져와봅시다.
# 과도한 요청을 방지하기 위해 1초마다 요청을 보내봅시다.
import time
# 1~6 페이지까지 호출
for i in range(1,6):
    requests.get("https://hashcode.co.kr/?page={}".format(i),user_agent)
    soup = BeautifulSoup(res.text,"html.parser")
    
    questions = soup.find("li","question-list-item")

    # 전부 찾기
    for question in questions :
        print(question.find("div","question").find("div","top").h4.text)
    # 0.5초마다 뽑기
    time.sleep(0.5)

🔎 동적 웹 사이트

  • 웹페이지의 종류

    • 정적 웹 사이트

      • HTML 내용이 고정된 정적(static) 웹 사이트
      • Request에 대한 Response가 고정되어 있다.
      • HTML 문서가 완전하게 응답됨
        (parsing을 해도 아무 문제 없음)
    • 동적 웹 사이트

      • HTML 내용이 변하는 동적(dynamic) 웹 사이트
      • 새로고침을 할 때마다 서버가 갱신해서 Response
      • 응답 후 HTML이 렌더링이 될 때까지의 지연시간이 존재!
        ( Request를 보낸 다음에 바로 parsing을 하면 안될 수도 있음 )
      • e.g.) 인스타그램, 유튜브
  • 동적 웹사이트

    • 웹 브라우저에선 javascript가 동작함
      • 비동기 처리를 통해서 필요한 데이터를 채움
        ( 요청에 따른 응답을 기다리지 않음 )
        => 상황에 따라 데이터가 완전하지 않은 경우가 발생
  • Requests로 요청 시 발생하는 문제점

    • 키보드 입력, 마우스 클릭 등을 requests로 진행하기 힘듬
      ( Requests는 단순히 요청만 받기 때문 )
  • 해결 방법

    • 임의로 시간을 지연한 후, 데이터 처리가 끝난 후 정보를 가져오면 됨

    • UI Action(키/마)을 프로그래밍을 통해 명령을 내림
      => 웹 브라우저를 Python으로 조작
      e.g.) 파이썬을 통해 이 버튼을 클릭해, 이 명령을 입력해, ...

  • 웹 브라우저를 자동화하는 라이브러리 (Python)

    • Selenium
    • 응답 후 시간을 지연시킬 수 있음
    from selenium import webdriver
    
    # Chrome 브라우저
    driver = webdriver.Chrome()
    # 10초간 지연 시킴
    driver.implicitly_wait(10)
    driver.get("http://www.example.com")
    from selenium import webdriver
    
    # UI와 상호작용
    elem = driver.find_element_by_tag_name("hello-input")
    elem.send_keys("Hello!")
    
    • 정리
      1. 동적 웹사이트는 응답 후 바로 정보를 추출하기 어렵다
      2. 다양한 키보드 입력과 마우스 클릭 등의 상호작용이 존재
      3. 이런 상황을 해결하기 위해, 웹 브라우저를 파이썬으로 조작하는 전략을 취하자
        (selenium 라이브러리)
profile
데이터 엔지니어를 꿈꾸는 거북이, 한걸음 한걸음

0개의 댓글