๐ 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 |
|---|