Link Search Menu Expand Document
정적 페이지 크롤링
Table of contents
  1. 정적 페이지 크롤링하기
    1. 웹 페이지의 HTML 소스 갖고 오기
      1. requests 라이브러리
      2. BeautifulSoup 라이브러리
      3. parsing 파싱
      4. prettify()
    2. 원하는 요소 찾기
      1. find
      2. CSS 선택자(selector)
      3. 텍스트만 깔끔하게 추출하기 위해서
    3. 정적 웹 페이지에서 이미지 가져오기
      1. 하나의 이미지 내려받기
      2. reshot에서 이미지 여러 개 내려받기

정적 페이지 크롤링하기

웹 크롤링을 하는 경우 대부분은 정적 페이지를 크롤링하는 경우가 많다. 이때 정적 페이지는 말 그대로 움직이지 않는 페이지를 말한다. 버튼을 누르는 등의 동적인 행동이 없는 경우에 정적 페이지라 하고 정적 페이지를 크롤링하기 위해서는 주로 requests와 BeautifulSoup 를 많이 사용한다.

웹 페이지의 HTML 소스 갖고 오기

웹 페이지의 HTML 소스를 가져오기 위해서는 requests와 BeautifulSoup 라이브러리가 필요하다.

requests 라이브러리

Requests: HTTP for Humans™ - Requests 2.25.0 documentation

기본 예시

import requests

html = requests.get("https://www.google.co.kr")
html
html.text[0:100]

# '<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ko"><head><meta content'
  • 접속이 잘 된 경우에는 <Response [200]> 을 확인할 수 있다
  • 그 외 HTTP 상태코드는 이 곳에서 확인할 수 있다
  • request.get() 을 이용해 가져온 html 소스 뒤에 .text 를 붙이면 소스 코드를 확인할 수 있다

BeautifulSoup 라이브러리

기본 예시

from bs4 import BeautifulSoup

# 테스트용 html 코드
html = **"""**<html><body><div><span>**\**
        <a href=http://www.naver.com>naver</a>**\**
        <a href=https://www.google.com>google</a>**\**
        <a href=http://www.daum.net/>daum</a>**\**
        </span></div></body></html>**"""**

# soup = BeautifulSoup(html, 'html.parser')
soup = BeautifulSoup(html, 'lxml')
soup

# <html><body><div><span> <a href="http://www.naver.com">naver</a> <a href="https://www.google.com">google</a> <a href="http://www.daum.net/">daum</a> </span></div></body></html>

parsing 파싱

HTML 코드를 분석하기 위해서는 HTML 코드 구문을 이해하고 요소별로 HTML 코드를 분류해야 한다. HTML 소스를 처리하는 파서(parser)에는 html.parser와 lxml이 있다. lxml이 html.parser에 비해 좀 더 빠르기 때문에 lxml을 쓰는 것을 추천한다.

soup = BeautifulSoup(html, 'html.parser')
soup = BeautifulSoup(html, 'lxml')

prettify()

HTML 소스를 HTML 구조의 형태로 예쁘게 확인하고 싶을 때 사용한다.

print(soup.prettify())

<html>
 <body>
  <div>
   <span>
    <a href="http://www.naver.com">
     naver
    </a>
    <a href="https://www.google.com">
     google
    </a>
    <a href="http://www.daum.net/">
     daum
    </a>
   </span>
  </div>
 </body>
</html>

원하는 요소 찾기

requests와 BeautifulSoup를 통해 html을 가져오고 parsing을 통해 html 코드를 분류했다면 이제는 크롤링하고자 하는 요소를 찾아야 한다. 이를 찾기 위해서는 크롬 브라우저를 기준으로 오른쪽 버튼 -> 검사 버튼을 이용해 개발자 도구에 접근해야 한다. 그래서 크롤링하고자 하는 요소의 ID, class 등 그 요소의 구조, 특징을 찾아 find, selector 를 이용하면 크롤링할 수 있다.

find

  • **soup.find("태그")**
    HTML 소스코드에서 해당 “태그”가 있는 첫 번째 요소를 찾아서 반환
      soup.find("a")
    
      # <a href="http://www.naver.com">naver</a>
    
  • **soup.find("태그").get_text()**
    HTML 소스코드에서 해당 “태그”가 있는 첫 번째 요소 하나만 찾아서 텍스트 부분만 추출할 때 이용
      soup.find("a").get_text()
    
      # naver
    
  • **soup.find_all("태그")**
    HTML 소스코드에서 해당 “태그”가 있는 모든 요소를 찾아서 리스트로 반환
      soup.find_all("a")
    
      # [<a href="http://www.naver.com">naver</a>,
      #  <a href="https://www.google.com">google</a>,
      #  <a href="http://www.daum.net/">daum</a>]
    
  • 여러 태그에서 텍스트 부분만 추출하기 ⇒ for문 이용

    site_names = soup.find_all('a')
    for site_name in site_names:
        print(site_name.get_text())
    
    # naver
    # google
    # daum
    
  • class나 id 속성을 이용해 찾고 싶을 때

    a = soup.find('태그', {"class":"class_속성값"})
    b = soup.find('태그', {"id":"id_속성값"})
    

CSS 선택자(selector)

  • 태그 안에 속성이 class인 경우 태그.class_속성값 속성이 id인 경우 태그#id_속성값
  • soup.select("태그 및 속성")

    HTML 소스코드에서 해당 “태그 및 속성”이 있는 모든 요소를 찾아서 리스트로 반환

    soup2.select("body h1")
    # [<h1>책 정보</h1>]
    
  • soup.select_one("태그 및 속성")

    HTML 소스코드에서 해당 “태그 및 속성”이 있는 첫 번째 요소를 찾아서 반환

  • soup.select_one("태그 및 속성").text

    HTML 소스코드에서 해당 “태그 및 속성”이 있는 첫 번째 요소 하나만 찾아서 텍스트 부분만 추출할 때 이용

  • 여러 태그에서 텍스트 부분만 추출하기 ⇒ for문 이용

    site_names = soup.select('a')
    for site_name in site_names:
        print(site_name.text)
    
    # naver
    # google
    # daum
    

✅ 꼭 알아두기!

  • > 부모-자식

  • 띄어쓰기 부모-자손

CSS 선택자에서는 띄어쓰기가 하위요소라는 뜻

⇒ 띄어쓰기가 더 큰 개념 (> 는 바로 아래줄, 띄어쓰기는 더 아래줄까지 포함)

ex.

  • soup.select("html body a")

    soup에서 html 안에 있는 body 안에 있는 a 태그를 선택

  • soup.select('#fruits1 > span.name')

    soup에서 html 안에 있는 fruits1이라는 아이디 바로 아래에 있는 span태그 중에서 클래스가 name인 요소를 선택

  • a.portal 같은 줄에 쓰여져 있다
  • a .portal 다른 줄에 쓰여져 있다

텍스트만 깔끔하게 추출하기 위해서

  • strip()
    텍스트 앞 뒤로 개행문자가 있는 경우에 사용
      a = "\nabc\n"
      print(a)
    
      #
      # abc
      #
    
      b = a.strip()
      print(b)
      # abc
    
  • replace("old", "new")

    old를 new로 바꿔준다

    a = "a,b,c"
    a.replace(",", "")
    # "abc"
    

정적 웹 페이지에서 이미지 가져오기

하나의 이미지 내려받기

import requests

# 이미지 주소 가져오기
url = 'https://www.python.org/static/img/python-logo.png'
html_image = requests.get(url)

import os

image_file_name = os.path.basename(url)

# 이미지를 다운받을 폴더 경로 지정하기
folder = 'C:/myPyCode/download'

# 이미지를 다운받을 폴더가 없는 경우에는 새 폴더 생성하기
if not os.path.exists(folder):
    os.makedirs(folder)

# 이미지 경로 지정하기 = 폴더 경로 + 이미지 파일 이름
image_path = os.path.join(folder, image_file_name)

# 이미지 데이터를 1000000 바이트씩 나눠서 내려받고 파일에 순차적으로 저장
chunk_size = 1000000
for chunk in html_image.iter_content(chunk_size):
    imageFile.write(chunk)
imageFile.close()

# 파일 잘 다운받았는지 확인하기
os.listdir(folder)

  • 이미지를 한 번에 다운받으면 오류가 났을 때 문제가 생길 수 있기 때문에 나눠서 다운받는다 chunk_size는 네트워크 속도에 따라 결정하면 된다

reshot에서 이미지 여러 개 내려받기

import requests
from bs4 import BeautifulSoup
import os

# 원하는 이미지들이 있는 url에서 HTML 소스 가져오기
URL = 'https://www.reshot.com/search/animal'
html_reshot_image = requests.get(URL).text
soup_reshot_image = BeautifulSoup(html_reshot_image, "lxml")

# 다운받고자 하는 이미지 요소들을 선택하기
reshot_image_elements = soup_reshot_image.select('a img')

# 이미지 주소에 해당하는 것만 추출하기
# 이미지 태그 중 세 번째에 해당하는 주소만 가져옴
# => 여러개의 이미지를 다운받기 위해서는 이때 for문을 이용하기
reshot_image_url = reshot_image_elements[2].get('src')

html_image = requests.get(reshot_image_url)

# 이미지를 다운받을 폴더 경로 지정하기
**f**older = "C:/myPyCode/download"

# 이미지를 다운받을 폴더가 없는 경우에는 새 폴더 생성하기
if not os.path.exists(folder):
    os.makedirs(folder)

# os.path.basename(URL)는 웹사이트나 폴더가 포함된 파일명에서 파일명만 분리하는 방법
imageFile = open(os.path.join(folder, os.path.basename(reshot_image_url)), 'wb')

# 이미지 데이터를 1000000 바이트씩 나눠서 저장하는 방법
chunk_size = 1000000
for chunk in html_image.iter_content(chunk_size):
    imageFile.write(chunk)
imageFile.close()


Page last modified: Nov 25 2020 at 12:11 AM.