How to Scrape a Paginated Website with Firecrawl
Quick Summary
Firecrawl의 스키마 기반 JSON 추출과 빈 결과 배열을 종료 조건으로 활용하면 페이지 수를 미리 알지 못해도 페이지네이션된 사이트의 전체 데이터를 안정적으로 수집할 수 있다.
🖼️ 인포그래픽
🖼️ 4컷 인포그래픽
💡 한 줄 요약
Firecrawl의 스키마 기반 JSON 추출과 빈 결과 배열을 종료 조건으로 활용하면 페이지 수를 미리 알지 못해도 페이지네이션된 사이트의 전체 데이터를 안정적으로 수집할 수 있다.
📌 핵심 요약
- Firecrawl은 원시 HTML 대신 일관된 구조의 JSON을 반환하므로 페이지별 마크업을 직접 분석하지 않고도 수집 결과를 처리할 수 있다.
- 공식 SDK·CLI·MCP 서버의 스크레이프 기능은 API 키 없이도 IP별 속도 제한 아래 사용할 수 있으며, 키를 발급받으면 더 높은 한도와 추가 엔드포인트를 이용할 수 있다.
- Node SDK 예제는 Zod로 제목·URL·점수·댓글 수를 갖는 스토리 배열을 정의하고, 이를 JSON 스키마로 변환해 Hacker News의 각 페이지에서 같은 형태의 데이터를 추출한다.
- 페이지 번호를 증가시키며 반복 호출하고 스토리 배열이 처음 비는 시점에 중단하는 방식으로 Hacker News 61페이지에서 약 1,040개 미만의 스토리를 하나의 파일에 저장한다.
- 오프셋 방식은 시작 위치를 페이지 크기만큼 늘려 같은 패턴을 적용하며, URL이 바뀌지 않는 무한 스크롤이나 클릭 방식에는 브라우저를 제어하는 상호작용 엔드포인트가 필요하다.
🧩 주요 포인트
- 마크다운 출력은 연결 확인에는 편리하지만 필드가 뒤섞인다 → 스키마 기반 JSON을 사용하면 정규식이나 사이트별 파싱 없이 바로 처리 가능한 데이터가 된다.
- 고정 페이지 수나 다음 링크에 의존하지 않고 빈 배열을 경계로 삼는다 → 실제 마지막 페이지를 추측하지 않으면서 동일한 종료 규칙을 적용할 수 있다.
- CLI·MCP의 대화형 호출은 일회성 수집에 적합하고 반복문 코드는 무인 실행에 적합하다 → 정기 작업·CI·백엔드에서는 페이지마다 LLM을 호출하지 않는 코드 방식이 더 반복 가능하다.
🧠 상세 정리
1. 첫 페이지만 수집하고 끝나는 문제
페이지네이션된 사이트를 에이전트에게 단순히 수집하도록 요청하면 첫 페이지의 결과만 가져온 뒤 전체 작업이 끝났다고 판단할 수 있다. Hacker News의 경우 한 페이지에 최대 30개 스토리가 표시되지만, 실제 데이터는 그 뒤의 여러 페이지에 계속 존재한다. 원문은 에이전트가 전체 페이지 수를 신뢰성 있게 알 수 없다는 점을 핵심 문제로 제시한다. Firecrawl은 원시 HTML이 아니라 각 페이지에서 같은 형태의 구조화된 JSON을 반환하게 하여 사이트별 마크업이나 다음 페이지 링크를 직접 분석할 필요를 없앤다. 그 결과 정의된 결과 배열이 처음으로 비는 페이지를 경계로 삼아, 전체 페이지 수를 사전에 추측하지 않고도 반복 수집을 종료할 수 있다.
2. API 키와 사용 가능한 접근 방식
Firecrawl의 스크레이프 엔드포인트는 공식 SDK, CLI 또는 MCP 서버에서 API 키 없이 호출할 수 있으며, 이 경우 IP를 기준으로 속도 제한이 적용된다. 더 높은 호출 한도가 필요하거나 크롤링·추출·맵 기능을 제공하는 다른 엔드포인트를 사용하려면 Firecrawl 대시보드에서 API 키를 발급받아야 한다. 예제는 Node SDK를 설치하고 환경 변수의 API 키를 선택적으로 전달해 클라이언트를 생성하는 방식으로 시작한다. 다만 페이지네이션 처리 원리는 Node에만 한정되지 않으며 Python SDK, CLI, MCP 서버에서도 동일하게 적용할 수 있다. 따라서 인증 여부는 호출 한도와 이용 가능한 기능에 영향을 주지만, 기본적인 페이지 단위 스크레이프 자체는 키 없이도 시험할 수 있다.
3. 마크다운으로 수행하는 첫 스크레이프
첫 번째 예제는 Hacker News의 첫 페이지를 스크레이프하면서 출력 형식을 마크다운으로 지정해 클라이언트 연결과 기본 호출이 동작하는지 확인한다. 마크다운은 별도로 출력 형식을 지정하지 않아도 적용되는 기본 형식이므로, 예제의 명시적 설정은 동작을 분명하게 보여주기 위한 것이다. 호출 결과에는 페이지에 있는 모든 스토리의 제목, 점수, 댓글 정보 등이 하나의 긴 텍스트 안에 섞여 반환된다. 이런 결과는 내용을 빠르게 읽거나 연결 상태를 점검하는 데에는 유용하지만, 개별 필드를 안정적으로 분리해야 하는 프로그램 처리에는 적합하지 않다. 같은 호출을 반복해도 첫 페이지에 해당하는 30개 항목만 다시 반환되므로, 출력 형식을 바꾸는 것과 별개로 페이지 이동 로직도 추가해야 한다.
4. 스키마 기반의 구조화된 JSON 추출
프로그램에서 바로 사용할 수 있는 결과를 얻기 위해 예제는 출력 형식을 JSON으로 바꾸고, 페이지에서 추출할 내용을 프롬프트와 스키마로 정의한다. Zod 스키마는 스토리 배열과 각 스토리의 제목, URL, 점수, 댓글 수를 명시하며, Node SDK는 이를 API가 실제로 받는 JSON 스키마로 변환한다. Firecrawl은 이 요청에 맞춰 결과 객체의 JSON 필드에 동일한 구조를 가진 스토리 배열을 반환한다. 따라서 마크다운에서 특정 문자열을 찾거나 정규식을 작성하고, 사이트의 HTML 구조에 맞춘 별도 파서를 유지할 필요가 없다. Zod는 Node SDK에서 스키마를 편리하게 작성하기 위한 선택 사항일 뿐이며, 사용자는 같은 내용을 담은 JSON 스키마를 직접 작성해 전달할 수도 있다.
5. 빈 배열을 종료 신호로 사용하는 반복문
전체 페이지를 순회하는 코드는 페이지 번호를 1로 초기화한 뒤 무한 반복문 안에서 Hacker News의 주소에 페이지 매개변수를 붙여 스크레이프한다. 각 호출은 동일한 JSON 스키마와 추출 프롬프트를 사용하므로, 반환된 스토리 배열의 길이를 매번 같은 방식으로 검사할 수 있다. 배열에 항목이 있으면 각 스토리에 현재 페이지 번호를 추가해 전체 결과 배열에 넣고, 다음 호출을 위해 페이지 번호를 하나 증가시킨다. 반대로 반환된 배열의 길이가 0이면 더 수집할 결과가 없는 것으로 판단하고 즉시 반복문을 종료한다. 수집이 끝난 뒤에는 전체 배열을 들여쓰기된 JSON으로 직렬화해 stories.json 파일에 기록하며, 이 과정에는 예상 페이지 수나 HTML의 다음 링크를 해석하는 로직이 필요하지 않다.
6. 실행 결과와 출처 페이지 추적
원문의 코드를 Hacker News에 실행하면 61개 페이지를 순서대로 방문해 약 1,040개에 조금 못 미치는 스토리를 하나의 파일에 수집한다. 이 결과는 전체 페이지 수를 코드에 고정하거나 마지막 페이지를 사전에 조사하지 않고, 실제 응답이 비는 시점까지 진행해 얻어진다. 각 스토리에는 원래 스키마의 제목·URL·점수·댓글 수뿐 아니라 수집 당시의 페이지 번호도 함께 저장된다. 페이지 번호를 기록해 두면 나중에 특정 항목이 어느 페이지에서 추출됐는지 원본 위치를 추적할 수 있다. 구조화된 결과와 출처 페이지 정보를 함께 보존함으로써, 사이트의 HTML 마크업을 별도로 저장하거나 파싱 규칙과 계속 동기화하지 않고도 수집 데이터의 위치를 확인할 수 있다.
7. 오프셋·전체 사이트·무한 스크롤 처리
모든 사이트가 페이지 번호 매개변수를 사용하는 것은 아니며, 일부 포럼이나 디렉터리는 시작 위치 또는 오프셋을 페이지 크기만큼 증가시키는 방식을 사용한다. 이 경우 반복문의 전체 구조와 빈 배열을 확인하는 종료 조건은 그대로 유지하고, 페이지 번호 증가 대신 시작 위치에 페이지 크기를 더하면 된다. 알려진 페이지네이션 주소를 수집할 때는 대상 URL과 추출 스키마만 해당 사이트의 필드에 맞게 바꾸어 같은 흐름을 재사용할 수 있다. 반면 루트 URL에서 사이트 전체의 링크를 발견하고 따라가야 한다면 페이지 번호 반복문보다 크롤링 엔드포인트가 그 목적에 맞는다. URL이 변하지 않는 무한 스크롤이나 버튼 클릭 방식은 단순 URL 증가로 탐색할 수 없으므로, 실제 브라우저 세션에서 스크롤·클릭·입력을 수행하는 상호작용 엔드포인트를 사용해야 한다.
8. 대화형 수집과 무인 실행 코드의 선택
MCP 서버와 CLI에서도 에이전트가 대화 세션 안에서 페이지마다 한 번씩 호출하는 방식으로 페이지네이션을 순회할 수 있다. 이 방식은 즉시 데이터를 가져오는 일회성 요청에는 사용할 수 있지만, 매번 대화형 에이전트가 각 페이지 호출을 주도하게 된다. 정기 작업, CI 파이프라인 또는 백엔드 서비스처럼 사람의 개입 없이 반복 실행해야 하는 경우에는 명시적인 반복문을 가진 코드가 더 적합하다고 원문은 설명한다. 코드 방식은 페이지마다 LLM 호출을 요구하지 않으며, 같은 조건으로 반복 실행하고 결과를 채팅 창이 아닌 파일이나 데이터 저장소에 남길 수 있다. 결국 스키마가 보장하는 일관된 응답과 빈 배열 종료 조건을 코드로 결합하면, 대화형 판단에 의존하지 않는 재현 가능한 페이지네이션 수집 절차가 된다.
🧾 핵심 주장 / 시사점
- 페이지네이션의 종료 판단을 사이트의 탐색 UI가 아니라 스키마로 정의된 결과 배열에 연결하면, 페이지 번호 방식과 오프셋 방식에 같은 제어 흐름을 적용할 수 있다.
- 수집 시 각 항목에 페이지 번호를 함께 저장하는 것은 데이터 값만 보존하는 것보다 원본 위치를 확인하기 쉬운 결과를 만든다.
- 페이지 이동이 URL에 드러나는지는 도구 선택의 기준이 되며, URL 기반 이동은 반복 스크레이프가 처리하고 화면 상호작용만 가능한 이동은 브라우저 세션이 처리한다.
✅ 액션 아이템
- Firecrawl 수집은 마크다운 대신 스키마 기반 JSON으로 제목·URL·점수·댓글 수 필드를 고정해 사이트별 파싱을 제거한다.
- 페이지네이션은 고정 페이지 수 대신 스토리 배열이 처음 비는 시점을 종료 조건으로 두고 페이지 번호를 증가시키며 반복 호출한다.
- Hacker News처럼 URL이 안 바뀌는 무한 스크롤·클릭 방식은 일반 스크레이프가 아니라 브라우저 상호작용 엔드포인트 필요 여부를 먼저 가른다.
❓ 열린 질문
- 빈 결과 배열 종료 규칙을 Hacker News 61페이지·약 1,040개 미만 사례 밖으로 옮겨도 같은 안정성을 유지하는가?
- 공식 SDK·CLI·MCP를 API 키 없이 IP별 속도 제한으로 쓸 때와 키 발급 후 높은 한도·추가 엔드포인트 사용의 분기 기준은 무엇인가?
- 정기 작업·CI·백엔드에서 페이지마다 LLM을 호출하지 않는 Node SDK 반복문 코드가 CLI·MCP 대화형 호출보다 항상 적합한가?