Python으로 Craigslist를 스크래핑하는 방법 (차단당하지 않고)

최종 업데이트: July 15, 2026
Python으로 Craigslist를 스크래핑하는 방법 (차단당하지 않고)

Craigslist는 지금도 700개 안팎의 지역 사이트에서 월 1억 800만 방문을 끌어모으지만, 여전히 공개 API가 없어요. 아파트 매물, 중고차, 구인 공고, 단기 일감 광고에서 구조화된 데이터를 얻고 싶다면 사실상 스크래핑밖에 길이 없죠.

문제는 Craigslist 자체 안티봇이 꽤 빡세다는 거예요. Cloudflare나 DataDome이 아니라, 10년 넘게 다듬은 nginx 기반 자체 레이트 리미터를 돌리거든요. 잘못 건드리면 두 번째 커피를 마시기도 전에 403이 떨어져요. 저는 Craigslist 방어를 상대로 여러 접근법을 시험했고, 그 결과를 이 글에 담았어요. 2025년 기준 최신에, 카테고리를 안 가리는 Python 튜토리얼이에요. 옛 글 대비 가장 큰 개선점인 JSON-LD 추출, 현실적인 차단 회피 전략, 법적 이슈, 코드를 한 줄도 쓰기 싫은 분을 위한 노코드 대안까지 다 짚어요.

Python으로 Craigslist를 스크래핑한다는 건 무슨 뜻일까?

Craigslist 웹 스크래핑은 Python 스크립트로 Craigslist 페이지에 접속한 뒤, 제목·가격·설명·이미지·위치·게시 날짜 같은 구조화 데이터를 뽑아 스프레드시트, 데이터베이스, JSON 파일로 저장하는 작업이에요.

Python이 이 일에 제일 많이 쓰이는 건 라이브러리 생태계가 워낙 탄탄해서예요. requests, BeautifulSoup, lxml, curl_cffi만 잘 엮어도 100줄 안 되는 코드로 쓸 만한 Craigslist 스크래퍼가 나와요. 커뮤니티도 워낙 커서, Craigslist가 뭔가 바꿔도(실제로 자주 바꿔요) 누군가 이미 해법을 찾아 둔 경우가 많고요.

꼭 알아둘 게 있어요. Craigslist는 공개 읽기용 API를 제공하지 않아요. 유일한 공식 인터페이스인 Bulk Posting Interface(BAPI)는 쓰기 전용이에요. 승인받은 유료 게시자만 글을 올릴 수 있고, 데이터를 가져오는 용도가 아니죠. 외부 플랫폼에서 보이는 「Craigslist API」 제품은 죄다 공식 엔드포인트가 아니라 비공식 스크래퍼예요. 대량 데이터가 필요하다면 결국 스크래핑이에요.

왜 Craigslist를 스크래핑할까? 실제 활용 사례

Craigslist는 중고 소파나 찾는 곳이 아니에요. 수십 개 분야에 걸쳐 끊임없이 갱신되는 거대한 데이터셋이죠. 실제로 스크래핑 덕을 보는 사람들은 이래요.

활용 사례주요 사용자추출 데이터
아파트 및 임대료 모니터링부동산 중개인, 임차인, PropTech 기업가격, 면적, 침실 수, 동네, 위도/경도
중고차 시장 분석딜러십, 소비자 앱, 연구자가격, 제조사, 모델, 연식, 주행거리, 상태
채용 시장 리서치리크루터, 노동경제학자, 인력 분석가제목, 보상, 고용 형태, 게시일
리드 생성영업팀, 서비스 제공업체연락처 정보, 업체명, 서비스 지역
경쟁 가격 조사지역 서비스 업체, 이커머스 운영팀서비스 가격, 설명, 서비스 범위

가장 자주 인용되는 학술 사례는 Kaggle의 "Used Cars Dataset"이에요. 미국 중고차 매물 약 50만 건과 변수 26개를 담아, 2024년 ResearchGate의 미국 중고차 시장 동향 연구를 비롯한 논문 수십 편의 토대가 됐죠. 헤지펀드들은 임대료 추세 연구용으로 Craigslist 임대 데이터를 집계 형태로 사들이기도 했고, 영업팀은 서비스·일감 카테고리를 리드 생성용으로 꾸준히 긁어요.

계산은 단순해요. 손으로 복붙하면 8시간, 잘 만든 스크래퍼면 약 10분.

craigslist_stats_55285c3a34.png

Python으로 Craigslist 스크래핑하기: 자동차만이 아니다, 모든 카테고리

제가 본 Craigslist 스크래핑 가이드는 거의 다 중고차만 다뤄요. Google 튜토리얼인데 이미지 검색만 설명하는 격이죠. Craigslist는 카테고리가 아주 많고, URL 패턴도 제각각이에요.

기본 구조는 늘 이래요. https://{city}.craigslist.org/search/{category_slug}

도시 서브도메인과 슬러그만 바꾸면 완전히 다른 분야를 긁게 돼요. 아래는 가장 많이 쓰는 카테고리 참고표예요(2025년 4월 검증).

카테고리URL 슬러그주로 추출하는 필드
아파트 / 주거/search/apa가격, 면적, 침실 수, 위치, 반려동물 정책
자동차 & 트럭/search/cta가격, 제조사, 모델, 연식, 주행거리
채용/search/jjj제목, 회사, 급여, 고용 형태
서비스/search/bbb제목, 설명, 전화번호, 지역
일감/search/ggg제목, 보상, 날짜, 카테고리
판매(일반)/search/sss제목, 가격, 상태, 위치

필터링용 쿼리 파라미터도 같이 쓸 수 있어요.

파라미터용도예시
query전체 텍스트 키워드?query=studio
min_price / max_price가격 범위&min_price=1500&max_price=3000
hasPic이미지가 있는 게시물만&hasPic=1
postedToday최근 24시간&postedToday=1
sort정렬 방식&sort=priceasc
s페이지네이션 오프셋(페이지당 120개)?s=120

그러니까 https://newyork.craigslist.org/search/apa?min_price=1500&max_price=3000&hasPic=1 같은 URL은 사진이 있는 뉴욕 아파트 매물 중 $1,500~$3,000 범위를 보여줘요. 이 글의 Python 스크래퍼는 슬러그만 바꾸면 이 모든 카테고리에서 다 돌아가요.

2025 Craigslist HTML 셀렉터: 옛 방식 vs. 새 방식(그리고 JSON 지름길)

Craigslist 스크래퍼가 깨지는 가장 큰 이유는 HTML 구조가 바뀌어서예요. 2022년 튜토리얼처럼 .result-row.result-info를 노리고 있다면, 그 스크래퍼는 이미 끝난 거나 마찬가지예요.

Craigslist는 2023~2024년에 검색 결과 마크업을 다시 썼어요. 옛 클래스명이 새 래퍼 안에 들어가 있어서, DOM 최상위에서 그것만 노리면 빈 리스트가 나와요. 달라진 점은 이래요.

요소기존 셀렉터(2024년 이전)현재 셀렉터(2025)
매물 컨테이너.result-info.cl-search-result
제목 링크.result-title.posting-title a
가격.result-price.priceinfo
메타데이터(지역).result-hood.meta

하지만 진짜 핵심은 따로 있어요. 2025년 기준 제대로 된 스크래퍼라면 HTML을 아예 파싱할 필요가 없다는 점이에요.

Craigslist는 이제 보이는 매물 전부를 <script id="ld_searchpage_results"> 태그 안에 구조화된 JSON-LD로 심어 둬요. requests.get() 한 번이면 페이지의 모든 매물 schema.org ItemList를 그대로 받아요. 제목, 가격, 통화, 위치, 이미지 URL, 상세 페이지 링크까지 다 들어 있죠. JavaScript 렌더링도 필요 없고, CSS 셀렉터가 깨질 걱정도 없어요.

JSON-LD 방식은 더 빠르고, 더 안정적이고, Craigslist가 UI를 조금 손봐도 깨질 확률이 훨씬 낮아요. 지금 활발히 유지되는 GitHub 저장소들이 이 방식을 쓰고, 아래 튜토리얼도 이 방식으로 가요.

예외는 하나 있어요. JSON-LD 블록은 가격이 있는 카테고리, 즉 아파트(apa), 판매(sss), 자동차(cta), 주거(hhh)에는 보통 존재해요. 반면 채용(jjj), 일감(ggg), 커뮤니티(ccc), 서비스(bbb)는 schema.org/Offer 가격 정보가 없어서 대체로 비어 있거나 내용이 빈약해요. 이럴 땐 .cl-search-result 기반 HTML 경로로 돌아가면 돼요.

Python 스택 고르기: Requests + BS4 vs. Selenium vs. Playwright

스크래핑 포럼에 늘 나오는 질문이 있어요. 「어떤 라이브러리를 써야 하나요?」 Craigslist에 한해선 대부분 사이트보다 답이 훨씬 명확해요.

비교 항목requests + BeautifulSoupSeleniumPlaywright
속도초당 5~15페이지 (네트워크 병목)초당 0.3~1페이지초당 0.5~2페이지
JS 렌더링 콘텐츠아니오
메모리약 30~60MB약 400~700MB약 300~500MB
설정 복잡도낮음중간중간
안티봇 대응력낮음(헤더/프록시 필요)중간(실제 브라우저)중간~높음
Craigslist에 가장 적합한 용도검색 결과(JSON-LD)동적 콘텐츠가 있는 상세 페이지대규모 비동기 스크래핑
학습 난이도초보자 친화적보통보통

Craigslist 페이지는 서버에서 렌더링돼요. JSON-LD 블록도 초기 HTML에 들어 있고요. 읽기 경로엔 JavaScript 챌린지가 없어요. 활발히 유지되는 GitHub의 Craigslist 스크래퍼들도 대부분 requests + BeautifulSoup이나 Scrapy를 써요. Selenium이나 Playwright는 거의 안 쓰고요. 우연이 아니에요. 브라우저 자동화 프레임워크는 수백 MB 메모리에 10~100배 속도 손실을 떠안기면서, 오히려 더 눈에 띄는 지문만 남기고 실익은 거의 없거든요.

제 추천은 이래요.

  • requests + BS4: 여기서 시작하세요. JSON-LD 추출과 궁합이 가장 좋고, Craigslist 스크래핑의 95%는 이것으로 끝나요.
  • Selenium: 특정 상세 페이지에서 동적 콘텐츠와 상호작용해야 할 때만 쓰세요(Craigslist에선 드물어요).
  • Playwright: 수천 페이지를 비동기 병렬로 확장할 때 고려할 만하지만, 솔직히 Craigslist의 병목은 라이브러리 성능이 아니라 레이트 리미터예요.

더 자세한 비교가 궁금하면 Playwright vs. Puppeteer최고의 Python 웹 스크래핑 도구 글도 따로 있어요.

Python 없이 Craigslist 데이터 스크래핑하기 Get Started Free

노코드 대안: Python 없이 Craigslist 스크래핑하기

코드 설명에 들어가기 전에 잠깐 옆길로 새 볼게요. 이 섹션은 개발자가 아닌 분들 몫이에요. 부동산 중개인, 영업팀, 운영 관리자처럼 Python을 배울 생각은 없고 데이터만 필요하다면 더 빠른 길이 있어요.

Thunderbit은 Chrome 확장 프로그램으로 도는 AI 웹 스크래퍼예요. 코딩 없이 Craigslist를 약 두 번 클릭으로 긁을 수 있어요. 흐름은 이래요.

  1. 아무 Craigslist 검색 결과 페이지로 갑니다(아파트, 자동차, 채용 등 모든 카테고리 가능).
  2. Thunderbit 사이드바에서 **「AI Suggest Fields」**를 클릭해요. AI가 페이지를 읽고 제목, 가격, 위치, 링크 같은 열을 자동 감지해요.
  3. **「Scrape」**를 클릭하면 데이터가 몇 초 안에 추출돼요.
  4. 서브페이지 스크래핑으로 매물 상세 페이지를 하나씩 방문해 전체 설명, 전화번호, 이미지, 속성 정보까지 보강해요.
  5. Google Sheets, Excel, Airtable, Notion으로 바로 내보낼 수 있고, 모두 무료예요.

매일 아파트 가격을 보거나 매주 구인 공고를 모으는 식의 반복 작업이라면, Thunderbit의 Scheduled Scraper에 일정을 자연어로 설명만 해두면 자동으로 돌아요. cron job도, 서버 세팅도 필요 없어요.

Thunderbit는 Cloud Scraping 모드로 안티봇도 알아서 처리하니, 프록시를 돌리거나 헤더를 직접 꾸밀 걱정이 없어요. 직접 써보고 싶으면 Thunderbit Chrome Extension을 설치해 보세요.

직접 제어하고 커스터마이징하고 싶다면, 아래 Python 단계별 가이드를 계속 읽으세요.

단계별: Python으로 Craigslist를 스크래핑하는 방법(전체 튜토리얼)

  • 난이도: 중급
  • 소요 시간: 약 30분(설정 + 첫 스크래핑)
  • 준비물: Python 3.8+, Chrome 브라우저(페이지 검사용), 터미널

1단계: Python 환경 설정하기

필요한 라이브러리를 설치해요.

pip install requests beautifulsoup4 lxml

lxml은 선택이지만 BeautifulSoup 파싱 속도를 꽤 올려줘요. 나중에 TLS 지문 문제를 만나면(차단 방지 섹션에서 자세히 설명) curl_cffi도 깔 수 있어요.

pip install curl_cffi

임포트 코드는 이래요.

import requests
from bs4 import BeautifulSoup
import json
import csv
import time
import random

이제 필요한 의존성이 다 깔린 깨끗한 Python 환경이 준비됐어요.

2단계: 어떤 카테고리든 Craigslist URL 만들기

도시 + 카테고리 슬러그 + 선택 필터로 목표 URL을 동적으로 만들어요.

from urllib.parse import urlencode

BASE = "https://{city}.craigslist.org/search/{slug}"

def build_url(city, slug, **params):
    return f"{BASE.format(city=city, slug=slug)}?{urlencode(params)}"

# 예시: 뉴욕 아파트, $1500~$3000, 사진 포함
url = build_url("newyork", "apa", min_price=1500, max_price=3000, hasPic=1)
print(url)
# https://newyork.craigslist.org/search/apa?min_price=1500&max_price=3000&hasPic=1

"apa""cta"(자동차), "jjj"(채용), "bbb"(서비스) 등 위 표의 다른 슬러그로 바꾸면 돼요. "newyork""sfbay", "chicago", "losangeles" 등으로 바꿀 수 있고요.

3단계: 페이지를 가져와 내장 JSON 추출하기

적절한 헤더와 함께 GET 요청을 보내고, JSON-LD 블록을 파싱해요.

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/124.0.0.0 Safari/537.36"
    ),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Referer": "https://www.craigslist.org/",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "same-origin",
    "Upgrade-Insecure-Requests": "1",
}

session = requests.Session()
r = session.get(url, headers=HEADERS, timeout=20)
r.raise_for_status()

soup = BeautifulSoup(r.text, "html.parser")
tag = soup.select_one("script#ld_searchpage_results")
data = json.loads(tag.text) if tag else {"itemListElement": []}

tagNone이면 그 카테고리엔 JSON-LD 블록이 없는 거예요. 그럴 땐 HTML 파싱으로 전환하세요(위 셀렉터 표 참고). 아파트, 자동차, 판매 카테고리는 대체로 JSON-LD 블록이 안정적으로 있어요.

4단계: 매물 데이터를 구조화된 레코드로 파싱하기

JSON 항목을 순회하면서 필요한 필드를 뽑아요.

listings = []
for entry in data["itemListElement"]:
    item = entry["item"]
    offers = item.get("offers", {}) or {}
    addr = (offers.get("availableAtOrFrom") or {}).get("address", {})
    listings.append({
        "name":     item.get("name"),
        "url":      offers.get("url"),
        "price":    offers.get("price"),
        "currency": offers.get("priceCurrency"),
        "locality": addr.get("addressLocality"),
        "region":   addr.get("addressRegion"),
        "image":    item.get("image"),
    })

print(f"{len(listings)}개의 매물을 찾았습니다")

예를 들어 「Found 120 listings」 같은 결과가 나와야 해요(Craigslist는 페이지당 120개를 보여줘요). 일부 매물은 게시자가 가격을 안 넣었을 수 있으니, 후속 로직에서 None 값을 자연스럽게 처리하세요.

5단계: 상세 페이지를 스크래핑해 더 풍부한 데이터 얻기

검색 결과엔 요약 정보만 나와요. 전체 설명, 속성 정보(침실 수, 면적, 반려동물 정책), 위도/경도, 이미지까지 얻으려면 매물별 상세 URL로 들어가야 해요.

def fetch_detail(url, session):
    r = session.get(url, headers=HEADERS, timeout=20)
    r.raise_for_status()
    s = BeautifulSoup(r.text, "html.parser")
    body = s.select_one("#postingbody")
    mp = s.select_one("#map")
    return {
        "description": body.get_text("\n", strip=True) if body else None,
        "attributes":  [x.get_text(" ", strip=True) 
                        for x in s.select("p.attrgroup span, div.attrgroup .attr")],
        "lat": mp.get("data-latitude") if mp else None,
        "lng": mp.get("data-longitude") if mp else None,
        "images": [img["src"] for img in s.select("div.gallery img")],
    }

for item in listings:
    item.update(fetch_detail(item["url"], session))
    time.sleep(random.uniform(3, 6))  # 중요: 안티봇용 지터

time.sleep(random.uniform(3, 6))는 선택이 아니에요. 이걸 빼면 수십 번 요청 안에 403을 맞기 쉬워요. 상세 페이지는 #titletextonly, #postingbody, #map 같은 안정적인 셀렉터를 쓰는데, Craigslist에서 드물게 믿을 만한 부분이에요.

6단계: 페이지네이션으로 모든 결과 스크래핑하기

Craigslist는 페이지네이션에 ?s=120 오프셋 파라미터를 써요. 페이지당 120개가 나오고, 최대 오프셋은 보통 2999예요.

def iter_all(city, slug, max_pages=25, **filters):
    for page in range(max_pages):
        offset = page * 120
        url = build_url(city, slug, s=offset, **filters)
        r = session.get(url, headers=HEADERS, timeout=20)
        r.raise_for_status()
        soup = BeautifulSoup(r.text, "html.parser")
        tag = soup.select_one("script#ld_searchpage_results")
        if not tag:
            break
        data = json.loads(tag.text)
        items = data.get("itemListElement", [])
        if not items:
            break
        for entry in items:
            item = entry["item"]
            offers = item.get("offers", {}) or {}
            yield {
                "name": item.get("name"),
                "url": offers.get("url"),
                "price": offers.get("price"),
            }
        time.sleep(random.uniform(2.5, 5.0))

수천 페이지를 짧은 시간에 몰아서 긁으려 하지 마세요. Craigslist 레이트 리미터는 IP 단위로 돌아가고, 어떤 라이브러리를 쓰든 단일 IP의 지속 가능한 처리량은 대략 초당 0.3~0.5 요청이에요. 이 한계는 Python이 아니라 Craigslist가 정한 거예요.

7단계: Craigslist 데이터를 CSV, JSON 또는 Google Sheets로 내보내기

결과를 저장해요.

# CSV
with open("craigslist.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=listings[0].keys())
    w.writeheader()
    w.writerows(listings)

# JSON
with open("craigslist.json", "w", encoding="utf-8") as f:
    json.dump(listings, f, indent=2, ensure_ascii=False)

내보내기 코드 자체를 안 쓰고 싶다면, Thunderbit는 브라우저에서 Google Sheets, Excel, Airtable, Notion으로 무료 내보내기를 지원해요. 다만 Python 파이프라인에선 CSV와 JSON이 표준 출력 형식이에요. pandas로 바로 넘겨 분석하거나 sqlite3로 데이터베이스에 저장할 수도 있고요.

Python으로 Craigslist를 스크래핑할 때 차단을 피하는 방법

대부분 튜토리얼은 이 부분을 대충 넘겨요. 하지만 Craigslist 안티봇은 자체 제작이라 몇 가지 독특한 특징이 있어요.

craigslist_antibot_ae9ddc3f54.png

현실적인 요청 헤더를 사용하기

Craigslist는 헤더의 순서와 완성도를 검사해요. Sec-Fetch-Dest가 빠졌거나 오래된 User-Agent를 쓰면, 콘텐츠에 닿기도 전에 막힐 수 있어요. 위 3단계의 Chrome 120+ 헤더 세트가 최소 기준이에요. 세션마다 최근 Chrome/Firefox 데스크톱 UA 5~10개 사이에서 돌려 쓰되, 세션 도중엔 바꾸지 마세요. 그러면 오히려 부자연스러워 보여요.

Sec-Fetch-* 헤더 누락이 처음 스크래퍼를 만들 때 곧장 차단당하는 가장 흔한 이유예요.

요청 사이에 랜덤 지연 추가하기

여러 출처에서 의견이 모이는 커뮤니티 권장치는 검색 결과 페이지 간 랜덤 2~5초, 상세 페이지 간 3~6초예요. 일정한 간격은 봇처럼 보여요. time.sleep(2)가 아니라 time.sleep(random.uniform(2, 5))를 쓰세요.

프록시 로테이션 사용하기(대규모 스크래핑일 때)

Craigslist는 AWS, GCP, Azure 전체 IP 대역을 선차단하는 경우가 많아요. 데이터센터 프록시는 시작하자마자 막히기 일쑤죠. 수백 페이지 넘게 긁으려면 주거용 회전 프록시가 필요하고, 2030 요청마다 바꿔야 해요. 모바일 프록시는 탐지 위험이 가장 낮지만 GB당 $830 수준으로 비싸요.

프록시 유형Craigslist에서의 탐지 위험비용(2025)
데이터센터매우 높음 — 첫 요청부터 막히는 경우가 많음$0.50~2/GB
주거용 회전낮음 — 권장$5~15/GB
모바일가장 낮음$8~30/GB

Thunderbit의 Cloud Scraping 모드를 쓰면 프록시 로테이션을 자동 처리하니, 직접 관리하기 싫다면 이 방법도 좋아요.

CAPTCHA는 부드럽게 처리하기

Craigslist 읽기 경로에서 CAPTCHA는 드문 편이고, 주로 게시/응답 흐름에서 나와요. 만약 떴다면 최소 60초 이상 멈추고, IP를 바꾸고, 쿠키를 지우고, 속도를 낮추세요. CAPTCHA가 계속 보인다면 솔버로 억지로 푸는 문제가 아니라, 요청 속도가 너무 빠르다는 신호예요.

레이트 리밋을 존중하고 백오프 구현하기

Craigslist는 레이트 리밋에 닿으면 429가 아니라 403을 돌려줘요. 403은 현재 IP가 거부 목록에 올랐다는 뜻이니, 무작정 재시도하면 안 돼요. IP를 바꾸고, UA를 바꾸고, 기다리세요.

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(
    total=5,
    backoff_factor=1.5,  # 1.5, 3, 6, 12, 24초
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods={"GET"},
    respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("https://", adapter)

팁 하나 더. 커뮤니티 보고서들에 따르면 대상 도시 현지 시간 기준 새벽 2~6시가 가장 안전한 스크래핑 시간대이고, 낮보다 차단율이 약 30~40% 낮아요.

숨은 함정, TLS 핑거프린팅

Craigslist 봇 레이어는 TLS ClientHello를 검사해요. Python requests(OpenSSL 기반)는 실제 브라우저와 안 맞는 JA3 지문을 남겨요. 완벽한 User-Agent 헤더에 브라우저가 아닌 TLS 지문이 섞이면, 그 불일치 자체가 잡힐 수 있어요. 해법은 impersonate="chrome124"를 지원하는 curl_cffi예요. Chrome의 TLS 핸드셰이크를 흉내 내거든요.

from curl_cffi import requests as cffi_requests

r = cffi_requests.get(url, headers=HEADERS, impersonate="chrome124")

주거용 IP를 쓰고 헤더도 제대로 넣었는데 설명 안 되는 403이 계속 난다면, TLS 지문이 원인일 가능성이 아주 높아요.

Craigslist robots.txt, 이용약관, 그리고 윤리적 스크래핑

대부분 가이드는 이 부분을 완전히 건너뛰거나 FAQ에 한 줄만 넣어요. 하지만 Craigslist는 스크래퍼 RadPad를 상대로 $6,050만 판결을 받아낸 적이 있어서, 가볍게 볼 문제가 아니에요.

Craigslist robots.txt가 실제로 말하는 것

robots.txt 파일은 놀랄 만큼 짧아요. User-agent: * 블록 하나에, 금지 경로는 단 7개예요.

Disallow: /reply
Disallow: /fb/
Disallow: /suggest
Disallow: /flag
Disallow: /mf
Disallow: /mailflag
Disallow: /eaf

이 7개는 전부 상호작용/변경용 엔드포인트예요. reply, flag, suggest, email-a-friend 같은 기능이죠. 매물 페이지(/search/..., 개별 게시물 URL)는 금지되지 않았어요. Crawl-delay 지시문도 없지만, Craigslist는 IP 차단으로 사실상 이를 강제해요.

도시 서브도메인에는 사이트맵도 올라와 있어요. 예를 들어 https://newyork.craigslist.org/sitemap/index.xml는 매물 페이지를 공식적으로 발견할 수 있는 경로예요.

중요한 법적 선례

Craigslist v. 3Taps (2013, 2015년 합의): 3Taps는 Craigslist 매물을 긁어 재판매했어요. Craigslist가 중지 요청을 보내고 IP를 차단하자, 3Taps는 회전 프록시로 차단을 우회했고요. 법원은 명시적 철회 이후 IP 차단을 우회하는 행위가 CFAA상 「without authorization」에 해당한다고 봤어요. 3Taps는 100만 달러에 합의했어요.

Meta v. Bright Data (2024): 더 최근 판결에서는 Meta 이용약관이 로그아웃 상태의 공개 데이터 스크래핑을 막을 수 없다고 봤어요. 법원은 로그아웃 상태 스크래퍼를 「방문자와 같은 지위」로 판단했고요. 2024~2025년 스크래퍼에게 가장 중요한 판결이에요. Craigslist 계정을 안 만들고, 로그인하지 않고, 공개로 보이는 페이지만 본다면, 이용약관을 계약 위반으로 적용하기 어려울 수 있어요.

실무상 요점: Van Buren(2021)과 hiQ v. LinkedIn(2022) 이후, 공개 접근 가능한 페이지에 대해선 CFAA 리스크가 상당히 줄었어요. 하지만 주법상 불법행위 청구(trespass-to-chattels, misappropriation)는 여전히 살아 있어요. 3Taps 합의와 $6,050만 RadPad 판결도 바로 이 지점에서 나왔고요.

이 내용은 법률 자문이 아니라 정보 제공 목적이에요. Craigslist를 상업적으로 긁을 거라면 변호사와 상담하세요.

실무용 윤리적 스크래핑 체크리스트

  • ✅ robots.txt의 모든 Disallow 지키기 — 특히 7개 액션 엔드포인트
  • ✅ IP당 24시간에 1,000페이지를 넘기지 않기(Craigslist 이용약관은 그 이상에 대해 페이지당 $0.25의 손해배상액을 명시)
  • ✅ 로그아웃 상태 유지 — 스크래핑용으로 Craigslist 계정 만들지 않기
  • ✅ 명시적 차단 이후 프록시로 IP 금지 우회하지 않기(3Taps가 이 때문에 무너졌어요)
  • ✅ 요청 사이에 지연 추가 — 최소 2~5초
  • ✅ 스팸 목적으로 개인 연락처 정보 수집하지 않기
  • ✅ 원본 Craigslist 데이터를 재배포하거나 내 플랫폼인 척하지 않기
  • ✅ 합법적인 리서치, 분석, 개인 용도로만 쓰기
  • ✅ 가능하면 무작정 크롤링보다 공개 사이트맵 먼저 활용하기
  • ✅ 저장 전에 PII(이메일, 전화번호) 제거하기

웹 스크래핑의 법적 의미를 더 깊이 다룬 가이드도 있어요.

Python vs. 노코드: 어떤 방법이 나에게 맞을까?

비교 항목Python (requests + BS4)Thunderbit (노코드)
준비 시간30~60분(설치, 코드 작성)2분(Chrome 확장 설치)
필요한 기술 수준중급 Python없음
커스터마이징로직, 필드, 흐름을 완전히 제어 가능AI가 필드를 자동 감지, 사용자가 조정 가능
규모 확장무제한(프록시, 스케줄링 활용)반복 작업용 Scheduled Scraper
차단 대응수동 설정(헤더, 지연, 프록시, TLS)내장(Cloud Scraping)
내보내기 옵션CSV, JSON(직접 구현)Google Sheets, Excel, Airtable, Notion — 무료
적합한 사용자개발자, 데이터 과학자, 커스텀 파이프라인영업팀, 부동산 중개인, 운영 관리자

Python은 완전한 커스터마이징이 필요하거나, 더 큰 데이터 파이프라인과 엮을 계획이거나, 내부 동작을 정확히 이해하고 싶을 때 맞아요. 반대로 코드를 짜거나 관리하지 않고 빠르게 결과만 필요하다면 Thunderbit이 낫고요. 둘 다 유효한 선택이에요. 결국 사용 사례와, 터미널에서 시간을 보낼지 브라우저에서 보낼지에 달려 있어요.

마무리

Craigslist는 주거, 자동차, 채용, 서비스, 일감 등 여러 분야를 아우르는 풍부하고 끊임없이 갱신되는 데이터 소스예요. 공개 API가 없으니 구조화 데이터를 대규모로 얻는 길은 결국 스크래핑뿐이고요. 2025년 기준 실제로 잘 되는 방법은 검색 결과에서 내장 JSON-LD를 추출하고(깨지기 쉬운 CSS 셀렉터 대신), Selenium이 아니라 requests + BeautifulSoup을 쓰고, Sec-Fetch-*를 포함한 현실적 헤더를 넣고, 지연을 랜덤화하고, 수백 페이지 넘게 긁을 때만 주거용 프록시를 쓰는 거예요.

JSON-LD 방식이 옛 가이드 대비 가장 큰 개선점이에요. 더 빠르고, 레이아웃 변경에 강하고, JavaScript 렌더링이 아예 필요 없어요. 위 안티차단 전략과 함께 쓰면 대부분 스크래퍼가 겪는 403 문제를 피할 수 있어요.

코드를 아예 건너뛰고 싶다면, Thunderbit Chrome Extension으로 어떤 Craigslist 카테고리든 몇 번 클릭에 긁고 원하는 스프레드시트나 데이터베이스로 바로 내보낼 수 있어요. 더 깊이 배우고 싶다면 Python으로 웹 스크래핑하는 방법웹 스크래핑 모범 사례 가이드를 참고하세요.

Craigslist 스크래핑용 Thunderbit 사용해 보기

Craigslist 데이터용 AI 웹 스크래퍼 사용해 보기 Get Started Free

자주 묻는 질문

Craigslist를 스크래핑하는 건 합법인가요?

Craigslist 이용약관은 자동 스크래핑을 금지하고, 일일 1,000페이지를 넘기면 페이지당 $0.25의 손해배상 조항이 붙어요. 다만 최근 판결, 특히 Meta v. Bright Data(2024)와 hiQ v. LinkedIn(2022)은 로그아웃 상태에서 공개 데이터를 긁는 행위에 대한 CFAA 책임 범위를 좁혔어요. 주법상 불법행위 청구(trespass-to-chattels)는 여전히 위험 요소이고, 특히 상업적 재배포라면 더 그래요. robots.txt를 지키고, 로그아웃 상태를 유지하고, 지연을 두고, 원본 데이터를 재배포하지 마세요. 이는 일반 정보이며 법률 자문이 아니에요.

Craigslist에 공개 API가 있나요?

아니요. Craigslist는 승인된 유료 게시자를 위한 쓰기 전용 Bulk Posting Interface(BAPI)만 제공해요. 공개 읽기 API도, 개발자 포털도, 데이터 조회용 레이트 리미트 계층도 없어요. 외부 플랫폼에서 보이는 「Craigslist API」는 다 비공식 스크래퍼예요.

왜 Craigslist 스크래퍼가 계속 깨지나요?

대부분 HTML 구조 변경 때문이에요. Craigslist는 2023~2024년에 검색 결과 마크업을 다시 썼고, .result-row.result-info 같은 옛 셀렉터를 쓰는 가이드는 더 이상 안 돌아가요. 훨씬 견고한 방법은 내장 JSON-LD를 파싱하는 방식(script#ld_searchpage_results)이에요. 또 헤더에 Sec-Fetch-* 필드가 들어 있는지 확인하세요. 이게 빠지면 즉시 막힐 수 있어요.

Python 없이 Craigslist를 스크래핑할 수 있나요?

네. Thunderbit의 AI 웹 스크래퍼 Chrome 확장 프로그램은 아파트, 자동차, 채용, 서비스 등 어떤 Craigslist 페이지에서도 돌아가요. 「AI Suggest Fields」를 눌러 열을 자동 감지하고, 「Scrape」로 데이터를 뽑은 뒤, Google Sheets, Excel, Airtable, Notion으로 무료 내보내기 하면 돼요. 코딩도, 설정도, 프록시 관리도 필요 없어요.

차단당하지 않고 얼마나 자주 Craigslist를 스크래핑할 수 있나요?

단일 주거용 IP 기준, 페이지 사이에 랜덤 25초 지연을 두면 지속 가능한 처리량이 대략 초당 0.30.5 요청이에요. 차단과 Craigslist 이용약관의 손해배상 기준을 피하려면 IP당 24시간에 1,000페이지를 넘기지 마세요. 대상 도시 현지 시간 기준 새벽 26시처럼 비혼잡 시간대에 긁으면 차단율이 약 3040% 낮아요. 더 많은 양이 필요하면 20~30 요청마다 주거용 프록시를 교체하세요.

더 알아보기

Ke
Ke
Thunderbit CTO | 시니어 데이터 사이언티스트 & ML 전문가 머신러닝과 데이터 과학 분야에서 약 10년에 가까운 경험을 쌓아온 Ke Shen은 컬럼비아 대학교 출신이며, 전 Walmart Labs의 시니어 데이터 사이언티스트였습니다. Python, R, Java, 통계 분야에서 동료들에게도 인정받는 깊은 전문성을 바탕으로, 복잡한 AI 알고리즘을 이론에서 실제 운영 수준의 아키텍처로 전환하는 데 필요한 실전 인사이트를 공유합니다.
목차

말 한마디로 웹페이지를 스크래핑하세요

원하는 걸 평범한 영어로 말하면 됩니다. 아니면 아무 말도 안 해도 괜찮아요.

Thunderbit 사용해보기 무료
AI로 데이터 추출하기
Google Sheets, Airtable, Notion으로 데이터를 손쉽게 منتقل하세요
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week