본문 바로가기

Data Science/3. 데이터 엔지니어링

[n323]Web Scraping

■ Key words

  ㆍ크롤링(Crawling) / 웹스크래핑(Web Scraping)

  ㆍDOM

  ㆍHTML, CSS

  ㆍbeautifulsoup library

  ㆍparser

  ㆍrequests library

 

■ 주요내용

  ㆍHTML(Hyper Text Markup Language) : 웹에서 페이지를 표시할 떄 사용. MDN(Mozilla Developer Network)에

     따르면 프로그래밍 언어는 아니며, 웹페이지의 구성을 알려주는 마크업 언어임.

     - 요소(element) : HTML을 구성하는 것. opening tag<>와 closing tag</>로 구성.

     - Children : 요소 안의 요소

  ㆍCSS(Cascading Style Sheets) : 웹 페이지 문서가 어떻게 표현되는지 알려주는 스타일시트 언어

     - Selector : 특정 요소를 선택하는 방법

       · Type Selector : CSS 타입에 따라 선택 

       · Class Selector : 클래스에 따라 선택

       · ID Selector : ID에 따라 선택

       * DOM에서 Selector를 활용하여 원하는 element를 가지고 작업 가능

     - CSS 상속 : 하위 요소는 상위 요소의 스타일을 상속받게 됨

     - CSS Class : 어떤 특정 요소들의 스타일을 정하고 싶을 떄 사용

     - CSS ID : Class와 비슷하지만, 특정 HTML 요소를 가리킬 떄만 사용

  ㆍDOM(Document Object Model, 문서 객체 모델) : HTML, XML 등 문서의 프로그래밍 인터페이스.

    프로그래밍 언어를 통해 HTML 문서 등에 접근할 수 있도록 해줌.

     - 프로그래밍 언어에서 웹 페이지의 요소나 스타일 등을 추가하거나 수정하는 등 다양한 작업 가능

     - 문서를 구조화된 형식으로 표현함으로써 원하는 동작 가능

     - 객체(Object) : javascript에서 사용되는 구조 중 하나. python의 dictionary와 유사. 

       DOM을 통해 프로그래밍 언어에서 사용할 수 있는 데이터 구조 형태로 작업 수행 가능

       ⇒ 크롤링 등 웹 페이지와 작업할 때 중요

     - DOM Method : 웹 브라우저 - 개발자 도구 - 콘솔창에서 자바스크립트 실행을 통해 DOM 사용 가능

     - DOM과 크롤링 : 웹 페이지를 문자열(text)로 읽게 되면 데이터 해석/활용이 쉽지 않으므로 DOM 활용

  ㆍWeb Scraping / Crawling

     - Web Crawling : 컴퓨터가 웹을 자동적으로 돌아다니면서 사이트들을 인덱싱하는 행위.

     - Web Scraping : 웹을 돌아다니며 특정 정보를 수집하는 행위.

  ㆍRequests Library : 웹과 소통을 도와주는 library. Python에서  HTTP 요청을 보낼 때 거의 표준으로 사용됨.

     가장 큰 장점은 HTTP 요청을 간단한 메소드를 통해 실행할 수 있도록 짜여있음.

     - 요청 보내기 : 대상 웹페이지 서버에 요청 보내기(get)

     - 응답 받기 : 응답에 성공하면 HTTP 상태 메세지와 코드를 받게 됨

     - 응답내용 : 응담은 문서 파일인 HTML, CSS 등의 형태로 받게 됨

       * 서버에서 Response를 bytes로 받기 때문에 해당 데이터를 text로 인지하기 위해서는 디코딩이 필요함

         ⇒ encoding 속성 설정을 통해 인코딩 방식을 정할 수 있음.

         Response Object에 'text' 속성이 존재하여 HTML 파일로 encoding을 해줌

  ㆍBeautifulSoup Library : Web Scraping에서 서버에 요청을 보내고 응답을 받은 후 내용을 Parsing하고

     정보 획득하게 해주는 Library

     - 기본 parsing : parsing할 대상 문자열과 그 방법 정하기.

       · 기본 사용가능한 parser : html.parser

       * Parsing : 문자열로 구성된 특정 문서들(HTML, XML 등)을 python에서도 쉽게 사용할 수 있게 변환하는 것

     - 요소(elements) 찾기 : Parsing 완료 후 원하는 elements를 특징에 따라 찾아야 함(id/class/tag, find/find_all)

       · find : 조건에 일치하는 첫 번째 결과만 return

       · find_all : 조건에 일치하는 모든 결과를 list 형태로 return

     - 태그 활용 : 상세하게 검색하기 위해 tag 활용('div' tag 혹은 'p' tag 등)

     - string 활용 : 특정 문자열이 포함된 element 검색에 string= 활용 / 정규표현식을 활용을 통한 특수문자도 검색 가능

  ㆍ정보 얻기 : 원하는 요소 선택 후 text 속성을 활용하여 내부 문자 획득 / .strip()을 통한 띄어쓰기 제거

 

■ Session note

  ㆍcrawling의 목적 : 능동적으로 데이터를 수집하기 위해
  ㆍHTML : 웹페이지를 이루는 뼈대
  ㆍCSS : 웹페이지를 이루는 살. 사이트 이미지 출처, 크기, 배경색, 모형 등을 지정
  ㆍjavascript : 옷. 동적으로 바꿀 수 있음. 클릭에 따라 어떻게 반응하여 동적으로 변하는지 지정
                   javascript는 웹을 위해 만들어짐. 최근들어 client(react)/server(node.js)도 만들 수 있게 됨.

  ㆍ<head> 웹페이지의 주요 정보들이 들어있음
     * 사람이 읽을 수 없도록 compile된 코드가 많음
  ㆍ화면을 선택해서 상속관계에 한 번에 찾아들어갈 수 있음
  ㆍjavascript를 활용하면 좀 더 수월하게 웹페이지를 수정할 수 있음
  ㆍchrome : 흰색/회색글씨 - fishing site에서 도메인이 이상한 경우가 많음
     ⇒ 확인해야 할 도메인(흰색글씨)를 보기 / https가 있어야 안전함
  ㆍweb browser : 웹언어(JS/HTML/CSS; text)를 시각적으로 보여주는 것
  ㆍscraping은 웹브라우저가 필요 없음; server에서 정보를 가져옴
    cf> domain name : 알아보기 쉽도록 영어로 표기한 것. google.com 등
        ⇒ 컴퓨터는 문자열을 인식할 수 없음. 도메인 이름에 연결된 IP주소를 통해 서버에 접속하는 것

            (get 요청; HTTP - 하나의 언어 → 웹 브라우저 없이 python에서 바로 보낼 수 있음)
  * scraping : 회색영역. robots.txt가 있는 경우 scraping을 조심해야 함. does/doesn'ts가 나와있음
    crawling은 무차별적으로 인터넷을 탐색하게 됨; 서버에 과부하를 주어 사이트의 서비스 질이 떨어질 수 있음 /

    최악의 경우 서버가 다운될 수도 있음
    민감한 데이터는 상업적으로 사용하지 말자
  ㆍcrawling : web에서 거미 한 마리가 왔다갔다 하는 것; 연결된 사이트를 자유롭게 돌아다니는 것.

    모든 것을 가져옴 != scraping
  ㆍ1초에 30번 : 사이트에 접근 요청을 보내는 것

  ㅁ 실습
  ㆍimport request

    response = request.get("http://www.google.com")

  ㆍbeautifulsoup으로 text를 soup 형태의 인스턴스로 불어오게 됨
   * 원하는 데이터를 어떻게 가져올 것인지가 중요함
  ㆍstatus code : 200 (okay)
  ㆍremote address : 요청을 보내는 주소
  ㆍ로그인 등으로 인해 server의 구성이 달라질 수 있음

  ㅁ 오후 QnA
  ㆍFind all
  ㆍ정규식
  ㆍ검색 시 대/소문자 구분 철저히(document)
  ㆍ500은 내부 서버 문제; 말도 안 되는 요청을 보내 오류 메세지로 반환
  ㆍmro : 상속 순서
  ㆍdictionary와 tag의 차이점
  ㆍbs4 librarry를 사용할 때 class 'bs4.element.Tag' type이 사용됨
  ㆍtag['class'] 입력 시 들어갈 수 있는 class들이 나옴
     HTML.Parcel => class 변수:함수 ≒ dictionary key:value
     class를 dictionary처럼 활용할 수 있지만, 실질적으로는 HTML에 접근하는 것을 쉽게 하기 위해

     비슷한 양식으로 만들어 주는 것일 뿐
  ㆍ@property 사용하는 이유 : dictionary 처럼 무분별한 사용 혹은 접근을 제한하기 위함
   cf> 셀레늄 : beautifulsoup을 활용하여 동적 사이트로 만들어 줌; site는 변화하지 않고,

                   해당 사이트를 동적으로만 바꿔줌
  ㆍ과제 : 아래 있는 함수를 위에서 쓰는 경우도 있음
  ㆍ요즘 javascript가 동적인 것이 많아 셀레늄을 많이 쓰긴 하지만, 무거워서 RAM의 resource를

     많이 잡아먹기 때문에 가급적 안 사용하는 것이 좋음; 취향 차이 - 케이크를 자를 때 전기톱을 쓰지 말자
     ⇒ 요즘은 J-query/react 등 동적인 웹은 잘 사용하지 않는 편

 

■ 주요함수

  ㆍRequest Library

# 설치
pip install requests

# 불러오기
import requests

# 요청 보내기 : 'get' 사용
requests.get('[웹 주소]')
⇒ <Response [200]> : HTTP 응답객체가 리턴됨
⇒ <class 'requests.models.Response'> : python에서 .type 확인 시 객체의 타입이 class로 나옴

# 응답 받기 : 요청에 따라 HTTP의 상태 메세지와 번호를 응답 받음
import requests
url = '[웹 주소]'
resp = requests.get.(url)
resp.status_code # 상태코드 확인

# raise_for_status method를 활용하여 응답이 성공적이지 않을 경우 일부러 에러를 일으킬 수도 있음
import requests
from requests.exceptions import HTTPError

url = 'https://google.com'

try:
    resp = requests.get(url)

    resp.raise_for_status()
except HTTPError as Err:
    print('HTTP 에러가 발생했습니다.') # HTTPError 발생 상황만 별도로 관리
except Exception as Err:
    print('다른 에러가 발생했습니다.')
else:
    print('성공')
# 서버와 통신을 통해 대상 웹페이지를 먼저 받아올 수 있어야 함

# 응답 내용 : reso.text의 정보 확인; HTML 파일
resp.text
⇒ '<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ko"><head>...</body></html>'

 

  ㆍBeautifulSoup Library

# BeautifulSoup Library 설치
pip install beautifulsoup4

# 기본 Parsing
import requests
from bs4 import BeautifulSoup

url = 'https://google.com'
page = requests.get(url) # 1. request로 parsing할 page 받아오기

soup = BeautifulSoup(page.content, 'html.parser') 
# html.parser는 python 내장함수 / XML 혹은 다른 문서 parsing을 위해선 별도 설치 필요

# 요소 찾기 : id = 'dog'인 요소 1개만 찾기
dog_element = soup.find(id='dog')

# class에 'cat'이 들어있는 모든 요소 찾기 / class는 class_로 검색
cat_elements = soup.find_all(class_='cat')

# class에 'cat'이 있는 elements 중 세부 조건 검색
cat_elements = soup.find_all(class_='cat')

for cat_el in cat_elements:
    cat_el.find(class_='fish')
    
# 태그 활용 : 'div' tag의 class가 'cat'인 elements 검색
cat_div_elements = soup.find_all('div', class_='cat')

# String 활용 : 'raining'이라는 문자열이 포함된 elements 검색(대/소문자 구별)
soup.find_all(string='raining')

# 대/소문자 구별 없이 'raining' 포함여부 검색
soup.find_all(string=lambda text: 'raining' in text.lower())

# .string 속성을 받아온 것이기 때문에 tag를 통해 elements로 받기
soup.find_all('h3', string='raining')

# 정보얻기 : text 속성을 활용하여 내부 문자 검색 - 'p' tag 내부 글 검색
<p class='cat'>This is a p-cat</p>

cat_el = soup.find('p', class_='cat')

cat_el.text #=> 'This is a p-cat'

# python의 strip method를 활용한 띄어쓰기 정리
cat_el.text.strip()

 

■ Reference

  ㆍHTTP 상태 코드 : https://developer.mozilla.org/ko/docs/Web/HTTP/Status

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

  ㆍParsing : https://na27.tistory.com/230

 

Parsing (파싱) 이란? Parser (파서) 란?

Parsing (파싱) 이란? Parser (파서) 란? Parsing 언어학에서 parsing은 구문 분석이라고도하며 문장을 그것을 이루고 있는 구성 성분으로 분해하고 그들 사이의 위계 관계를 분석하여 문장의 구조를 결정

na27.tistory.com

 

  ㆍrequests와 urllib.request의 차이  : https://0ver-grow.tistory.com/1008

 

requests와 urllib.request의 차이는 뭘까?

requests urllib.request 데이터를 보낼 때 딕셔너리 형태로 보낸다 데이터를 보낼 때 인코딩하여 바이너리 형태로 보낸다 없는 페이지를 요청해도 에러를 띄우지 않는다 없는 페이지를 요청해도 에러

0ver-grow.tistory.com