웹 스크래핑 기초 - HTML을 분석해주는 BeautifulSoup
- BeautifulSoup 라이브러리
- 지난 실습들에서 requests 모듈을 사용해 HTTP 요청을 보내고, 이 응답을 받아 살펴보았음. 하지만 내용이 아주 긴 텍스트로 와서 분석하기가 정말 힘들다는 문제점이 있었음
- 원하는 요소만을 가져올 수 있다면 더 좋겠쥬? → HTML parser 등장 = BeautifulSoup4
## www.example.com 사이트를 요청한 후 응답 받기
import requests
res = requests.get("http://www.example.com")
res.text
## BeautifulSoup4 - bs4를 불러오기
from bs4 import BeautifulSoup
## BeautifulSoup 객체 생성. 첫번째 인자 - response의 body, 두번째 인자 - html로 분석한다는 것을 명시
soup = BeautifulSoup(res.text, "html.parser")
## .prettify() : 분석된 HTML을 보기 편하게 반환
print(soup.prettify())
## title 가져오기
soup.title
## head 가져오기
soup.head
## body 가져오기
soup.body
## <h1> 태그로 감싸진 요소 하나 찾기
h1 = soup.find('h1')
h1
## <p> 태그로 감싸진 요소들 찾기
soup.find_all('p')
## 태그 이름 가져오기
h1.name
## 태그 내용 가져오기
h1.text
웹 스크래핑 기초 - 원하는 요소 가져오기 1
- 예시 사이트로 스크래핑 하기
*찾아온 데이터들은 모두 객체!이므로 익숙한 방식대로 데이터 추출 가능
## 스크래핑에 필요한 라이브러리 불러오기
import requests
from bs4 import BeautifulSoup
## 예시 사이트에 요청하고, 응답을 바탕으로 BeautifulSoup 객체 생성
res = requests.get("http://books.toscrape.com/catalogue/category/books/travel_2/index.html")
soup = BeautifulSoup(res.text,"html.parser")
## <h3> 태그에 해당하는 요소 하나 찾기
book = soup.find("h3")
book
## <h3> 태그에 해당하는 요소 모두 찾기
book_list = soup.find_all('h3')
book_list.results[0]
## book_list에서 우리가 원하는 제목(title)만 추출하기
for book in book_list:
print(book.a["title"])
웹 스크래핑 기초 - HTML의 Locator로 원하는 요소 가져오기
- 원치 않는 정보를 가져오지 않기 위해 알아야함
- 태그는 자신의 이름 뿐만 아니라 고유한 속성 또한 가질 수 있는데,
이 중 id와 class는 Locator로서 특정 태그를 지칭하는 데에 사용됨
- tagname : 태그의 이름
- id : 하나의 고유 태그를 가리키는 라벨(중복 불가!)로, 해당하는 태그 단 하나를 쉽게 가져옴
- class : 여러 태그를 묶는 라벨(유사한 요소들을 구분지음)로, 해당하는 태그 단 하나를 쉽게 가져옴
## 스크래핑에 필요한 라이브러리를 불러오고 soup 객체 생성
import requests
from bs4 import BeautifulSoup
res = requests.get("http://example.python-scraping.com")
soup = BeautifulSoup(res.text, "html.parser")
## id를 이용해서 요소 가져오기
#1. id 없이 div 태그 찾기
soup.find("div")
#2. id가 results인 div 태그 찾기
soup.find("div", id="results")
# class를 이용해서 요소(들) 가져오기
#1. class가 "page-header"인 div 태그를 찾기
find_result = soup.find("div", "page-header")
#2. 위 결과에서 text 값을 깔끔하게 가져오기
find_result.h1.text.strip()
웹 스크래핑 기초 - 원하는 요소 가져오기 2
- 두 가지 원칙
1) 과도한 요청을 보내지 않기
2) 받아온 정보 활용에 유의하기(상업적 활용) - 페이지네이션(pagination)
- 많은 정보를 인덱스로 구분하는 기법
- 예시에선, Query String을 통해 이를 구분하고 있음. https://hashcode.co.kr/?page={i}
## 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
res = requests.get("https://qna.programmers.co.kr", user_agent)
# 응답을 바탕으로 BeautifulSoup 객체를 생성하기
soup = BeautifulSoup(res.text, "html.parser")
# 질문의 제목 하나 가져오기
soup.find("li", "question-list-item").find("div", "question").find("div","top").h4.text
# 질문의 제목 모아서 가져오기
questions = soup.find_all("li", "question-list-item")
for ques in questions:
print(ques.find("div", "question").find("div", "top").h4.text)
## bonus. 페이지네이션(Pagination)
# Pagination이 되어있는 질문 리스트의 제목을 모두 가져오고,
# 과도한 요청을 방지하기 위해 1초마다 요청을 보내본다
import time
for i in range(1,6):
res = requests.get("https://hashcode.co.kr/?page={0}".format(i),user_agent)
soup = BeautifulSoup(res.text, "html.parser")
questions = soup.find_all("li", "question-list-item")
for ques in questions:
print(ques.find("div", "question").find("div","top").h4.text)
time.sleep(0.5)
웹 브라우저 자동화 - Selenium
- 웹 페이지 종류
- 정적(static) 웹 사이트 : HTML 내용이 고정됨. 요청시 HTML 문서가 완전하게 응답됨
- 동적(dynamic) 웹 사이트 : HTML 내용이 변함. 요청시 HTML이 렌더링이 될 때까지 지연시간이 존재함. 따라서 상황에 따라 데이터가 완전하지 않은 경우 발생 - 동적 웹사이트의 작동 방식
- 웹 브라우저에선 JavaScript라는 프로그래밍 언어가 동작함
- JS는 비동기* 처리를 통해서 필요한 데이터를 채움. 어떻게? 임의로 시간을 지연 후, 데이터 처리가 끝나고 정보를 가져오면 됨
*동기와 비동기 : 동기 처리는 요청에 따른 응답을 기다리는 반면(렌더링 → 데이터 처리), 비동기 처리는 요청에 따른 응답을 기다리지 않음(렌더링을 하는 와중에 데이터 처리를함) - Selenium - 웹 브라우저를 자동화하는 라이브러리
[정리] 동적 웹 사이트는 응답 후 바로 정보 추출이 어렵고 다양한 입출력 장치와의 상호작용이 존재함. 이런 상황을 해결하기 위해 웹 브라우저를 파이썬으로 조작하는 전략을 취해보자! → Selenium
'dev course - DE > TIL' 카테고리의 다른 글
[데브코스] TIL 10일차 (0) | 2024.04.05 |
---|---|
[데브코스] TIL 9일차 (0) | 2024.04.04 |
[데브코스] TIL 7일차 (0) | 2024.04.02 |
[데브코스] TIL 6일차 (0) | 2024.04.01 |
[데브코스] TIL 5일차 (1) | 2024.03.29 |