python

ํฌ๋กค๋ง(Crawling)

dddzr 2026. 1. 25. 13:22

๐Ÿ” 1. ํฌ๋กค๋ง(Crawling)์ด๋ž€?

์›น ํฌ๋กค๋ง์€ ์ธํ„ฐ๋„ท ์ƒ์˜ ์›น ํŽ˜์ด์ง€๋ฅผ ์ž๋™์œผ๋กœ ํƒ์ƒ‰ํ•˜๊ณ , ๊ทธ ๋‚ด์šฉ์„ ์ˆ˜์ง‘ํ•˜๋Š” ๊ธฐ์ˆ  ๋˜๋Š” ๊ณผ์ •! ๋ณดํ†ต ๊ฒ€์ƒ‰ ์—”์ง„(์˜ˆ: ๊ตฌ๊ธ€)์ด๋‚˜ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ๋ด‡์ด ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค.

 

๐Ÿ“Œ ํฌ๋กค๋ง์˜ ๊ธฐ๋ณธ ์›๋ฆฌ

1๏ธโƒฃ ํฌ๋กค๋Ÿฌ(bot, spider)๊ฐ€ ์‹œ์ž‘ URL(์˜ˆ: https://example.com)์— ์ ‘์†
2๏ธโƒฃ ํ•ด๋‹น ํŽ˜์ด์ง€์˜ HTML ๋ฌธ์„œ๋ฅผ ์ฝ๊ณ , ๋‚ด๋ถ€์— ์žˆ๋Š” ๋งํฌ๋“ค์„ ์ˆ˜์ง‘
3๏ธโƒฃ ์ˆ˜์ง‘ํ•œ ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด์„œ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋„ ํƒ์ƒ‰
4๏ธโƒฃ ์ด ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ์ ์  ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘

 

๐Ÿ“Œ ํฌ๋กค๋ง์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

โœ… ์›น์‚ฌ์ดํŠธ์˜ ์ •๋ณด๋ฅผ ์ž๋™์œผ๋กœ ๋ชจ์œผ๊ธฐ ์œ„ํ•ด
โœ… ์ƒํ’ˆ ๊ฐ€๊ฒฉ, ๋‰ด์Šค ๊ธฐ์‚ฌ, ๊ฒŒ์‹œ๊ธ€ ๋“ฑ์„ ์ˆ˜์ง‘ํ•ด ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด
โœ… ๊ฒ€์ƒ‰ ์—”์ง„์ด ์›น์‚ฌ์ดํŠธ๋ฅผ ์ธ๋ฑ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด

 

๐Ÿ“Œ ํฌ๋กค๋ง vs ์Šคํฌ๋ž˜ํ•‘

  • ํฌ๋กค๋ง: ์›นํŽ˜์ด์ง€๋ฅผ ๋Œ์•„๋‹ค๋‹ˆ๋ฉฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์œผ๋Š” ๊ณผ์ •
  • ์Šคํฌ๋ž˜ํ•‘: ๊ทธ ํŽ˜์ด์ง€ ์•ˆ์—์„œ ์›ํ•˜๋Š” ์ •๋ณด๋ฅผ ๋ฝ‘์•„๋‚ด๋Š” ์ž‘์—…
ํ•ญ๋ชฉ ํฌ๋กค๋ง ์Šคํฌ๋ž˜ํ•‘
๋ชฉ์  ํŽ˜์ด์ง€ ํƒ์ƒ‰ ๋ฐ ๋งํฌ ์ˆ˜์ง‘ ํŽ˜์ด์ง€์—์„œ ํŠน์ • ์ •๋ณด ์ถ”์ถœ
์˜ˆ์‹œ ์—ฌ๋Ÿฌ ๋‰ด์Šค ์‚ฌ์ดํŠธ ์ˆœํšŒ ๋‰ด์Šค ๊ธฐ์‚ฌ์—์„œ ์ œ๋ชฉ/๋ณธ๋ฌธ ์ถ”์ถœ

 

๐Ÿ“Œ 2. ํฌ๋กค๋ง ๊ตฌํ˜„ ๋ฐฉ๋ฒ• (ํŒŒ์ด์ฌ)

ํŒŒ์ด์ฌ์—์„œ ํฌ๋กค๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•œ๋‹ค.


โœ… ํฌ๋กค๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • BeautifulSoup → ์ •์  HTML์—์„œ ๋ฐ์ดํ„ฐ ์ถ”์ถœ
  • Selenium → ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ (JS ๋ Œ๋”๋ง๋œ ํŽ˜์ด์ง€)
  • Scrapy → ๋Œ€๊ทœ๋ชจ ํฌ๋กค๋ง์— ์ ํ•ฉ

 

โœ… 2-1. ๊ธฐ๋ณธ ์›น ํฌ๋กค๋ง ๋ฐฉ์‹ (requests & BeautifulSoup)

  • requests.get(url)์„ ์‚ฌ์šฉํ•˜๋ฉด ์›น์‚ฌ์ดํŠธ์˜ HTML์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
import requests
from bs4 import BeautifulSoup

url = "https://example.com"
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, headers=headers)

soup = BeautifulSoup(response.text, "html.parser")
print(soup.prettify())  # ์‚ฌ์ดํŠธ์˜ HTML ์ฝ”๋“œ ์ถœ๋ ฅ

 

โœ… 2-2. ๋™์  ์›นํŽ˜์ด์ง€ ํฌ๋กค๋ง (Selenium)

  • requests๋Š” HTML ์ฝ”๋“œ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์—, JavaScript๋กœ ๋ Œ๋”๋ง๋˜๋Š” ๋ฐ์ดํ„ฐ(์˜ˆ: ์œ ํŠœ๋ธŒ ๋Œ“๊ธ€, ์ฟ ํŒก ์ƒํ’ˆ ๋ชฉ๋ก ๋“ฑ)๋Š” ํฌ๋กค๋งํ•  ์ˆ˜ ์—†์Œ!
  • Selenium์„ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ๊ธฐ ๋•Œ๋ฌธ์— JavaScript๋กœ ๋ Œ๋”๋ง๋œ ๋ฐ์ดํ„ฐ๋„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ! 
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# ํฌ๋กฌ ๋“œ๋ผ์ด๋ฒ„ ์‹คํ–‰
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service)

# ์›น์‚ฌ์ดํŠธ ์ ‘์†
driver.get("https://example.com")
time.sleep(2)  # ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋”ฉ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ

# HTML ๊ฐ€์ ธ์˜ค๊ธฐ
html = driver.page_source
print(html)

#๋‰ด์Šค ์ œ๋ชฉ ๊ฐ€์ ธ์˜ค๊ธฐ
titles = driver.find_elements(By.CLASS_NAME, "sa_text_strong")
print("๐Ÿ“ฐ ๋„ค์ด๋ฒ„ ๋‰ด์Šค ์ œ๋ชฉ:")
for idx, title in enumerate(titles, 1):
    print(f"{idx}. {title.text}")

# ๋ธŒ๋ผ์šฐ์ € ์ข…๋ฃŒ
driver.quit()

โœ” find_elements(By.CLASS_NAME, "ํด๋ž˜์Šค์ด๋ฆ„") ์œผ๋กœ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ.
โœ” driver.quit()์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋‹ซ์•„์•ผ ํ•จ.

 

๐Ÿ”ฅ 2-3. ํฌ๋กค๋ง ๋ฐฉ์‹ ๋น„๊ต

๋ฐฉ์‹ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
requests + BeautifulSoup ์„œ๋ฒ„์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •์ ์ธ HTML requests, BeautifulSoup
Selenium JavaScript๋กœ ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ (์˜ˆ: ๋ฌดํ•œ ์Šคํฌ๋กค) Selenium, webdriver
Scrapy ๋Œ€๋Ÿ‰์˜ ์›นํŽ˜์ด์ง€์—์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ (ํฌ๋กค๋ง ์†๋„ ๋น ๋ฆ„) Scrapy

๐Ÿ‘‰ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ํฌ๋กค๋งํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํƒ!

๐Ÿ“Œ3. ํฌ๋กค๋ง ์ฃผ์˜์‚ฌํ•ญ

โœ…3-1. robots.txt ํ™•์ธ (์‚ฌ์ดํŠธ ํฌ๋กค๋ง ํ—ˆ์šฉ ์—ฌ๋ถ€ ์ฒดํฌ)

์›น์‚ฌ์ดํŠธ๋งˆ๋‹ค ํฌ๋กค๋ง ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ์„ค์ •ํ•œ robots.txt ํŒŒ์ผ์ด ์žˆ๋‹ค.
โžก๏ธ ํฌ๋กค๋ง ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ๋จผ์ € ์ฒดํฌ!!

๐Ÿ”น ํ™•์ธ ๋ฐฉ๋ฒ•:

https://์‚ฌ์ดํŠธ์ฃผ์†Œ/robots.txt

 

๐Ÿ“– ์˜ˆ์‹œ (๋„ค์ด๋ฒ„ ๋‰ด์Šค)

https://news.naver.com/robots.txt

 

โœ” User-agent: * ๋ถ€๋ถ„์—์„œ Disallow ๋œ URL์ด ์žˆ์œผ๋ฉด ํฌ๋กค๋งํ•˜๋ฉด ์•ˆ ๋จ!
โœ” ๋งŒ์•ฝ Allow: / ๋ผ๊ณ  ๋˜์–ด ์žˆ์œผ๋ฉด ํฌ๋กค๋ง ๊ฐ€๋Šฅ!

 

โœ…3-2. User-Agent ์„ค์ • (ํฌ๋กค๋ง ์ฐจ๋‹จ ๋ฐฉ์ง€)

์„œ๋ฒ„๊ฐ€ ๋ด‡(์ž๋™ ํฌ๋กค๋Ÿฌ)์ธ ๊ฑธ ๊ฐ์ง€ํ•˜๋ฉด ์ฐจ๋‹จํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
โžก๏ธ User-Agent๋ฅผ ์„ค์ •ํ•˜๋ฉด ์‚ฌ๋žŒ์ด ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ(๋ธŒ๋ผ์šฐ์ € ์ฒ˜๋Ÿผ) ๋ณด์ผ ์ˆ˜ ์žˆ์Œ!

๐Ÿ“– ์˜ˆ์‹œ (๋„ค์ด๋ฒ„ ๋‰ด์Šค ํฌ๋กค๋ง)

import requests

url = "https://news.naver.com/"
headers = {"User-Agent": "Mozilla/5.0"}  # ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ €์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ์„ค์ •
response = requests.get(url, headers=headers)

print(response.status_code)  # 200์ด๋ฉด ์ •์ƒ ์ ‘์†

 

โœ…3-3. IP ์ฐจ๋‹จ ๋ฐฉ์ง€ (์š”์ฒญ ๊ฐ„๊ฒฉ ์กฐ์ ˆ)

๋„ˆ๋ฌด ๋น ๋ฅด๊ฒŒ ์š”์ฒญํ•˜๋ฉด ์ฐจ๋‹จ๋  ์ˆ˜๋„ ์žˆ์–ด!
โžก๏ธ time.sleep(1) ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์š”์ฒญ ๊ฐ„๊ฒฉ์„ ์กฐ์ ˆ.

๐Ÿ“– ์˜ˆ์ œ (10์ดˆ๋งˆ๋‹ค ๋‰ด์Šค ํฌ๋กค๋ง)

import time

for _ in range(5):
    response = requests.get(url, headers=headers)
    print("ํฌ๋กค๋ง ์™„๋ฃŒ, 10์ดˆ ๋Œ€๊ธฐ ์ค‘...")
    time.sleep(10)  # 10์ดˆ ๋Œ€๊ธฐ

 


๐Ÿ“Œ 4. ์ถ”๊ฐ€ ๊ธฐ๋Šฅ

โœ…4-1. API ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (requests + JSON ํ™œ์šฉ)

์›น์‚ฌ์ดํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” API๋ฅผ ์ด์šฉํ•˜๋ฉด ๋” ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
(๐Ÿ’ก API๋Š” ๋ฐ์ดํ„ฐ๋ฅผ JSON ํ˜•์‹์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ!)

 

๐Ÿ“– API ์š”์ฒญ ์˜ˆ์ œ (jsonplaceholder.typicode.com ์‚ฌ์šฉ)

import requests

url = "https://jsonplaceholder.typicode.com/posts"
response = requests.get(url)

data = response.json()  # JSON ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ด์ฌ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ณ€ํ™˜

# 5๊ฐœ์˜ ์ œ๋ชฉ ์ถœ๋ ฅ
for post in data[:5]:
    print(post["title"])

โœ” ์žฅ์ : ํฌ๋กค๋ง๋ณด๋‹ค ๋น ๋ฅด๊ณ  ์ •ํ™•ํ•จ! (robots.txt ๊ฑฑ์ • ์—†์Œ)
โœ” ๋‹จ์ : API๋ฅผ ์ œ๊ณตํ•˜๋Š” ์‚ฌ์ดํŠธ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

 

โœ…4-2. ํฌ๋กค๋ง ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต ํ›„ ์‹œ๊ฐํ™” (pandas + matplotlib)

ํฌ๋กค๋งํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ”น ์„ค์น˜ ๋ฐฉ๋ฒ•

pip install pandas matplotlib

 

๐Ÿ“– CSV ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์™€์„œ ์‹œ๊ฐํ™” ์˜ˆ์ œ

import pandas as pd
import matplotlib.pyplot as plt

# CSV ํŒŒ์ผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
df = pd.read_csv("news.csv")

# ๋‰ด์Šค ๊ฐœ์ˆ˜ ํ™•์ธ
print(df.head())  # ์ƒ์œ„ 5๊ฐœ ์ถœ๋ ฅ

# ๋‰ด์Šค ์ œ๋ชฉ ๊ธ€์ž ์ˆ˜ ๋ถ„ํฌ ์‹œ๊ฐํ™”
df["title_length"] = df["๋‰ด์Šค ์ œ๋ชฉ"].str.len()

plt.hist(df["title_length"], bins=10, color="skyblue", edgecolor="black")
plt.xlabel("์ œ๋ชฉ ๊ธธ์ด")
plt.ylabel("๊ธฐ์‚ฌ ๊ฐœ์ˆ˜")
plt.title("๋‰ด์Šค ์ œ๋ชฉ ๊ธธ์ด ๋ถ„ํฌ")
plt.show()

โœ” pandas๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Œ
โœ” matplotlib๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ํ‘œ์‹œ ๊ฐ€๋Šฅ


'python' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[python] list์™€ ๋‚ด์žฅํ•จ์ˆ˜  (0) 2023.05.29