
“크롤러로 사이트를 긁을 때 브라우저를 띄워야 할까요?” 답은 대부분 아니오입니다. 개발자 도구의 네트워크 탭을 먼저 열어서 페이지가 실제로 호출하는 JSON API를 찾아내면, 셀레니움이나 플레이라이트 같은 브라우저 자동화 없이도 데이터를 훨씬 빠르고 가볍게 가져올 수 있습니다. 다만 모든 사이트에 이 방법이 통하지는 않아서, 확인 순서와 브라우저가 꼭 필요한 예외 상황까지 함께 정리했습니다.
브라우저 렌더링과 API 호출, 체감 속도 차이가 이렇게 납니다
브라우저로 페이지를 열면 HTML, CSS, 이미지, 폰트, 자바스크립트 실행까지 전부 거쳐야 눈에 보이는 화면이 완성됩니다. 반면 페이지가 내부적으로 호출하는 JSON API를 직접 두드리면 필요한 데이터만 담긴 응답을 바로 받아옵니다. 크롬 개발자도구(F12)의 네트워크 탭을 기준으로 보면, 같은 목록 페이지라도 화면을 그리는 데 들어가는 요청 수는 수십 건인 반면 실제 데이터를 담은 API 응답은 보통 한두 건으로 끝납니다.
브라우저가 자바스크립트로 직접 호출할 때는 다른 도메인으로 보내는 요청이 CORS 정책에 막혀 응답을 읽지 못하는 경우가 있는데, 파이썬 스크립트에서 requests로 같은 주소에 직접 요청하면 이 제한 없이 응답을 그대로 받을 수 있는 경우가 많습니다. 서버 입장에서는 둘 다 그냥 HTTP 요청이기 때문입니다.
| 구분 | 브라우저 자동화(Playwright 등) | JSON API 직접 호출 |
|---|---|---|
| 받아오는 데이터 | HTML, CSS, 이미지까지 전부 | 필요한 JSON 응답만 |
| 실행 환경 | 브라우저 엔진 구동 필요 | HTTP 요청만으로 충분 |
| 자바스크립트 실행 | 필요 | 불필요(응답이 이미 완성된 JSON) |
| 적합한 상황 | 로그인 유지, 클릭·스크롤 재현 | 목록·검색 결과 같은 구조화된 데이터 |
개발자 도구 Network 탭에서 API를 찾는 순서
순서는 생각보다 단순합니다. F12로 개발자도구를 열고 Network 탭에서 XHR과 Fetch 필터만 켠 다음, 페이지를 새로고침하거나 검색·스크롤 같은 동작을 직접 실행해 봅니다. 그러면 화면에 보이는 데이터와 같은 내용이 담긴 요청이 목록에 떠오르고, 그 요청을 클릭해서 Preview나 Response 탭을 보면 Content-Type이 application/json인지 바로 확인됩니다.

원하는 요청을 찾았으면 우클릭해서 Copy as cURL로 헤더 구성을 그대로 복사하거나, Headers 탭에서 필요한 값만 눈으로 옮겨 적어도 됩니다. 아래는 공개 테스트 API인 httpbin.org로 같은 과정을 흉내 낸 코드입니다.
import requests
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json",
}
res = requests.get("https://httpbin.org/get", headers=headers)
print(res.status_code)
print(res.json()["headers"]["Accept"])
실행 결과는 다음과 같습니다.
200
application/json
실제 대상 사이트에서는 httpbin.org 자리에 네트워크 탭에서 확인한 API 주소를 넣고, 응답에 로그인 토큰이나 Referer 값이 필요하면 headers 딕셔너리에 그대로 추가하면 됩니다. requests 라이브러리의 세션(Session) 객체를 쓰면 쿠키를 자동으로 유지해 주기 때문에 로그인 상태가 필요한 API에서도 편리합니다.
XHR/Fetch에 아무 응답도 안 보이면 어떻게 하나요?
필터를 켜도 목록이 비어 있다면 몇 가지 경우를 의심해 볼 수 있습니다. 데이터가 WebSocket으로 실시간 전송되는 경우에는 Network 탭에서 WS 필터로 따로 확인해야 하고, 서버사이드 렌더링(SSR) 사이트라면 별도의 API 호출 없이 처음 HTML 응답 안에 데이터가 이미 포함돼 있을 수 있습니다.
이럴 때는 페이지 소스에서 <script> 태그를 검색해 보는 쪽이 빠릅니다. Next.js 기반 사이트라면 __NEXT_DATA__라는 이름으로 JSON이 그대로 박혀 있는 경우가 흔하고, 이 값만 파싱해도 별도의 API 호출 없이 똑같은 데이터를 얻을 수 있습니다. Network 탭 상단의 검색창(돋보기 아이콘)에 화면에 보이는 텍스트 일부를 넣고 All 필터로 전체 요청을 뒤져보는 방법도 꽤 잘 통합니다.
숨은 API를 찾아도 브라우저가 필요한 경우

API를 찾았다고 끝이 아닙니다. Cloudflare 같은 봇 차단 서비스가 걸려 있으면 요청 헤더를 아무리 똑같이 맞춰도 서버가 자바스크립트 실행 여부나 브라우저 핑거프린팅까지 검사해서 차단하는 경우가 있습니다. 이런 사이트는 requests만으로는 뚫기 어렵고, 자바스크립트 실행 환경 자체가 필요합니다.
요청마다 서명 토큰(signature)을 만들어 붙이는 구조도 까다로운 경우입니다. 토큰 생성 로직이 난독화된 자바스크립트 안에 있으면 토큰 값만 베껴서는 재사용이 안 되고, 매번 새로 계산해야 합니다. 이때는 토큰 생성 부분만 자바스크립트 실행 엔진으로 돌리는 절충안도 있지만, 대부분은 Playwright 같은 도구로 브라우저를 띄워 해당 동작을 그대로 재현하는 쪽이 유지보수하기 쉽습니다.
토큰 값이 계속 바뀌는데, API 호출이 의미 없는 건가요?
꼭 그렇지는 않습니다. 토큰이 타임스탬프와 고정된 비밀값을 섞어 해시만 다시 계산하는 수준이라면, 그 규칙을 역으로 찾아내 파이썬 코드로 똑같이 구현할 수 있는 경우도 있습니다. 네트워크 탭에서 같은 요청을 시간차를 두고 여러 번 비교해 보면 토큰 값이 바뀌는 패턴이 보일 때가 많습니다.
패턴을 못 찾았다면 하이브리드 방식도 괜찮은 선택입니다. 처음 한 번만 브라우저로 페이지에 접속해 토큰이나 쿠키를 받아오고, 이후 같은 세션 안에서 보내는 나머지 요청은 requests로 처리하는 방식입니다. 브라우저를 매 요청마다 띄우는 것보다는 자원을 훨씬 덜 씁니다.
이 순서대로 10분만 확인해 보세요
정리하면 이렇습니다. 먼저 Network 탭의 XHR/Fetch 필터로 JSON 응답이 있는지 확인하고, 있다면 헤더를 복사해 requests로 재현해 봅니다. 응답이 막히거나 토큰이 매번 달라진다면 패턴을 먼저 찾아보고, 그래도 안 풀리면 그제야 Playwright로 넘어가는 순서가 가장 자원을 적게 씁니다.
이런 식으로 서버가 내려주는 JSON을 직접 읽어오는 방식도 넓게 보면 웹 크롤러가 하는 일의 한 갈래입니다. 브라우저를 먼저 띄우는 습관을 버리고 Network 탭부터 열어보는 순서를 한 번만 몸에 익혀두면, 다음 사이트를 긁을 때도 똑같은 순서로 훨씬 빠르게 끝낼 수 있습니다.
