Chrome DevTools · Elements · Network · Console · 데이터 위치 찾기
박수현 · aSSIST University · 2026
🎨 이미지 프롬프트(배경): "A modern browser window with developer tools open, showing network requests and DOM tree, glowing teal and purple highlights, clean editorial illustration, 16:9, no text"
📖 도서 매핑: Ch.3 §3 + 보강
데이터의 위치·형태·접근 방법 을 먼저 확인한 뒤 코드를 작성한다.
DevTools = 데이터 위치 탐색기
데이터 구조를 모르고 코드부터 쓰면 → 이틀 헤맨 후 처음으로 돌아옴.
📖 도서 참조: Ch.3 §3 — "F12, 한 번 누르고 평생 쓴다"

| 패널 | 보조 단축키 | 핵심 용도 | 스크래퍼 활용 |
|---|---|---|---|
| Elements | F12 → 클릭 (요소 선택기 = Ctrl+Shift+C / macOS Cmd+Shift+C) |
라이브 DOM 탐색 | 선택자 발굴 |
| Console | Ctrl+Shift+J (Win/Linux) / Cmd+Opt+J (macOS) |
JS 실행·로그 | 선택자 검증 |
| Network | 패널 안에서 Ctrl+R = 캡처 + 새로고침 |
모든 통신 캡처 | JSON API 추적 |
| Sources | 패널 안에서 Ctrl+P = 파일 검색 |
JS 코드·중단점 | 로직 역공학 |
| Application | — | 저장소·쿠키 | 세션·로그인 분석 |
📌
Ctrl+Shift+C는 「요소 선택기」 단축키 (Elements 패널 단축키가 아니라 마우스로 페이지의 요소를 클릭해 즉시 점프하는 모드)🎯 사용 빈도
스크래퍼 입장 8:2 비율 — Network + Elements 가 80%, 나머지가 20%.
"마우스로 페이지를 가리키면 코드가 점프한다"
키워드: 요소 선택기 · Computed · 검사 · 편집

평소 보이는 화면. 책 카드 20개가 격자로 배치.

같은 페이지의 DOM 트리.
페이지의 모든 데이터가 여기 어딘가에.
Ctrl+Shift+C)article div span?💡 페이지에 처음 접근하는 모든 작업의 0번 단계.


article.product_pod ← 카드 단위
├ div.image_container
│ └ a > img.thumbnail ← 썸네일
├ p.star-rating .Three ← 별점 (클래스로!)
├ h3
│ └ a [title="..."] ← 책 제목
├ div.product_price
│ ├ p.price_color ← £가격
│ └ p.instock.availability ← 재고
부모(article.product_pod) → 자식(h3 > a) 체인이 그대로 CSS 선택자.
요소 위에서 우클릭 → Copy 하면 즉시 쓸 수 있는 6가지.
| 항목 | 결과 | 스크래퍼 용도 |
|---|---|---|
| Copy selector | div.foo > a:nth-child(2) |
BS4 select() 즉시 사용 |
| Copy XPath | //*[@id="x"]/a[2] |
Selenium XPath |
| Copy outerHTML | <a class="..">...</a> (요소 자체 포함) |
구조 분석·LLM에 입력 |
| Copy element | outerHTML 통째 (Chrome 메뉴 명칭) | 빠른 복붙 — outerHTML 과 동일 |
| Copy JS path | document.querySelector(...) |
콘솔에서 검증 |
| Edit as HTML | 인라인 편집 | 가설 테스트 |
📌
outerHTML= 요소 자체 + 안쪽 모두 /innerHTML= 요소 안쪽만. Chrome 의 "Copy element" 는 outerHTML 과 같은 결과.⚠️ Copy selector 가 너무 길면 부서지기 쉽다. 사람이 짧게 다듬어 쓰기.
$(selector) = document.querySelector(...) (첫 1개)$$(selector) ≈ document.querySelectorAll(...) (전체)$$ 는 결과를 NodeList 가 아닌 Array 로 반환 → .map() · .filter() 바로 사용 가능$0 = Elements 에서 선택된 요소copy(value) = 클립보드 복사$$("article.product_pod").length
// → 20
$("article.product_pod h3 a").innerText
// → "A Light in the ..."
$$(".price_color").length // → 20
$0.outerHTML // → 방금 클릭한 요소
copy($$("a").length) // → 클립보드로
🎯 작업 흐름
1) Elements 에서 선택 → 2) 콘솔에서
$0으로 받아 검증 → 3) BS4 코드로 옮김
F12 → Elements + Console 만 사용.
5개 데이터의 선택자를 종이에 적어 가져오세요.
| # | 데이터 | 힌트 | 검증 |
|---|---|---|---|
| 1 | 책 카드 1개 단위 | article.____ |
$$(...).length === 20 |
| 2 | 책 제목 | h3 a 의 title 속성 |
.innerText 또는 ["title"] |
| 3 | 가격 | 클래스에 price |
text 에 £ 포함 |
| 4 | 별점 | 클래스에 star-rating |
클래스 두 번째가 "One"~"Five" |
| 5 | 다음 페이지 | 푸터 페이지네이션 | href 속성 |
📌 정답은 다음 슬라이드 — 먼저 시도!
| 데이터 | 선택자 |
|---|---|
| 책 카드 | article.product_pod |
| 책 제목 | article.product_pod h3 a |
| 가격 | article.product_pod .price_color |
| 별점 | article.product_pod .star-rating |
| 다음 | li.next a |
$$("article.product_pod").length
// → 20
$$("article.product_pod h3 a")
.map(a => a.title)
// → ["A Light in the ...", ...]
$$(".price_color")
.map(p => p.innerText)
// → ["£51.77", "£53.74", ...]
$("li.next a").href
// → ".../page-2.html"
🎯 회차 3 미리보기
이 5개 선택자가 그대로 BeautifulSoup
select()인자로 들어갑니다.
"동적 페이지의 실제 데이터는 여기 어딘가에 있다"
키워드: XHR/Fetch · 필터 · Headers · Preview · Copy as cURL
XHR = XMLHttpRequest, JS 가 만든 비동기 데이터 요청 / AJAX = 페이지 새로고침 없이 서버와 통신하는 방식 (XHR 또는 fetch 사용)
Ctrl+R)api, json)
평범한 시세 페이지. 사용자에겐 한 화면.

같은 페이지에서 수십 개의 요청.
Fetch/XHR 필터로 좁히면 데이터 전송용 요청만 남는다.
🔑 보이는 한 화면 뒤에 여러 개의 JSON API가 숨어 있을 수 있다.
| 열 | 의미 | 스크래퍼 시점 |
|---|---|---|
| Name | URL 끝 부분 | path 패턴 단서 |
| Status | 200 OK = 정상 / 304 = 캐시 그대로 / 404 = 없음 | 정상 응답인지 |
| Type | xhr·fetch = 비동기 데이터 / document = HTML / image = 그림 | xhr/fetch = 데이터 후보 |
| Initiator | 어느 코드가 호출했나 | 스크립트 추적 |
| Size | 응답 크기 | 큰 거 = 본 데이터 |
| Time | 응답 시간 | 병목 분석 |
xhr 또는 fetch → JS 가 동적으로 호출/api/, /v1/, .json, ?format=json?_=1714512000 (캐시버스터 — 같은 URL 을 매번 새로 받기 위해 붙이는 더미 파라미터)Content-Type: application/json (서버가 "이건 JSON" 이라고 알려주는 표시)Preview 트리 (이상적):
data ▾
items ▾ (length: 20)
[0] ▾
name: "갤럭시 S24"
price: 1390000
stock: 12
[1] ▾ ...
🎯 Preview 가 JSON 트리 = 빙고. 다음 슬라이드의 Copy as cURL 로 바로 추출 가능.
requests 코드 자동 생성curl 'https://finance.naver.com/item/main.naver?code=005930' \
-H 'User-Agent: Mozilla/5.0 ... Chrome/120.0' \
-H 'Accept: text/html,application/xhtml+xml' \
-H 'Cookie: NID_AUT=...; NID_SES=...' \
-H 'Referer: https://finance.naver.com/'
# curlconverter 가 만들어주는 코드 (예시)
import requests
headers = {
"User-Agent": "Mozilla/5.0 ... Chrome/120.0",
"Accept": "text/html,application/xhtml+xml",
"Referer": "https://finance.naver.com/",
}
r = requests.get("https://finance.naver.com/item/main.naver?code=005930", headers=headers)
print(r.text[:500])
⚠️ 위 URL 은 형식 예시. 실제로는 본인이 F12 Network 에서 캡처한 실제 엔드포인트를 그대로 복사해서 사용 (도메인/경로가 시기마다 바뀜).
🎯 회차 6 의 로그인 분석에 이 기법 필수.
General — URL · Method · Status · Remote Address
Response Headers — 서버가 보낸 헤더
Request Headers — 브라우저가 보낸 헤더
Query/Form Params — 파싱된 파라미터
| 헤더 | 왜 필요? |
|---|---|
User-Agent |
기본 python-requests/... 가 차단되어 403 자주 — Chrome UA 로 위장 |
Cookie |
서버가 발급한 세션 ID — 로그인 상태 유지 |
Referer |
직전 페이지 URL — 핫링크 차단·CSRF 방어 검사용 |
X-Requested-With: XMLHttpRequest |
브라우저 JS 가 보내는 표식 → 서버가 "AJAX 요청이구나" 인식 |
Authorization |
API 인증 토큰 (Bearer / API Key) |
📌 응답 헤더의
Content-Type: application/json은 JSON 임을 확정 짓는 도장.
r = requests.get(url, headers=headers)
data = r.json()
for item in data["items"]:
print(item["name"], item["price"])
트리에서 본 키 경로 가 그대로 코드.
🎯 회차 5 미리보기
Preview 트리 → 코드 → DataFrame → 1초에 100건 수집.
https://finance.naver.com/item/main.nhn?code=005930(삼성전자)
⚠️ 시연 후 도덕적 주의 — 회차 4·7 윤리 파트 예고.
로그인의 비밀이 여기에 있다
키워드: Cookies · LocalStorage · SessionStorage · IndexedDB
| 컬럼 | 의미 |
|---|---|
| Name | NID_AUT, session_id |
| Value | 보통 암호화된 토큰 |
| Domain | 어느 도메인의 쿠키인가 |
| Path | 어느 path 에서만 보내나 |
| Expires | Session = 브라우저 닫으면 끝 |
| HttpOnly | JS 접근 차단 (탈취된 JS 가 쿠키 못 읽음) |
| Secure | HTTPS 연결에서만 전송 (HTTP 평문 노출 방지) |
| SameSite | 다른 사이트가 사용자 권한을 도용해 요청하는 CSRF (Cross-Site Request Forgery) 공격 방지 정책 |
requests.get(..., cookies=...) 로 옮기면 로그인 우회📖 도서 참조: Ch.5 §3 — "세션과 쿠키, 그리고 그들의 사회생활"
| 항목 | LocalStorage | SessionStorage |
|---|---|---|
| 수명 | 영구 (사용자가 직접 지우거나 코드로 제거할 때까지) | 탭 닫으면 끝 |
| 공유 | 같은 도메인의 모든 탭에서 공유 | 그 탭에서만 접근 가능 |
| 크기 | ~5MB | ~5MB |
| 서버 자동 전송 | ❌ (쿠키와 달리 자동으로 안 감) | ❌ |
📌 쿠키는 매 요청마다 자동으로 서버에 전송되지만, LocalStorage 는 그렇지 않음. 인증 토큰을 LocalStorage 에 두면 JS 가 매 요청마다 직접 헤더에 실어줘야 함 (
Authorization: Bearer ...).
// 모든 키
Object.keys(localStorage)
// 특정 값
localStorage.getItem("auth_token")
// 전체 덤프
JSON.stringify(localStorage)
⚠️ 일부 사이트는 인증 토큰을 LocalStorage에 저장. 쿠키와 별개로 챙겨야 함.
어떤 도구로 갈 것인가 — 시간을 아끼는 결정 트리
각 단계에서 「아니오」 면 ↓ 다음 단계로
1
Ctrl+U 소스에 원하는 데이터가 이미 있나?
✅ "있다" → requests + BS4가장 빠름·안정적 (회차 3)
2
Network Fetch/XHR 에 JSON 응답이 있나?
✅ "있다" → JSON API 직접 호출Copy as cURL → Python 변환 (회차 5)
3
로그인·캡차·쿠키가 필요한가?
⚠️ "예" → Selenium + 로그인본인 계정·약관 점검 필수 (회차 6)
4
위 3개가 모두 "아니오"
✅ Selenium / Playwright렌더 의존 페이지 자동화 (회차 6)
🔑 우선순위
JSON API > requests + BS4 > Selenium — 위에서부터 시도, 안 되면 한 단계 내려가기.
requests.get() 이 보는 것과 동일원하는 데이터가 Ctrl+U 에 있다 → 정적 → requests 가능
Ctrl+U 엔 없는데 F12 엔 있다 → 동적 → JS 실행 필요
| 사이트 | Ctrl+U? | Network XHR? | 판정 |
|---|---|---|---|
books.toscrape.com |
? | ? | ? |
finance.naver.com/item/main.nhn?code=005930 |
? | ? | ? |
news.naver.com (메인) |
? | ? | ? |
자주 쓰진 않지만 알면 빛나는 도구
Ctrl+P 로 파일명 검색 → 빠르게 점프{}) — 한 줄로 압축된 코드를 사람이 읽기 좋게 들여쓰기 복원Ctrl+Shift+F — 모든 JS 파일 텍스트 검색"api/v1" → 하드코딩된 API URL 발견🎯 활용 사례: "이 사이트가 어떤 API 호출하는지" 빠르게 파악할 때
api,fetch단어 전역 검색.
<h1>, <article>, <nav>) ↑ → 스크래핑 ↑📌 두 패널은 부가 정보. 주력은 Network + Elements.
오늘 학습한 F12 5대 패널은 이후 실습에서 반복적으로 활용됩니다.
Ctrl+Shift+C) → 마우스로 페이지 → HTML 점프$$() 로 선택자 검증requests 변환"코드는 다음 회차. 오늘은 눈 을 만들었다."