DevCourse TIL Day3 Week3

김태준·2023년 4월 19일
0
post-thumbnail

✅ BeautifulSoup

HTML Parser인 bs4 라이브러리를 사용하고자 한다.
기존의 body를 이용해 모든 html 코드를 텍스트로 가져오는 과정을 거쳤는데, BeautifulSoup4를 통해 원하는 elements만 가져오도록 할 수 있다.
간단한 구현 코드는 다음과 같다!

from bs4 import BeautifulSoup
import requests

res = resquests.get('www.example.com')
res.text

# 첫번째 인자로는 response의 body를 텍스트로 전달합니다.
# 두번째 인자로는 "html"로 분석한다는 것을 명시해줍니다.
soup = BeautifulSoup(res.text, 'html.parser')

# 객체 soup의 .prettify()를 활용하면 분석된 HTML을 보기 편하게 반환해줍니다.
print(soup.prettify())
# title 가져오기
print(soup.title)
# head 가져오기
soup.head
# body 가져오기
soup.body

# <h1> 태그로 감싸진 요소 하나 찾기
soup.find('h1')
# <p> 태그로 감싸진 모든 요소들 찾기
soup.find_all('p')

# 태그 이름 가져오기 (찾은 요소를 객체로 변환한 변수에 name 적용할 것!)
h1 = soup.find('h1')
h1.name
# 태그 내용 가져오기
h1.text

✍️ Exercise 1 (특정 요소 추출하기)

실습을 통해 BeautifulSoup을 사용해보고자 한다!
코드는 다음과 같다.

# 스크래핑에 필요한 라이브러리를 불러와봅시다.
from bs4 import BeautifulSoup as bf
import requests

# 예시 사이트에 요청을 진행하고, 응답을 바탕으로 BeautifulSoup 객체를 만들어봅시다.
url = 'http://books.toscrape.com/catalogue/category/books/travel_2/index.html'
res = requests.get(url)
soup = bf(res.text, 'html.parser')

# <h3> 태그에 해당하는 요소를 하나 찾아봅시다
soup.find('h3')
# <h3> 태그에 해당하는 요소를 모두 찾아봅시다
soup.find_all('h3')
book = soup.find('h3')
# book_list에서 h3 특정 태그 내 우리가 원하는 제목(title)만 추출해봅시다.
book.a.text
# < 결과 >
# - > "It's Only the Himalayas"

# book_list에서 우리가 원하는 제목(title)을 모두 추출해봅시다.
h3_results = soup.find_all('h3')
for i in h3_results:
    print(i.a['title'])
# < 결과 > 
'''
It's Only the Himalayas
Full Moon over Noah’s Ark: An Odyssey to Mount Ararat and Beyond
See America: A Celebration of Our National Parks & Treasured Sites
Vagabonding: An Uncommon Guide to the Art of Long-Term World Travel
Under the Tuscan Sun
A Summer In Europe
The Great Railway Bazaar
A Year in Provence (Provence #1)
The Road to Little Dribbling: Adventures of an American in Britain (Notes From a Small Island #2)
Neither Here nor There: Travels in Europe
1,000 Places to See Before You Die
'''

🎈 HTML의 Locator로 특정 요소 찾기

태그는 자신 이름 뿐만 아니라 고유한 속성 또한 가질 수 있다.
이중 id, class는 Locator로써 특정 태그를 지칭하는 데 사용된다.

  • tagname : 태그 이름
  • id : 하나의 고유 태그를 가리키는 라벨 (unique)
  • class : 여러 태그를 묶는 라벨
<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>

< 실습 코드는 다음과 같다.

soup = BeautifulSoup(res.text, 'html.parser')
# id는 요소 하나를 지칭하는 Unique한 성질을 가졌고, 이를 이용해 단 1개의 태그를 쉽게 가져올 수 있다.
# id 없이 div 태그 찾기
soup.find('div')
# id가 ASDASDA인 div 태그 찾기
soup.find('div', id = 'ASDASDA')

# class는 유사한 요소들을 구분 지을 수 있고, 이를 이용해 해당하는 태그 1개를 쉽게 가져올 수 있다.
# class가 'page-header'인 div 태그 찾기
find_result = soup.find('div', 'page-header')
# text만 깔끔하게 가져오기 (공백, 줄 제거 등)
find_result.태그명.text.strip() 

✍️ Exercise 2 (특정 요소 추출하기)

# 다음 User-Agent를 추가해봅시다.
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"}
# 라이브러리 호출
import requests
from bs4 import BeautifulSoup
>
url = 'https://hashcode.co.kr/'
res = requests.get(url, user_agent)
soup = BeautifulSoup(res.text, 'html.parser')
>
# 질문의 제목을 모아서 가져오기
questions = soup.find_all('li', 'question-list-item')
question_list = []
for quest in questions:
    print(quest.find('div', 'question').find('div','top').h4.text)
    question_list.append(quest.find('div', 'question').find('div','top').h4.text)
    
# 질문의 빈도를 체크하는 dict를 만든 후, 빈도를 체크해봅시다.
frequency_check = {}
for i in question_list:
    if i not in frequency_check:
        frequency_check[i] = 1
    else:
        frequency_check[i] += 1
frequency_check
# 결과 ex) {'파이썬 프로젝트 중 오류': 1, '구조체 사용하여 다항식의 덧셈 구현하는 문제': 1}

##### Pagination 
많은 정보를 인덱스로 구분하는 기법으로 주어진 page 별 추출하는 기법
# Pagination이 되어있는 질문 리스트의 제목을 모두 가져와봅시다.
# 과도한 요청을 방지하기 위해 1초마다 요청을 보내봅시다.
url = "https://hashcode.co.kr/?page={}"

import time
# 1~5페이지 상대로
for i in range(1, 6):
    res = requests.get(url.format(i), user_agent)
    soup = BeautifulSoup(res.text, 'html.parser')
    questions = soup.find_all('li', 'question-list-item')
    for quest in questions:
        print(quest.find('div', 'question').find('div', 'top').h4.text)
    # 1초의 interval 주기
    time.sleep(1)

✅ Web Page - Dynamic

웹 페이지는 크게 정적 웹사이트와 동적 웹사이트로 나뉜다.
이를 살펴보면 다음과 같다.

  • 정적 웹사이트
    HTML 내용이 고정된 것
    같은 주소 요청 시 같은 응답, 완전한 응답 얻을 수 있음
    앞서 학습한 BeautifulSoup으로 get 가능
  • 동적 웹사이트
    HTML 내용이 변하는 것
    응답 후 HTML이 렌더링 될 때까지의 지연시간이 존재해 곧바로 정보 추출이 힘듬
    다양한 키보드 입력, 마우스 클릭 등 상호작용 존재
    ex) 피드 빠르게 변화되는 SNS, 어플 등
    -> 웹 브라우저를 파이썬으로 조작하는 방식 必

🧨 동적 웹사이트 동작 방식

✔️ 웹 브라우저는 HTTP 통신을 보내고 받는데 사용.
✔️ 웹 브라우저에선 JavaScript라는 프로그래밍 언어가 동작.
✔️ 비동기 처리(요청에 따른 응답 대기 없이)를 통해 필요 데이터를 채움

-> 비동기 처리의 경우 렌더링 과정에서 데이터 처리가 이루어지기에 데이터가 완전치 않은 경우가 발생해 기존 request로 요청하면 안된다.

✨ 동적 웹사이트에 request 요청 시 발생하는 문제 및 해결방안

    1. 완전치 않은 데이터에 요청을 보내면 불완전한 응답을 받게 되어 결과에 문제 발생
      -> 임의로 시간을 지연한 후 데이터 처리가 끝난 후 정보를 가져오도록 함
    1. 마우스 클릭, 키보드 입력 등은 수시로 이루어지기에 request로 진행하기 힘듬
      -> UI Action 문제는 웹 브라우저를 파이썬을 조작함으로써 해결 가능
      -> 웹 브라우저를 자동화하는 Selenium 사용

< Selenium 예제 코드 >

from selenium import webdriver
# 크롬 브라우저 활용
driver = webdriver.Chrome()
# 응답 후 지연시간 부여 (문제 1 해결)
driver.implicitly_wait(10)
driver.get(url)
# UI와 상호작용 가능, 태그 이름을 통해 요소를 가져온 후 문자열을 키보드로 입력 진행 가능 (문제 2 해결)
elem = driver.find_element_by_tag_name('hello-input')
elem.send_keys('Hello')
profile
To be a DataScientist

0개의 댓글