๐ ํ์ด์ฌ ์ด๋ณด์ ๊ฐ์ด๋: ์น์์ ๋ฐ์ดํฐ ์ฝ๊ฒ ๋ด๋ ค๋ฐ๊ธฐ (์คํฌ๋ ์ดํ ๊ธฐ์ด)
์๋ ํ์ธ์! ๋ฐ์ดํฐ ๋ถ์์ ๊ด์ฌ ์๋ ํ์ด์ฌ ์ด๋ณด์ ์ฌ๋ฌ๋ถ! ๐
๋ฐ์ดํฐ ๋ถ์์ ์ฒซ๊ฑธ์์ ๋น์ฐํ ๋ฐ์ดํฐ๋ฅผ ํ๋ณดํ๋ ๊ฒ์ด๊ฒ ์ฃ ? ํ์ง๋ง ๋งค๋ฒ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ์ ๋ ฅํ๊ฑฐ๋ ํ์ผ์ ๋ค์ด๋ก๋ํ๋ ๊ฑด ๋๋ฌด ๋นํจ์จ์ ์ด์์. ๐ซ
๋คํํ๋ ํ์ด์ฌ์๋ ์นํ์ด์ง์ ์๋ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ์ถ์ถํด์ฃผ๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ค์ด ์์ต๋๋ค. ์ด๋ฅผ ๋ณดํต **์น ์คํฌ๋ ์ดํ(Web Scraping)**์ด๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ์, ๋ณต์กํ๊ฒ ๋ค๋ฆด ์ ์์ง๋ง ๊ฑฑ์ ๋ง์ธ์! ๋ช ๊ฐ์ง ๊ฐ๋จํ ์์ ๋ฅผ ํตํด ๋๊ตฌ๋ ์ฝ๊ฒ ์น์์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค๋ฐ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ฆด๊ฒ์! ์ด ๋ธ๋ก๊ทธ๋ฅผ ๋๊น์ง ์ฝ์ผ๋ฉด ์ฌ๋ฌ๋ถ๋ ์น ์คํฌ๋ ์ดํ์ ๊ธฐ์ด๋ฅผ ๋ง์คํฐํ ์ ์์ ๊ฑฐ์์! ๐ช
๐ Step 1: ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์นํ๊ธฐ
์น ์คํฌ๋ ์ดํ์ ์ํด ์ฐ๋ฆฌ๋ ๋ ๊ฐ์ง ํต์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊ฑฐ์์:
requests: ์นํ์ด์ง์ HTML ์ฝ๋๋ฅผ ๊ฐ์ ธ์ค๋ ์ญํ ์ ํฉ๋๋ค.BeautifulSoup(bs4): ๊ฐ์ ธ์จ HTML ์ฝ๋์์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐ์ดํฐ(ํ ์คํธ, ๋งํฌ ๋ฑ)๋ฅผ ์์๊ฒ ์ถ์ถํ๋ ์ญํ ์ ํฉ๋๋ค. ์ด๋ฆ์ฒ๋ผ HTML์ ๋ถ์ํ๊ธฐ ์ข๊ฒ ๋ง๋ค์ด์ค์! ๐คฉ
ํฐ๋ฏธ๋์ด๋ ๋ช ๋ น ํ๋กฌํํธ์์ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ฌ ์ค์นํด ์ฃผ์ธ์.
pip install requests beautifulsoup4
๐ Step 2: ์นํ์ด์ง HTML ์ฝ๋ ๊ฐ์ ธ์ค๊ธฐ (requests)
์ฐ๋ฆฌ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค๋ฐ์ ์นํ์ด์ง์ ์ฃผ์(URL)๊ฐ ํ์ํด์. ์ฌ๊ธฐ์๋ ์๋ฅผ ๋ค์ด, ๊ฐ์์ ๋จ์ํ ์นํ์ด์ง์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค. ์ค์ ๋ก๋ ๋ด์ค ์ฌ์ดํธ, ๊ณต๊ณต ๋ฐ์ดํฐ ํฌํธ ๋ฑ ๋ค์ํ ๊ณณ์์ ํ์ฉํ ์ ์์ด์.
๋จผ์ requests ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ์นํ์ด์ง์ ์ ์ํ๊ณ ๊ทธ ๋ด์ฉ์ ๊ฐ์ ธ์๋ณผ๊ฒ์.
import requests
# ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์นํ์ด์ง URL
url = "https://example.com"
# requests.get() ํจ์๋ฅผ ์ฌ์ฉํด ์นํ์ด์ง์ ์์ฒญ(Request)์ ๋ณด๋
๋๋ค.
response = requests.get(url)
# ์์ฒญ์ด ์ฑ๊ณตํ๋์ง ํ์ธํฉ๋๋ค. (200์ด ๋จ๋ฉด ์ฑ๊ณต!)
print(f"์๋ต ์ํ ์ฝ๋: {response.status_code}")
# ์นํ์ด์ง์ HTML ์ฝ๋๋ฅผ ํ
์คํธ ํํ๋ก ์ถ๋ ฅํฉ๋๋ค.
html_content = response.text
# print(html_content[:500]) # ๋ด์ฉ์ด ๋๋ฌด ๊ธธ์ด ์๋ถ๋ถ 500์๋ง ์ถ๋ ฅํด๋ณผ๊ฒ์.
๐ค ์ ๊น! HTML์ด๋?
์นํ์ด์ง๋ **HTML(HyperText Markup Language)**์ด๋ผ๋ ์ธ์ด๋ก ๋ง๋ค์ด์ ธ์. HTML์ ๋ง์น ๊ฑด๋ฌผ์ ์ค๊ณ๋๋ ๋ผ๋์ ๊ฐ์์. ์ด ๋ผ๋ ์์๋ ํ ์คํธ, ์ด๋ฏธ์ง, ๋งํฌ ๋ฑ์ด **ํ๊ทธ(<...>)**๋ผ๋ ์ฝ์๋ ํ์์ผ๋ก ์ ๋ฆฌ๋์ด ์์ต๋๋ค. ์ฐ๋ฆฌ์ ๋ชฉํ๋ ์ด ๋ณต์กํ HTML ์ค๊ณ๋์์ ํ์ํ ์ ๋ณด๋ง ์์ ๋นผ๋ด๋ ๊ฒ์ ๋๋ค!
๐ Step 3: ์ํ๋ ๋ฐ์ดํฐ ์ถ์ถํ๊ธฐ (BeautifulSoup)
HTML ์ฝ๋๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์๋ค๋ฉด, ์ด์ BeautifulSoup์ ๋ง๋ฒ์ ๋ถ๋ฆด ์ฐจ๋ก์
๋๋ค! ✨
BeautifulSoup์ HTML ์ฝ๋๋ฅผ ๋ถ์ํ๊ธฐ ์ฌ์ด ๊ฐ์ฒด(Soup)๋ก ๋ง๋ค์ด์ค๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์ ๋ณด๋ฅผ HTML ํ๊ทธ์ **์์ฑ(Attribute, ์: class)**์ ์ด์ฉํด์ ์ฐพ์ ์ ์๊ฒ ํด์ค์.
๊ฐ์์ ์นํ์ด์ง https://example.com์๋ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ด ์๋ค๊ณ ๊ฐ์ ํ๊ณ , ์ฌ๊ธฐ์ <h1> ํ๊ทธ์ ์ ๋ชฉ๊ณผ <p> ํ๊ทธ์ ๋ณธ๋ฌธ ๋ด์ฉ์ ์ถ์ถํด ๋ณด๊ฒ ์ต๋๋ค.
from bs4 import BeautifulSoup
# Step 2์์ ๊ฐ์ ธ์จ HTML ๋ด์ฉ์ BeautifulSoup ๊ฐ์ฒด๋ก ๋ณํ
soup = BeautifulSoup(html_content, 'html.parser')
# 1. ์ ๋ชฉ(<h1> ํ๊ทธ) ์ถ์ถํ๊ธฐ
# find() ํจ์๋ ํด๋น ํ๊ทธ ์ค ๊ฐ์ฅ ๋จผ์ ๋์ค๋ ํ๋๋ฅผ ์ฐพ์์ค๋๋ค.
title_tag = soup.find('h1')
if title_tag:
# .text ์์ฑ์ ์ฌ์ฉํ๋ฉด ํ๊ทธ๋ฅผ ์ ์ธํ ์์ํ ํ
์คํธ๋ง ์ป์ ์ ์์ด์.
title_text = title_tag.text
print(f"\n๐ ์ถ์ถ๋ ์ ๋ชฉ(<h1>): {title_text}")
else:
print("<h1> ํ๊ทธ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.")
# 2. ๋ณธ๋ฌธ(์ฒซ ๋ฒ์งธ <p> ํ๊ทธ) ์ถ์ถํ๊ธฐ
paragraph_tag = soup.find('p')
if paragraph_tag:
paragraph_text = paragraph_tag.text
print(f"๐ ์ถ์ถ๋ ๋ณธ๋ฌธ(<p>): {paragraph_text}")
else:
print("<p> ํ๊ทธ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.")
# 3. ๋ชจ๋ ๋งํฌ(<a> ํ๊ทธ) ์ถ์ถํ๊ธฐ
# find_all() ํจ์๋ ํด๋น ํ๊ทธ๋ฅผ ๊ฐ์ง ๋ชจ๋ ์์๋ฅผ ๋ฆฌ์คํธ ํํ๋ก ์ฐพ์์ค๋๋ค.
link_tags = soup.find_all('a')
print(f"\n๐ ๋ฐ๊ฒฌ๋ ๋งํฌ ๊ฐ์: {len(link_tags)}")
if link_tags:
# ๋ฆฌ์คํธ๋ฅผ ๋ฐ๋ณต๋ฌธ์ผ๋ก ๋๋ฉด์ ๋งํฌ์ ํ
์คํธ์ ์ฃผ์(href ์์ฑ)๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
for link in link_tags:
link_text = link.text # ๋งํฌ์ ํ์๋๋ ํ
์คํธ
link_url = link.get('href') # ๋งํฌ์ ์ค์ ์ฃผ์ (href ์์ฑ ๊ฐ)
print(f" - [{link_text}]: {link_url}")
๐ก Step 4: ๋ฐ์ดํฐ๋ฅผ ์ ๋ฆฌํ๊ณ ์ ์ฅํ๊ธฐ (CSV ํ์ผ๋ก)
์น ์คํฌ๋ ์ดํ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ค๋ฉด, ๋ถ์ํ๊ธฐ ์ฝ๊ฒ ์ ๋ฆฌํ๊ณ ์ ์ฅํด์ผ๊ฒ ์ฃ ? ๊ฐ์ฅ ํํ๊ฒ ์ฌ์ฉํ๋ CSV(Comma-Separated Values) ํ์ผ๋ก ์ ์ฅํด ๋ณด๊ฒ ์ต๋๋ค. CSV๋ ๋ฐ์ดํฐ๋ฅผ ์ผํ(,)๋ก ๊ตฌ๋ถํ ๋จ์ํ ํ ์คํธ ํ์ผ ํ์์ด๋ฉฐ, ์์ ์ด๋ ๋ค์ํ ๋ถ์ ํ๋ก๊ทธ๋จ์์ ์ฝ๊ฒ ์ด์ด๋ณผ ์ ์์ต๋๋ค. ๐
์ด ์์ ์์๋ ์ถ์ถํ ์ ๋ชฉ๊ณผ ๋ณธ๋ฌธ์ ๋ฐ์ดํฐํ๋ ์์ผ๋ก ๋ง๋ค๊ณ ์ ์ฅํด ๋ณผ๊ฒ์. ์ด ์์
์ ์ํด pandas ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
pip install pandas
import pandas as pd
# ์ถ์ถํ ๋ฐ์ดํฐ๋ฅผ ๋์
๋๋ฆฌ ํํ๋ก ์ค๋น
data = {
'Category': ['Title', 'Paragraph'],
'Content': [title_text, paragraph_text]
}
# ๋์
๋๋ฆฌ๋ฅผ Pandas DataFrame์ผ๋ก ๋ณํ
df = pd.DataFrame(data)
# DataFrame์ CSV ํ์ผ๋ก ์ ์ฅํฉ๋๋ค.
# encoding='utf-8-sig'๋ ํ๊ธ ๊นจ์ง ๋ฐฉ์ง์ ์ ์ฉํฉ๋๋ค.
# index=False๋ ํ ๋ฒํธ๋ฅผ ํ์ผ์ ์ ์ฅํ์ง ์๋๋ก ํฉ๋๋ค.
df.to_csv('web_scraped_data.csv', index=False, encoding='utf-8-sig')
print("\n✅ ๋ฐ์ดํฐ ์ถ์ถ ๋ฐ CSV ํ์ผ ์ ์ฅ ์๋ฃ!")
print(" => 'web_scraped_data.csv' ํ์ผ์ ํ์ธํด๋ณด์ธ์.")
print("\n")
⚠️ ์ฃผ์์ฌํญ: ์คํฌ๋ ์ดํ์ ๋งค๋์๊ฒ!
์น ์คํฌ๋ ์ดํ์ ๊ฐ๋ ฅํ ๋๊ตฌ์ด์ง๋ง, ์ฌ์ฉํ ๋ ๋ช ๊ฐ์ง ๋งค๋๋ฅผ ์ง์ผ์ผ ํด์. ๐
์๋ฒ์ ๊ณผ๋ถํ๋ฅผ ์ฃผ์ง ๋ง์ธ์: ๋๋ฌด ์งง์ ์๊ฐ์ ๋ง์ ์์ฒญ์ ๋ณด๋ด๋ฉด ์น์ฌ์ดํธ ์๋ฒ์ ๋ถ๋ด์ ์ค ์ ์์ต๋๋ค. ์ฐ์์ ์ธ ์์ฒญ ์ฌ์ด์
time.sleep(1)๋ฑ์ ์ฌ์ฉํด ์ ์ ์ฌ์ด์ฃผ๋ ๊ฒ์ด ์ข์ต๋๋ค.robots.txtํ์ธ: ๋๋ถ๋ถ์ ์น์ฌ์ดํธ๋https://์ฌ์ดํธ์ฃผ์/robots.txtํ์ผ์ ์คํฌ๋ ์ดํ์ ํ์ฉํ๋ ์์ญ๊ณผ ๊ธ์งํ๋ ์์ญ์ ๋ช ์ํด ๋ก๋๋ค. ์คํฌ๋ ์ดํ ์ ์ ๊ผญ ํ์ธํ์ธ์!์์ ์ ์ด์ฉ ์ฃผ์: ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ ๋ชฉ์ ์ด ์์ ์ ์ธ ๊ฒฝ์ฐ, ๋ฐ๋์ ํด๋น ์น์ฌ์ดํธ์ ์ ์๊ถ ๋ฐ ์ด์ฉ ์ฝ๊ด์ ํ์ธํด์ผ ํฉ๋๋ค. ๋ฌด๋จ ๋์ฉ์ ๋ฒ์ ์ธ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค.
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์ค๋ ์ฐ๋ฆฌ๋ ํ์ด์ฌ์ requests์ BeautifulSoup์ ์ฌ์ฉํด์ ์นํ์ด์ง์์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค๋ฐ๊ณ CSV ํ์ผ๋ก ์ ์ฅํ๋ ๊ธฐ์ด์ ์ธ ๊ณผ์ ์ ๋ฐฐ์๋ดค์ต๋๋ค.
requests๋ก HTML ๊ฐ์ ธ์ค๊ธฐBeautifulSoup์ผ๋ก ๋ฐ์ดํฐ ์ถ์ถํ๊ธฐpandas๋ก CSV ํ์ผ๋ก ์ ์ฅํ๊ธฐ
์ด๊ฒ์ด ๋ฐ๋ก ์น ์คํฌ๋ ์ดํ์ ๊ธฐ๋ณธ ์๋ฆฌ์ ๋๋ค. ์ด์ ์ฌ๋ฌ๋ถ์ ์ด ๊ธฐ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ ๋ณต์กํ ์น์ฌ์ดํธ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๊ฑฐ๋, ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ ์๋ํ ํ๋ก๊ทธ๋จ์ ๋ง๋ค ์ ์๊ฒ ๋์์ด์! ๐ฅณ
๋ฐ์ดํฐ ๋ถ์, ์ด์ ํ์ด์ฌ์ผ๋ก ๋ ์ฝ๊ณ ์ฌ๋ฏธ์๊ฒ ์์ํด ๋ณด์ธ์!
๋ค์ ์๊ฐ์๋ ๋ ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ(ํ ์ด๋ธ, ๋์ ์ผ๋ก ๋ก๋ฉ๋๋ ๋ฐ์ดํฐ)๋ฅผ ๋ค๋ฃจ๋ ์ฌํ ๋ด์ฉ์ ์ค๋นํด๋ณผ๊ฒ์! ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ๋๊ธ๋ก ์ง๋ฌธํด ์ฃผ์ธ์! ๐

๋๊ธ
๋๊ธ ์ฐ๊ธฐ