크롤링과 HTML 추출 — 웹 데이터 수집 자동화
API를 제공하지 않는 웹사이트에서도 데이터를 자동으로 수집할 수 있다. HTTP Request로 페이지를 가져오고, HTML Extract로 원하는 정보만 뽑아내면 된다.
크롤링 기본 구조
[Schedule/Manual] → [HTTP Request: 웹 페이지 가져오기] → [HTML Extract: 데이터 추출]
→ [Edit Fields: 정제]
→ [저장/알림]
Step 1: HTTP Request로 페이지 가져오기
Method: GET
URL: https://news.ycombinator.com/
Options → Response Format: Text (HTML 원본)
💡 주의: Response Format을 Text로 설정해야 HTML 원본이 그대로 넘어온다. JSON(기본값)으로 설정하면 파싱 에러가 발생한다.
User-Agent 설정
일부 사이트는 봇을 차단한다. User-Agent 헤더를 추가하자:
Headers:
User-Agent: Mozilla/5.0 (compatible; n8n-bot/1.0)
Step 2: HTML Extract 노드
HTML에서 CSS 셀렉터를 사용해 원하는 데이터를 추출한다.
기본 설정
| 설정 | 설명 |
|---|---|
| Source Data | Binary (HTML 파일) 또는 JSON (텍스트 필드) |
| Extraction Values | CSS 셀렉터 + 반환할 속성 목록 |
CSS 셀렉터 기초
| 셀렉터 | 의미 | 예시 |
|---|---|---|
h1 | 태그 이름 | 모든 h1 태그 |
.class-name | 클래스 | .article-title |
#id-name | ID | #main-content |
div > p | 직접 자식 | div 바로 아래 p |
div p | 모든 자손 | div 안의 모든 p |
a[href] | 속성 있는 태그 | href 속성이 있는 a |
:nth-child(2) | N번째 요소 | 두 번째 자식 요소 |
반환 속성
| Return Value | 설명 |
|---|---|
| HTML | 태그의 innerHTML |
| Text | 태그의 텍스트만 (태그 제거) |
| Attribute: href | 링크 URL |
| Attribute: src | 이미지 URL |
| Attribute: class | 클래스명 |
실전 예제: Hacker News 크롤링
Hacker News 첫 페이지의 제목과 링크를 수집하자.
HTML Extract 설정
Extraction Values:
제목:
CSS Selector: .titleline > a
Return Value: Text
링크:
CSS Selector: .titleline > a
Return Value: Attribute → href
포인트:
CSS Selector: .score
Return Value: Text
결과
[
{ "제목": "Show HN: Something Cool", "링크": "https://...", "포인트": "142 points" },
{ "제목": "Why AI Needs More...", "링크": "https://...", "포인트": "89 points" }
]
여러 페이지 크롤링 (Pagination)
한 페이지만으로 부족할 때, 여러 페이지를 순회한다.
패턴: URL 패턴 기반
[Code: URL 목록 생성]
→ [Split Out: 각 URL]
→ [HTTP Request: 페이지 가져오기]
→ [HTML Extract: 데이터 추출]
→ [Aggregate: 결과 합치기]
Code 노드:
const pages = [];
for (let i = 1; i <= 5; i++) {
pages.push({ json: { url: `https://example.com/list?page=${i}` } });
}
return pages;
패턴: "다음 페이지" 링크 추적
[HTTP Request] → [HTML Extract: 데이터 + 다음 URL]
→ [IF: 다음 페이지 있는가?]
true → [Edit Fields: 다음 URL로] → [HTTP Request] (루프)
false → [완료]
SerpAPI 노드 — 검색 결과 수집
직접 크롤링 대신 SerpAPI를 사용하면 Google, Bing, YouTube 등의 검색 결과를 구조화된 JSON으로 받을 수 있다.
API: Google Search
Query: "n8n workflow automation"
Location: South Korea
Language: ko
결과:
{
"organic_results": [
{ "title": "...", "link": "...", "snippet": "..." },
{ "title": "...", "link": "...", "snippet": "..." }
]
}
실전: 경쟁사 가격 모니터링
[Schedule: 매일 오전 9시]
→ [HTTP Request: 경쟁사 상품 페이지]
→ [HTML Extract: 가격, 상품명]
→ [Code: 가격 파싱 (₩ 제거, 숫자 변환)]
→ [Google Sheets: 기록]
→ [IF: 가격 변동?]
true → [Slack: "⚠️ 경쟁사 가격 변경!" 알림]
가격 파싱 Code 노드
const items = $input.all();
return items.map(item => {
const priceText = item.json.가격 || '0';
const price = parseInt(priceText.replace(/[^0-9]/g, ''), 10);
return {
json: {
상품명: item.json.상품명?.trim(),
가격: price,
수집일: new Date().toISOString().split('T')[0],
원본가격: priceText
}
};
});
윤리적 크롤링 주의사항
| 원칙 | 설명 |
|---|---|
| robots.txt 확인 | 크롤링 허용 여부를 먼저 확인 |
| Rate Limiting | 요청 간 1~2초 이상 대기. 서버에 부하를 주지 말 것 |
| 이용약관 준수 | 사이트의 ToS에서 크롤링 금지 여부 확인 |
| 개인정보 보호 | 개인 식별 정보 수집 금지 |
| 캐싱 | 같은 페이지를 반복 요청하지 않도록 결과 캐싱 |
| API 우선 | API가 있으면 크롤링 대신 API 사용 |
⚠️ 경고: 무분별한 크롤링은 법적 문제를 야기할 수 있다. 항상 합법적이고 윤리적인 방법으로 데이터를 수집하자.
📝 정리
- [x] 크롤링 기본: HTTP Request(Text 모드) → HTML Extract(CSS 셀렉터)
- [x] CSS 셀렉터: 태그, 클래스(
.), ID(#), 속성([href]) 조합으로 정밀 추출 - [x] 반환 값: Text(텍스트만), HTML(태그 포함), Attribute(href, src 등)
- [x] 페이지네이션: URL 패턴 또는 "다음 페이지" 링크 추적
- [x] 윤리적 크롤링: robots.txt, Rate Limiting, 이용약관 준수 필수
다음 편 예고
21편: n8n × AI 입문 — LLM 노드로 텍스트 자동 생성
Part 4 시작! OpenAI, Claude, Gemini를 n8n에 연결하여 텍스트 요약, 번역, 감성 분석을 자동화한다. AI 워크플로우의 첫걸음.