A A
[Data Analysis] 데이터 μˆ˜μ§‘ (Crawling, Scrapping)

데이터 μˆ˜μ§‘

크둀링 (Crawling)

크둀링(crawling)은 μžλ™ν™”λœ 슀크립트λ₯Ό μ‚¬μš©ν•˜μ—¬ μ›Ή νŽ˜μ΄μ§€λ₯Ό νƒμƒ‰ν•˜κ³  데이터λ₯Ό μˆ˜μ§‘ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.
  • μ΄λŠ” μ›Ή μ‚¬μ΄νŠΈ 전체 λ˜λŠ” λ‹€μˆ˜μ˜ νŽ˜μ΄μ§€λ₯Ό νƒμƒ‰ν•˜λ©΄μ„œ 데이터λ₯Ό μΆ”μΆœν•˜λŠ” κ³Όμ •μœΌλ‘œ, 검색 μ—”μ§„μ˜ ν¬λ‘€λŸ¬μ™€ μœ μ‚¬ν•©λ‹ˆλ‹€.
  • μžλ™ν™”λœ 슀크립트: ν¬λ‘€λŸ¬λŠ” νŠΉμ • κ·œμΉ™μ— 따라 μ›Ή νŽ˜μ΄μ§€λ₯Ό λ°©λ¬Έν•˜κ³ , ν•΄λ‹Ή νŽ˜μ΄μ§€μ˜ μ½˜ν…μΈ λ₯Ό μˆ˜μ§‘ν•˜λŠ” μžλ™ν™”λœ μŠ€ν¬λ¦½νŠΈμž…λ‹ˆλ‹€.
  • 전체 μ‚¬μ΄νŠΈ λ˜λŠ” λ‹€μˆ˜μ˜ νŽ˜μ΄μ§€ 탐색: 크둀링은 일반적으둜 전체 μ›Ή μ‚¬μ΄νŠΈλ₯Ό νƒμƒ‰ν•˜κ±°λ‚˜, λ‹€μˆ˜μ˜ νŽ˜μ΄μ§€λ₯Ό 순차적으둜 λ°©λ¬Έν•˜μ—¬ 데이터λ₯Ό μˆ˜μ§‘ν•©λ‹ˆλ‹€.
  • 검색 μ—”μ§„μ˜ ν¬λ‘€λŸ¬μ™€ μœ μ‚¬: 검색 엔진 ν¬λ‘€λŸ¬λŠ” μ›Ή νŽ˜μ΄μ§€λ₯Ό μΈλ±μ‹±ν•˜μ—¬ 검색 κ²°κ³Όλ₯Ό μ œκ³΅ν•˜λŠ”λ°, 이와 μœ μ‚¬ν•œ λ°©μ‹μœΌλ‘œ 데이터λ₯Ό μˆ˜μ§‘ν•©λ‹ˆλ‹€.

 

μŠ€ν¬λž˜ν•‘ (Scrapping)

μŠ€ν¬λž˜ν•‘(scraping)은 νŠΉμ • μ›Ή νŽ˜μ΄μ§€μ—μ„œ ν•„μš”ν•œ 데이터λ₯Ό μΆ”μΆœν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€. μ΄λŠ” μ›Ή 크둀링의 ν•˜μœ„ μ§‘ν•©μœΌλ‘œ, νŠΉμ • λ°μ΄ν„°λ§Œ μˆ˜μ§‘ν•˜λŠ” 데 μ§‘μ€‘ν•©λ‹ˆλ‹€.
  • νŠΉμ • μ›Ή νŽ˜μ΄μ§€μ—μ„œ ν•„μš”ν•œ 데이터 μΆ”μΆœ: μŠ€ν¬λž˜ν•‘μ€ μ›Ή νŽ˜μ΄μ§€μ˜ νŠΉμ • μš”μ†Œλ‚˜ 정보λ₯Ό μΆ”μΆœν•˜λŠ” κ³Όμ •μž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ œν’ˆ 가격, 리뷰, μ—°λ½μ²˜ 정보 등을 μΆ”μΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ›Ή 크둀링의 ν•˜μœ„ 집합: μŠ€ν¬λž˜ν•‘μ€ 크둀링의 μΌλΆ€λ‘œμ„œ, ν¬λ‘€λŸ¬κ°€ νƒμƒ‰ν•œ μ›Ή νŽ˜μ΄μ§€μ—μ„œ νŠΉμ • 데이터λ₯Ό μΆ”μΆœν•˜λŠ” μž‘μ—…μ„ μ˜λ―Έν•©λ‹ˆλ‹€.

데이터 μˆ˜μ§‘ μ˜ˆμ‹œ (Scrapping)

빨간색 ν…Œλ‘λ¦¬ μ•ˆμ— μžˆλŠ” 데이터λ₯Ό μŠ€ν¬λž˜ν•‘ ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

  • Example Code
import requests
from bs4 import BeautifulSoup

# AI Hub νŽ˜μ΄μ§€ URL
url = 'https://www.aihub.or.kr/'

# μ›Ή νŽ˜μ΄μ§€ μš”μ²­
response = requests.get(url) response.raise_for_status() # μš”μ²­μ΄ μ„±κ³΅ν–ˆλŠ”μ§€ 확인

# BeautifulSoup 객체 생성
soup = BeautifulSoup(response.content, 'html.parser')

# 인기 데이터 TOP3 μ„Ήμ…˜ μ°ΎκΈ°
top3_section = soup.find('div', class_='secR')

# 각 데이터 ν•­λͺ© μΆ”μΆœ
data_list = top3_section.find_all('div', class_='list')

# 데이터 제λͺ© μΆ”μΆœ titles = []
for data in data_list:
   title = data.find('h3').get_text(strip=True)
   clean_title = title.split(']')[-1].strip()
   titles.append(clean_title)
   
# μΆ”μΆœν•œ 데이터 좜λ ₯
for idx, title in enumerate(titles, start=1):
   print(f"TOP {idx}: {title}")
  • Output
OUTPUT
TOP 1: 감성 λŒ€ν™” λ§λ­‰μΉ˜
TOP 2: μŒμ‹ 이미지 및 μ˜μ–‘μ •λ³΄ ν…μŠ€νŠΈ
TOP 3: ν•œκ΅­μ–΄ μŒμ„±

Crawling(크둀링) & Scrapping(μŠ€ν¬λž˜ν•‘)을 μœ„ν•œ 도ꡬ

requests

  • κ°œλ…: HTTP μš”μ²­μ„ 보내고 응닡을 λ°›κΈ° μœ„ν•œ 파이썬 라이브러리 μž…λ‹ˆλ‹€.
  • μš©λ„: μ›Ή νŽ˜μ΄μ§€μ˜ HTML을 κ°€μ Έμ˜€κ±°λ‚˜, API μš”μ²­μ„ λ³΄λ‚΄λŠ” 데 μ‚¬μš©ν•©λ‹ˆλ‹€.

BeautifulSoup

  • κ°œλ…: HTML 및 XML λ¬Έμ„œλ₯Ό νŒŒμ‹±ν•˜μ—¬ μ›ν•˜λŠ” 데이터λ₯Ό μΆ”μΆœν•˜κΈ° μœ„ν•œ 파이썬 라이브러리 μž…λ‹ˆλ‹€.
  • μš©λ„: μ›Ή νŽ˜μ΄μ§€μ—μ„œ νŠΉμ • μš”μ†Œλ‚˜ ν…μŠ€νŠΈ 데이터λ₯Ό μΆ”μΆœν•˜λŠ” 데 μ‚¬μš©ν•©λ‹ˆλ‹€.

Scrapy

  • κ°œλ…: 크둀링 및 μŠ€ν¬λž˜ν•‘μ„ μœ„ν•œ 파이썬 ν”„λ ˆμž„μ›Œν¬ μž…λ‹ˆλ‹€.
  • μš©λ„: λŒ€κ·œλͺ¨ μ›Ή 크둀링 μž‘μ—…μ„ κ΅¬μ‘°ν™”λœ λ°©μ‹μœΌλ‘œ μˆ˜ν–‰ν•˜λŠ” 데 μ‚¬μš©. 정적 μ›Ή νŽ˜μ΄μ§€ 크둀링에 강점이 μžˆμŠ΅λ‹ˆλ‹€.

Selenium

  • κ°œλ…: μ›Ή λΈŒλΌμš°μ € μžλ™ν™”λ₯Ό μœ„ν•œ 파이썬 라이브러리 μž…λ‹ˆλ‹€.
  • μš©λ„: μ‹€μ œ λΈŒλΌμš°μ €λ₯Ό μ œμ–΄ν•˜μ—¬ 동적인 μ›Ή νŽ˜μ΄μ§€μ—μ„œ 데이터λ₯Ό μˆ˜μ§‘ν•˜κ±°λ‚˜, λΈŒλΌμš°μ € μƒν˜Έμž‘μš©μ„ μžλ™ν™”ν•˜λŠ” 데 μ‚¬μš©ν•©λ‹ˆλ‹€.

Example Code

Scrapping 라이브러리 μ‚¬μš© 예제

import requests
from bs4 import BeautifulSoup

# AI Hub νŽ˜μ΄μ§€ URL
url = 'https://www.aihub.or.kr/'

# μ›Ή νŽ˜μ΄μ§€ μš”μ²­
response = requests.get(url)
response.raise_for_status()  # μš”μ²­μ΄ μ„±κ³΅ν–ˆλŠ”μ§€ 확인

# BeautifulSoup 객체 생성
soup = BeautifulSoup(response.content, 'html.parser')

# 인기 데이터 TOP3 μ„Ήμ…˜ μ°ΎκΈ°
top3_section = soup.find('div', class_='secR')

# 각 데이터 ν•­λͺ© μΆ”μΆœ
data_list = top3_section.find_all('div', class_='list')

# 데이터 제λͺ© μΆ”μΆœ
titles = []
for data in data_list:
    title = data.find('h3').get_text(strip=True)
    clean_title = title.split(']')[-1].strip()
    titles.append(clean_title)

# μΆ”μΆœν•œ 데이터 좜λ ₯
for idx, title in enumerate(titles, start=1):
    print(f"TOP {idx}: {title}")
TOP 1: 객체 κ°„ 관계성 μΈμ§€μš© ν•œκ΅­ν˜• λΉ„μ „ 데이터
TOP 2: μ†βˆ™νŒ” ν˜‘μ‘°μ— μ˜ν•œ νŒŒμ§€-μ‘°μž‘ λ™μž‘ 데이터
TOP 3: μƒμš© μžμœ¨μ£Όν–‰μ°¨ μ•Όκ°„ μžλ™μ°¨ μ „μš©λ„λ‘œ 데이터

Requests 라이브러리 및 BeautifulSoup μ‚¬μš© 예제

import requests
from bs4 import BeautifulSoup

# 1. μ›Ή νŽ˜μ΄μ§€ μš”μ²­
url = 'https://ko.wikipedia.org/wiki/μœ„ν‚€λ°±κ³Ό:λŒ€λ¬Έ'
response = requests.get(url)

# 2. μš”μ²­μ΄ μ„±κ³΅ν–ˆλŠ”μ§€ 확인
if response.status_code == 200:
    # 3. BeautifulSoup 객체 생성
    soup = BeautifulSoup(response.content, 'html.parser')

    # 4. νŽ˜μ΄μ§€ 제λͺ© μΆ”μΆœ
    title = soup.find('h1', id='firstHeading').text
    print(f"Title: {title}")

    # 5. 첫 번째 단락 μΆ”μΆœ
    first_paragraph = soup.find('p').text
    print(f"First paragraph: {first_paragraph}")
else:
    print(f"Failed to retrieve the web page. Status code: {response.status_code}")
Title: μœ„ν‚€λ°±κ³Ό:λŒ€λ¬Έ
First paragraph:  μœ„ν‚€λ°±κ³Ό

Selenium 라이브러리 μ‚¬μš© 예제

!pip install selenium
from selenium import webdriver
from selenium.webdriver.common.by import By

# URL μ„€μ •
URL = "https://ko.wikipedia.org/wiki/μœ„ν‚€λ°±κ³Ό:λŒ€λ¬Έ"

# Chrome μ˜΅μ…˜ μ„€μ •
options = webdriver.ChromeOptions()
options.add_argument("--headless") # λΈŒλΌμš°μ € 창을 λ„μš°μ§€ μ•ŠμŒ
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--no-sandbox")

# μ›Ή λ“œλΌμ΄λ²„ μ„€μ •
driver = webdriver.Chrome(options=options)

try:
    # μœ„ν‚€λ°±κ³Ό λŒ€λ¬Έ νŽ˜μ΄μ§€ μ—΄κΈ°
    driver.get(URL)

    # "우리 λͺ¨λ‘κ°€ λ§Œλ“€μ–΄κ°€λŠ” 자유 백과사전"κ³Ό "λ¬Έμ„œ μ΄ν•˜ λ‚΄μš©" μΆ”μΆœ
    main_content = driver.find_element(By.CSS_SELECTOR, "#mw-content-text > div.mw-content-ltr.mw-parser-output > div.main-box.main-top > div > p:nth-child(2)").text
    print("Main Content:", main_content)
finally:
    # μ›Ή λ“œλΌμ΄λ²„ μ’…λ£Œ
    driver.quit()
Main Content: 우리 λͺ¨λ‘κ°€ λ§Œλ“€μ–΄κ°€λŠ” 자유 백과사전
λ¬Έμ„œ 674,592κ°œμ™€ 졜근 κΈ°μ—¬μž 1,814λͺ…

Scrapy 라이브러리 μ‚¬μš© 예제

# @title
!pip install scrapy
# @title
!scrapy startproject wikipedia_scraper
# @title
# wikipedia_scraper/spiders/wikipedia_spider.py
import scrapy

class WikipediaSpider(scrapy.Spider):
    name = "wikipedia"
    start_urls = [
        'https://ko.wikipedia.org/wiki/μœ„ν‚€λ°±κ³Ό:λŒ€λ¬Έ',
    ]

    def parse(self, response):
        main_content = response.css('#mw-content-text > div.mw-content-ltr.mw-parser-output > div.main-pane > div.main-pane-right > div.wikipedia-ko.main-recommended.main-box').get()
        yield {
            'main_content': main_content,
        }
# @title
%%writefile -a wikipedia_scraper/wikipedia_scraper/settings.py
ROBOTSTXT_OBEY = False
import sys
import os

# ν˜„μž¬ μž‘μ—… 디렉토리λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
current_dir = os.getcwd()

# ν”„λ‘œμ νŠΈ 디렉토리 μ„€μ • (wikipedia_scraper ν΄λ”μ˜ μƒμœ„ 폴더)
project_dir = os.path.join(current_dir, 'wikipedia_scraper')

# ν”„λ‘œμ νŠΈ 디렉토리λ₯Ό PYTHONPATH에 μΆ”κ°€ν•©λ‹ˆλ‹€.
sys.path.append(project_dir)
!pwd
from scrapy.crawler import CrawlerProcess
from wikipedia_scraper.spiders.wikipedia_spider import WikipediaSpider

process = CrawlerProcess({
    'USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
    'FEEDS': {
        'output.json': {
            'format': 'jsonlines',
            'encoding': 'utf8',
            'store_empty': False,
            'fields': None,
            'indent': 4,
        },
    },
})

process.crawl(WikipediaSpider)
process.start()
# @title
!python run_scrapy.py
# @title
# 좜λ ₯물을 예쁘게 좜λ ₯
import json
from pprint import pprint

data = []
with open('output.json', 'r') as f:
    for line in f:
        line = line.strip()  # 곡백 제거
        if line:  # 빈 쀄 κ±΄λ„ˆλ›°κΈ°
            try:
                data.append(json.loads(line))
            except json.JSONDecodeError as e:
                print(f"Error decoding JSON: {e}")

pprint(data)