๐ธ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ์์์ผ ํ ์น ํฌ๋กค๋ง์ ๋ชจ๋ ๊ฒ
์๋ ํ์ธ์! ์ค๋์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ผ๋ฉด ํ ๋ฒ์ฏค ๊ถ๊ธํดํ์ ์น ํฌ๋กค๋ง(Web Crawling)์ ๋ํด ๊น์ด ์๊ณ ์ค์ฉ์ ์ธ ์ด์ผ๊ธฐ๋ฅผ ํ์ด๋ณด๋ ค๊ณ ํฉ๋๋ค.
'์ด...? ์น ํฌ๋กค๋ง์ ๋ฐฑ์๋๋ ๋ฐ์ดํฐ ์ฌ์ด์ธํฐ์คํธ๋ค์ด๋ ํ๋ ๊ฑฐ ์๋์ผ?'๋ผ๊ณ ์๊ฐํ์ จ๋ค๋ฉด, ์ค๋ ๊ธ์ด ๊ฝค ์๋ก์ด ์์ผ๋ฅผ ์ด์ด๋๋ฆด ์ ์์์ง๋ ๋ชฐ๋ผ์. ์ ์ญ์ ๊ทธ๋ ๊ฒ ์๊ฐํ๋ค๊ฐ, ํ๋ก์ ํธ์์ ์์ธ๋ก ํ์ํ ์๊ฐ์ด ์ฐพ์์๊ณ , ๊ทธ๋ ์ ๋ง ๋ง์ ๊ฑธ ๋ฐฐ์ ๊ฑฐ๋ ์.
๐ ์น ํฌ๋กค๋ง์ด ๋ญ๊ธธ๋?
๋จผ์ ์ฉ์ด ์ ๋ฆฌ๋ถํฐ ํ๊ณ ๊ฐ์ฃ .
- ์น ํฌ๋กค๋ง(Web Crawling): ์น ํ์ด์ง๋ฅผ ์๋์ผ๋ก ๋ฐฉ๋ฌธํ๊ณ , ๊ทธ ์์ ์ ๋ณด๋ฅผ ์ถ์ถํ๋ ์์ ์ ๋๋ค.
- ์น ์คํฌ๋ํ(Web Scraping): ํฌ๋กค๋ง์ ํตํด ๊ฐ์ ธ์จ HTML์์ ์ค์ ๋ก ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ํ์ฑํ๋ ์์ ์ ๋๋ค.
์ฆ, ํฌ๋กค๋ง์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ํ์์ด๊ณ , ์คํฌ๋ํ์ ๊ทธ ๋ฐ์ดํฐ์์ ์ ๋ณด๋ฅผ ์ถ์ถํ๋ ๊ณผ์ ์ด์์. ๋์ ์ข ์ข ํผ์ฉ๋์ง๋ง ์ญํ ์ด ์กฐ๊ธ ๋ค๋ฅด์ฃ .
๐ก ์ ํ๋ก ํธ์๋๊ฐ ์น ํฌ๋กค๋ง์ ์์์ผ ํ ๊น?
์ด ์ง๋ฌธ์ด ์ ์ผ ์ค์ํ์ฃ .
- ๐ SEO ํ ์คํธ์ฉ ๋ฉํ๋ฐ์ดํฐ ํ์ธ: SSR๋ HTML์ ์ ๋ฐํ๋์ง ํ์ธํ๊ณ ์ถ์ ๋.
- ๐ ์ธ๋ถ ๋ฐ์ดํฐ ๊ธฐ๋ฐ UI ํ ์คํธ: ์คํ API๊ฐ ์๊ฑฐ๋ ์ ํ์ ์ผ ๋, ๋น๊ณต์์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ํ๋ณดํด์ผ ํ ๋.
- ๐งช ๋์์ธ QA๋ UI ๋ณํ ์ถ์ : ํน์ ํ์ด์ง ๊ตฌ์กฐ๊ฐ ๋ฐ๋์๋์ง ๋ชจ๋ํฐ๋งํ ๋.
- ๐งต ํผ๋ธ๋ฆฌ์ฑ ์๋ํ: ์ปจํ ์ธ ๋ฅผ ๊ฐ์ ธ์ UI์ ์๋ ๋ฐ์ํ๊ฑฐ๋ ๋น๊ตํ ๋.
๊ฐ๋ฐ์๋ก์ ๋๊ตฌ๋ ๋ง์์๋ก ์ข์ต๋๋ค. ํฌ๋กค๋ง์ ์๊ฐ๋ณด๋ค ์ ์ฉํ ๋๊ตฌ์์.
๐ง ์น ํฌ๋กค๋ง ๋๊ตฌ ์ด์ ๋ฆฌ (ํ๋ก ํธ์๋ ๊ด์ )
1. axios
+ cheerio
์กฐํฉ (Node ๊ธฐ๋ฐ, ๊ฐ๋ณ๊ณ ๋น ๋ฆ)
npm install axios cheerio
import axios from "axios";
import * as cheerio from "cheerio";
const crawl = async () => {
const { data } = await axios.get("https://example.com");
const $ = cheerio.load(data);
const title = $("title").text();
const headlines = $("h1").map((_, el) => $(el).text()).get();
console.log("Title:", title);
console.log("Headlines:", headlines);
};
crawl();
๐ก cheerio๋ jQuery ๋ฌธ๋ฒ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ด์, ํ๋ก ํธ ๊ฐ๋ฐ์์๊ฒ ํนํ ์ต์ํฉ๋๋ค.
2. puppeteer (ํค๋๋ฆฌ์ค ๋ธ๋ผ์ฐ์ , ๋ ๋๋ง ๊ธฐ๋ฐ ๋ฐ์ดํฐ๋ ๊ฐ๋ฅ)
SPA์ฒ๋ผ JavaScript๋ก ๊ทธ๋ ค์ง๋ ์น์ฌ์ดํธ๋ ์ ์ฒ๋ฆฌํ๋ ค๋ฉด ์๊ฐ ํ์ํด์.
npm install puppeteer
import puppeteer from "puppeteer";
const crawl = async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com", { waitUntil: "networkidle2" });
const data = await page.evaluate(() => {
return {
title: document.title,
h1: Array.from(document.querySelectorAll("h1")).map(el => el.innerText),
};
});
console.log(data);
await browser.close();
};
crawl();
๐ง ์ฐธ๊ณ : puppeteer-core๋ Chrome ์์ด ์ฌ์ฉ ๊ฐ๋ฅํ๊ณ , playwright๋ ๋ฉํฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ง์ํฉ๋๋ค.
3. playwright (puppeteer๋ณด๋ค ๊ณ ๋ํ๋จ)
โข ๋ฉํฐ ๋ธ๋ผ์ฐ์ (Chromium, Firefox, WebKit)
โข ๋ชจ๋ฐ์ผ ์๋ฎฌ๋ ์ด์ ์ง์
โข ํ ์คํธ ์๋ํ์์ ๊ถํฉ๋ ๊ตฟ
npm install playwright
import { chromium } from "playwright";
const crawl = async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
const content = await page.innerText("h1");
console.log(content);
await browser.close();
};
crawl();
๐งฑ ๋ฐ์ดํฐ ํ์ฑ ํ
ํฌ๋กค๋ง์ ์ ๋ฐ์ HTML์ ์ด๋ป๊ฒ ํด์ํ๋๋์ ๋ฌ๋ ค ์์ด์.
โข cheerio๋ jQuery ์คํ์ผ์ด๋ผ ์ง๊ด์ ์ด์ง๋ง, JS ๋ ๋๋ง๋ ๋ด์ฉ์ ์ ๋ณด์ผ ์ ์์ด์.
โข puppeteer๋ playwright๋ ๋ ๋๋ง๋ DOM๊น์ง ์ ๊ทผ ๊ฐ๋ฅํ๋ ๋ณต์กํ ํ์ด์ง์ ์ ๋ฆฌํฉ๋๋ค.
โข setTimeout, waitForSelector, networkidle2 ๊ฐ์ ํ์ด๋ฐ ์ ์ด๊ฐ ํต์ฌ์ ๋๋ค.
โ๏ธ ์น ํฌ๋กค๋ง์ ์ค๋ฆฌ์ ๋ฒ์ ๊ณ ๋ ค
ํฌ๋กค๋ง์ ์ค๋ฆฌ์ ์ด๊ณ ํฉ๋ฒ์ ์ธ ์ ์ ์ง์ผ์ผ ํฉ๋๋ค.
๋ค์์ ๊ผญ ๊ณ ๋ คํด ์ฃผ์ธ์.
โข robots.txt ํ์ผ ํ์ธํ๊ธฐ (ํฌ๋กค๋ง ํ์ฉ ์ฌ๋ถ ๋ช ์๋จ)
โข API๊ฐ ์์ผ๋ฉด ๊ทธ๊ฑธ ๋จผ์ ์ฐ๊ธฐ
โข ํธ๋ํฝ ํญํ ์ฃผ์ (interval ๋๊ณ ์์ฒญํ๊ธฐ)
โข ๋ก๊ทธ์ธ ๊ธฐ๋ฐ ์ ๋ณด, ๊ฐ์ธ์ ๋ณด๋ ์ ๋ ์์งํ์ง ์๊ธฐ
โข ์์ ์ ๋ชฉ์ ์ ํนํ ์ฃผ์
๐งช ์ค์ ์์ : ํน์ ๋ธ๋ก๊ทธ ๊ธ ์ ๋ชฉ ๊ธ์ด์ค๊ธฐ
import axios from "axios";
import * as cheerio from "cheerio";
const crawlBlogTitles = async () => {
const { data } = await axios.get("https://velog.io/@username");
const $ = cheerio.load(data);
const titles = $(".sc-bxivhb").map((_, el) => $(el).text()).get();
console.log(titles);
};
๐งต ํด๋ผ์ด์ธํธ์์ ์ง์ ํฌ๋กค๋ง์ ๋ณด์, CORS, ์ฑ๋ฅ ๋ฌธ์ ๋๋ฌธ์ ๋ฐฑ์๋๋ ์๋ฒ๋ฆฌ์ค ํจ์๋ก ์ฐํํ๋ ๊ฒ ์ข์ต๋๋ค.
๐ฏ ํ๋ก ํธ์๋์ ๋ง์ถ ํ์ฉ ์์ด๋์ด
โข UI ํ ์คํธ์ฉ ๋ชฉ์ ๋ฐ์ดํฐ ํ๋ณด (API ์์ โ HTML ํฌ๋กค๋ง โ JSON ๊ฐ๊ณต)
โข ๋ด์ค ํค์๋ ํธ๋ ๋ ๋ถ์
โข ํฌ๋กค๋ง โ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์ฅ โ ํ๋ก ํธ์์ ์ฐจํธ๋ก ์๊ฐํ
โข ์ ์ ์ฌ์ดํธ SEO ํ ์คํธ ์๋ํ
โข ๋ธ๋ก๊ทธ ์๋ ์์ฝ๊ธฐ ๋ง๋ค๊ธฐ
๐ ์ถ์ฒ ์๋ฃ & ๋ ํผ๋ฐ์ค
โข playwright docs
โข cheerio docs
๋ง๋ฌด๋ฆฌํ๋ฉฐโฆ
์ฌ์ค ํ๋ก ํธ์๋๋ผ๊ณ ํด์ ์น์ โ๋ณด๋ ์ชฝโ์๋ง ๊ตญํ๋๋ฉด, ์ ์ ์๋์ ์ธ ๊ฐ๋ฐ์๊ฐ ๋๊ธฐ ์ฌ์ด ๊ฒ ๊ฐ์์.
์น์ด๋ผ๋ ๊ณต๊ฐ์ ์กด์ฌํ๋ ๋ฐ์ดํฐ๋ฅผ ์ค์ค๋ก ์์งํ๊ณ ํ์ฉํ ์ ์๋ ํ์ ๊ฐ์ถ๋ ๊ฑด, ์ฐ๋ฆฌ๊ฐ ๋ง๋๋ UI๋ฅผ ๋ ์ฃผ์ฒด์ ์ผ๋ก ๋ง๋ค์ด ์ค๋๋ค.
ํฌ๋กค๋ง์ ์ด๋ ต์ง ์์ง๋ง, ์๊ณ ๋๋ฉด ํ์คํ ๊ฐ๋ฐ์์ ๋ฌด๊ธฐ๊ฐ ํ๋ ๋์ด๋ ๋๋์ด์์.
ํน์ ๋น์ทํ ์ฃผ์ ๋ก ๊ด์ฌ ์์ผ์ ๋ถ์ด ์๋ค๋ฉด, ๋ฐ์ดํฐ ์๊ฐํ๋ ์๋ํ๋ ํจ๊ป ๊ณต๋ถํด๋ณด์๊ธธ ์ถ์ฒ๋๋ฆฝ๋๋ค!