jjinyeok 성장일지

웹크롤링 # 3 정적 페이지 크롤링 - 2022/08/03~2022/08/05 본문

[KT AIVLE School]

웹크롤링 # 3 정적 페이지 크롤링 - 2022/08/03~2022/08/05

jjinyeok 2022. 8. 8. 11:55

  크롤링하고 싶은 웹 서비스가 정적 페이지인 경우 즉, 이벤트가 발생할 때마다 URL이 변경되고 html 형식의 데이터를 response로 가져오는 경우 동적 페이지 크롤링 방법과 다른 방법으로 response로 HTML 형식의 문자열을 받아 BeautifulSoup 클래스를 사용하여 파싱하는 방법을 사용한다.

 

1. HTML이란?

  정적 페이지 크롤링에 대해 알기 위해서는 HTML에 대한 기본적 이해가 바탕이 되어야 한다. HTML은 웹 문서를 작성하는 언어이다. HTML의 구성은 다음과 같다.

  • Document : 한 페이지를 나타내는 코드
  • Element : 하나의 레이아웃 -> Element가 모여서 Document를 만듬
  • Tag : Element의 종류를 정의 -> 시작 태그 + 끝 태그 == Element
  • Attribute : 속성값 -> 시작 태그에서 태그의 기능을 정의함
    • id : Element를 지정하는 페이지 내에서 유일한 값
    • class : Element를 지정하는 값으로 페이지 내에서 여러개 사용 가능
    • attr : id, class를 제외한 나머지 속성값
  • Text : 시작 태그와 끝 태그 사이의 문자열

  HTML의 Element는 계층적인 구조를 지닌다.

 

2. CSS Selector란?

  CSS는 HTML에 스타일을 넣기 위한 언어이다. 따라서 CSS는 스타일을 적용시킬 HTML Element를 선택해야한다. 이때 사용하는 방법이 CSS Selector이다. CSS Selector가 Element를 선택하는 방법은 다음과 같다.

  1. Element Tag 이름으로 선택
    <span>kt에이블스쿨</span> -> span
  2. Tag의 id 값으로 선택
    <span id="aivle">kt에이블스쿨</span> -> #kt
  3. Tag의 class 값으로 선택
    <span class="kt aivle1">kt에이블스쿨1</span>
    <span class="kt aivle2">kt에이블스쿨2</span>

    -> .aivle2 -> kt에이블스쿨2 선택
    -> .kt -> kt에이블스쿨1, kt에이블스쿨2 선택
  4. Tag의 attr 값으로 선택
    <span value="aivle">kt에이블스쿨</span> -> [value: "aivle"]
  5. 여러개의 Element 선택 중 특정 Element 제외하여 선택 (not)
    <span class="kt aivle1">kt에이블스쿨1</span>
    <span class="kt aivle2">kt에이블스쿨2</span>
    <span class="kt aivle3">kt에이블스쿨3</span>
    -> .kt:not(.aivle2) -> kt에이블스쿨1, kt에이블스쿨3 선택
  6. n 번째 Element 선택 (nth-child)
    <span class="kt aivle1">kt에이블스쿨1</span>
    <span class="kt aivle2">kt에이블스쿨2</span>
    <span class="kt aivle3">kt에이블스쿨3</span>
    -> .kt:nth-child(2) -> kt에이블스쿨2 선택
    이때 가장 중요한 것은 .kt Element 중 2번째를 선택하는 것이 아닌 2번째 Element가 .kt 클래스를 갖는가를 판단하여 Select한다는 것이다.
  7. 계층적으로 Element 선택 (> 띄어쓰기)
    <div class="wrap">

        <span class="kt aivle1">kt에이블스쿨1</span>
        <div>
            <span class="kt aivle2">kt에이블스쿨2</span>
        </div>
    </div>
    -> .wrap > p : kt에이블스쿨1 선택
    -> .wrap p : kt에이블스쿨1, kt에이블스쿨2 선택
    '>' 를 통해 바로 다음 계층의 Element를 선택할 수 있고, '띄어쓰기'를 통해 다음 계층들의 Element들을 선택할 수 있다.
  8. 여러개의 Element 선택 (,)
    <span class="kt aivle1">kt에이블스쿨1</span>
    <span class="kt aivle2">kt에이블스쿨2</span>
    <span class="kt aivle3">kt에이블스쿨3</span>
    -> .kt.aivle1, .kt.aivle3 -> kt에이블스쿨1, kt에이블스쿨3 선택

 

3. BeautifulSoup란?

  동적 페이지 크롤링과는 다르게 정적 페이지 크롤링에서는 request에 대해 HTML 형식의 문자열을 response 받는다. 따라서 json 형식의 문자열과는 다르게 response로부터 데이터를 꺼내는 과정이 필요하다. 이떄 bs4 패키지의 BeautifulSoup를 사용한다. BeautifulSoup는 HTML 형식의 문자열 데이터를 BeautifulSoup 객체로 변환시킨다. BeautifulSoup 객체는 CSS Selector를 이용하여 Element를 선택할 수 있다. 따라서 HTML의 Element를 통한 데이터 크롤링이 가능하게 된다.

 

4. 정적페이지(request html) 웹 크롤링 프로세스

  1. 웹 서비스 분석 : 크롬 개발자 도구를 통해 URL을 찾아냄
  2. request(url) -> response(html) : html(str) : URL을 통해 request를 보내고 html 형식의 문자열을 response 받음
  3. html(str) -> BeautifulSoup -> CSS Selector -> DataFrame : 문자열 형식의 html을 BeautifulSoup 객체로 바꾸고 데이터프레임 자료형으로 재가공함

  다음 과정을 네이버 연관 검색어 데이터를 크롤링하는 예시를 통해 한번 더 정리하겠다.

 

  1. 웹 서비스 분석

  먼저 네이버 연관 검색어 데이터를 크롤링할 수 있는 웹 서비스를 분석한다. 네이버에 kt를 검색했을 때 https://m.search.naver.com/search.naver?query=kt URL로 request를 보내고 html을 response 받는다는 것을 알 수 있다.

response가 html 형식 같은 경우 구글 개발자 콘솔 미리보기에 css를 적용하기 전의 html 페이지를 보여준다.

 

  2. request(url) -> response(html) : html(str)

  과정은 다음과 같다.

import requests

keyword = 'kt'
url = f'https://m.search.naver.com/search.naver?query={keyword}'
response = requests.get(url)
print(response)
# <Response [200]>

 

  3. html(str) -> BeautifulSoup -> DataFrame or Text

  연관 검색어 데이터를 크롤링하기 위해서는 response로 받은 문자열에서 연관 검색어 Element들을 추출하고 재가공해야 한다. 구글 개발자 콘솔을 이용해 연관 검색어의 Element를 알 수 있다. 그 과정은 다음과 같다.

  1. 검사할 페이지 요소 선택 클릭
  2. 검사할 페이지 요소 클릭
  3. 요소에 나오는 Element를 분석하여 CSS Selector 추출

구글 개발자 콘솔을 이용해 간단하게 원하는 Element의 CSS Selector를 찾을 수 있다.

  이 과정을 통해 연관 검색어들의 CSS Selctor가 #_related_keywords_aside > div > div > div > a라는 것을 알 수 있다. 이 정보를 이용해서 연관검색어 데이터를 수집하는 과정은 다음과 같다.

from bs4 import BeautifulSoup

dom = BeautifulSoup(response.text, 'html.parser')
elements = dom.select('#_related_keywords_aside > div > div > div > a')
keywords = [element.text for element in elements]
print(keywords)
# ['삼성전자', 'kt 고객센터', 'ky', 'kt 인터넷', 'kt 대리점', '환율', 'SKT', 'kr', 'ktwiz', '날씨']

  이 과정을 keyword를 parameter로 하고 keyword에 대한 연관검색어들을 return하는 함수로 구현한 코드는 다음과 같다.

def find_related_search_terms(keyword):
    """This function is crawling related search keywords from NAVER search service.
    
    Params
    ------
    keyword: str: keyword
    
    Return
    ------
    type: list: list of related search terms
    """

    import requests
    from bs4 import BeautifulSoup
    url = f'https://m.search.naver.com/search.naver?query={keyword}'
    response = requests.get(url)
    dom = BeautifulSoup(response.text, 'html.parser')
    elements = dom.select('#_related_keywords_aside > div > div > div > a')
    keywords = [element.text for element in elements]
    return keywords


print(find_related_search_terms('ai'))
# ['ai 영화', 'ai 인공지능', '영화 ai', '인공지능', 'ai 뜻', 'artificial', 'ai 약자', 'ai 첫인상', 'artificial intelligence', '에이아이']
Comments