Sign In

[데이터 수집] BeautifulSoup, Selenium

Y
Yerim
  1. ML
  2. DataAnalysis

정적 웹페이지, 동적 웹페이지

정적 웹페이지
웹 서버에 이미 저장된 기본 html 전송
데이터 자체의 변화 없는 한 고정 데이터 전달
Ex) 홈페이지 회사 소개
동적 웹페이지
요청 정보 처리 후 제작된 html 페이지 전송
사용자가 요청하는 상황 등에 따라 다른 데이터 전달
Ex) 뉴스 댓글, 상품 검색
정적 웹페이지
동적 웹페이지
BeautifulSoup
O
(페이지 내의 고정 html만 전달받음)
X
Selenium
O
O
원래 테스트 엔지니어링을 돕는 프로그램
BeautifulSoup4
Selenium

BeautifulSoup

간단하게 HTML, XML에서 정보를 추출할 수 있는 라이브러리
HTML, XMl의 트리 구조를 탐색, 검색, 변경 가능
다운로드 기능은 포함하지 않음
다양한 파서(parser)를 선택하여 이용 가능
파서 (parser)
선언
장점
단점
html.parser
BeautifulSoup(markup, ‘html.parser’)
설치 필요 없음
적절한 속도
lxml HTML parser
BeautifulSoup(markup, ‘lxml’)
매우 빠름
lxml 추가 설치 필요
lxml XML parser
BeautifulSoup(markup, ‘xml’)
매우 빠름
유일한 xml parser
lxml 추가 설치 필요
html5lib
BeautifulSoup(markup, ‘html5lib’)
웹 브라우저와 같은 방식으로 파싱
유용한 HTML5 생성
html5lib 추가 설치 필요
매우 느림
# BeautifulSoup 설치
!pip install beautifulsoup4

HTML 파싱 (Parsing)

웹페이지 예제

%%writefile example.html
<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    <h1>Heading</h1>
    <p>Paragraph</p>
    <div>
      <a href="www.google.com">google</a>
    </div>
    <div class="class1">
      <p>a</p>
      <a href="www.naver.com">naver</a>
      <p>b</p>
      <p>c</p>
    </div>
    <div id = "id1">
      Example Page
      <p>g</p>
    </div>
  </body>
</html>

HTML parser

from bs4 import BeautifulSoup
import urllib.request
with open("example.html") as fp:
  soup = BeautifulSoup(fp, 'html.parser')

print(soup.prettify())

HTML 태그 파싱

soup.title # title 태그만
soup.title.name # title 태그의 이름만
soup.title.string # title 태그의 문자만
soup.title.parent # title 태그의 부모 태그

soup.h1 # h1 태그
soup.p # p 태그
soup.div # div태그, div에 포함된 태그들까지 함께 불러옴
soup.a # a태그

HTML 태그 검색

find()

해당 조건에 맞는 하나의 태그를 가져옴
soup.find('div') # 첫번째 div
soup.find('a').get('href') # a태그의 'href' 속성 값 
soup.find('a').get_text() # a태그의 텍스트만

find_all()

해당 조건에 맞는 모든 태그를 가져옴
리스트 형태로 반환
soup.find_all('div') # div가 사용된 모든 태그
soup.find_all('div', {'id':'id1'} # attribute 중 id가 id1인 div태그

site_names = soup.find_all('a') # a태그가 사용된 모든 태그
for name in site_names: 
  print(name.get('href')) # a태그의 href만 출력

site_names = soup.find_all('a') # a태그가 사용된 모든 태그
for name in site_names:
  print(name.get_text()) # a태그의 text만 출

select()

CSS 선택자와 같은 형식으로 선택 가능
soup.select('div#id1') # id는 #으로 접근
soup.select('div.class1') # class는 .으로 접근

soup.select('div.class1 a') # class가 class1인 태그의 a태그
soup.select('div.class1 > a') # 위와 같음

웹페이지 콘텐츠 가져오기

웹페이지 예제

%%writefile anthem.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <div>
    <p id="title">애국가</p>
    <p id="content">
      동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세. <br/>
      무궁화 삼천리 화려 강산 대한 사람, 대한으로 길이 보전하세, <br/>
    </p>
    <p id="content">
      남산 위에 저 소나무, 철갑을 두른 듯 바람 서리 불변함은 우리 기상일세. <br/>
      무궁화 삼천리 화려 강산 대한 사람, 대한으로 길이 보전하세. <br/>
    </p>
    <p id="content">
      가을 하늘 공활한데 높고 구름 없이 밝은 달은 우리 가슴 일편단심일세. <br/>
      무궁화 삼천리 화려 강산 대한 사람, 대한으로 길이 보전하세, <br/>
    </p>
    <p id="content">
      이 기상과 이 맘으로 충성을 다하여 괴로우나 즐거우나 나라 사랑하세. <br/>
      무궁화 삼천리 화려 강산 대한 사람, 대한으로 길이 보전하세, <br/>
    </p>
  </div>
</body>
</html>
with open("anthem.html") as fp:
  soup = BeautifulSoup(fp, 'html.parser')

soup

내용 추출

# 애국가의 가사만 가져오기
title = soup.find('p', {'id':'title'}) # id가 title인 p태그만
contents = soup.find_all('p', {'id':'content'}) # id가 content인 p태그만

print(title.get_text()) # title의 text 출력 
for content in contents:
  print(content.get_text()) # content의 text 출력

인터넷 웹페이지 가져오기

F12 or 우클릭 검사 → html 코드 확인 가능
검사창 왼쪽 위 화살표 그림을 클릭하여 원하는 요소의 코드 선택 가능
원하는 코드 우클릭 → copy → selector를 이용하면 css selector 사용 가능
복사해온 css selector에서 :nth-child() → 특정 대상을 지정, 웹 스크래핑의 경우 삭제 한다.
url = "http://suanlab.com" # 가져오고 싶은 웹페이지 주소
html = urllib.request.urlopen(url).read() # 웹페이지 읽어오기
soup = BeautifulSoup(html, 'html.parser') # html parser 이용
soup
labels = soup.find_all(['label']) # label 태그 모두
for label in labels:
  print(label.get_text()) # label 태그의 text만
labels = soup.select('#wrapper > section > div > div > div > div > div > label') # CSS selector 이용
for label in labels:
  print(label.get_text())

BeautifulSoup으로 gmarket 스크래핑

#1. 필요한 라이브러리 import 하기
import requests
from bs4 import BeautifulSoup
import re
import pandas as pd
#2. 링크를 request.get으로 가져오기 (header가 필요한 경우 headers 입력)
res = requests.get('https://www.gmarket.co.kr/n/best?viewType=G&groupCode=G07') # 식품 카테고리

#header 입력
headers = {
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}
#3. BeautifulSoup을 이용해 파싱하기
soup = BeautifulSoup(res.content, 'html.parser')
#4. 상품명 크롤링하여 리스트로 저장하기 (20점)
item_list = soup.select('a.itemname')

# 상품의 이름만 title_list에 저장
title_list = list()
for item in item_list:
  title_list.append(item.get_text())
#5. 정가 정보 크롤링하여 리스트로 저장하기 (10점)
item_list = soup.select('div.o-price')

# 상품의 정가 정보만 original_price_list에 저장
original_price_list = list()

for item in item_list:
  original_price_list.append(item.get_text())
#6. 할인가 정보 크롤링하여 리스트로 저장하기 (10점)
item_list = soup.select('div.s-price')

# 상품의 할인가 정보만 sale_price_list에 저장
sale_price_list = list()

for item in item_list:
  sale_price_list.append(item.get_text())
#7. 적절히 데이터 전처리를 하여 DataFrame 객체로 만들고 Excel 파일로 저장하기 (30점)
#이때, DataFrame의 열은 "상품명, 정가, 판매가"로 구성되어야 함.

# 정가, 판매가 서식
def extract_number(x):
  price = re.findall("\d.*원", x)
  try:
    price_num = price[0].replace("원", "")
    try:
      price_num = int(price_num.replace(",", ""))
    except:
      price_num = int(price_num)
  except:
    pass
  return price_num if price else None

# 정가 열이 na면 판매가를 대입
def fill_na_with_sales(row):
    if pd.isna(row['정가']):
        return row['판매가']
    else:
        return row['정가']

# gmarket_food_df에 상품명, 정가, 판매가의 정보를 DataFrame으로 저장
gmarket_food_df = pd.DataFrame([title_list, original_price_list, sale_price_list]).T
gmarket_food_df.columns = ['상품명', '정가', '판매가']

# extract_numer -> 정가, 판매가 숫자만 남기기
gmarket_food_df['정가'] = gmarket_food_df['정가'].apply(extract_number)
gmarket_food_df['판매가'] = gmarket_food_df['판매가'].apply(extract_number)

# fill_na_with_sale -> 정가의 빈값은 판매가로 채우기
gmarket_food_df['정가'] = gmarket_food_df.apply(fill_na_with_sales, axis=1)
gmarket_food_df['정가'] = gmarket_food_df['정가'].apply(int)

# 엑셀파일로 저장
gmarket_food_df.to_excel('gmarket_best_item.xlsx')
Yerim-DevNote
Subscribe to 'Yerim-DevNote'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'Yerim-DevNote'!
Subscribe
👍