Craigslist hiện vẫn thu hút khoảng 108 triệu lượt truy cập mỗi tháng trên khoảng 700 website địa phương — nhưng vẫn chưa có API công khai. Nếu bạn muốn lấy dữ liệu có cấu trúc từ tin thuê căn hộ, xe cũ, bài đăng việc làm hay tin rao dịch vụ, thì scraping gần như là lựa chọn duy nhất.
Tuy nhiên, hệ thống chống bot tự phát triển của Craigslist khá gắt. Nó không dùng Cloudflare hay DataDome — mà vận hành bộ giới hạn tốc độ riêng dựa trên nginx, được tinh chỉnh suốt hơn một thập kỷ. Chạm sai một phát là bạn sẽ ăn ngay lỗi 403 trước khi kịp uống xong ly cà phê buổi sáng. Tôi đã dành rất nhiều thời gian thử nghiệm các cách tiếp cận khác nhau để vượt qua cơ chế bảo vệ của Craigslist, và bài viết này là kết quả: một hướng dẫn Python cập nhật đến năm 2025, áp dụng cho mọi danh mục, có phần trích xuất JSON-LD (cải tiến lớn nhất so với các hướng dẫn cũ), chiến lược chống chặn thực tế, góc nhìn pháp lý, và cả một lựa chọn không cần code cho ai chỉ muốn lấy dữ liệu mà không muốn viết một dòng lệnh nào.
Scrape Craigslist bằng Python nghĩa là gì?
Web scraping Craigslist là dùng script Python để truy cập các trang Craigslist theo cách tự động, trích xuất dữ liệu có cấu trúc mà bạn cần — tiêu đề, giá, mô tả, hình ảnh, vị trí, ngày đăng — rồi lưu vào bảng tính, cơ sở dữ liệu hoặc file JSON.
Python là ngôn ngữ được chọn nhiều nhất cho việc này nhờ hệ sinh thái thư viện rất mạnh. Với requests, BeautifulSoup, lxml và curl_cffi, bạn có thể xây dựng một scraper Craigslist hoạt động được chỉ trong chưa đến 100 dòng code. Cộng đồng Python cũng rất lớn, nên khi Craigslist thay đổi gì đó (và họ có thay đổi thật), thường đã có người tìm ra cách xử lý.
Điều quan trọng cần biết: Craigslist không cung cấp public read API. Giao diện lập trình chính thức duy nhất của họ là Bulk Posting Interface (BAPI), nhưng nó chỉ cho phép ghi dữ liệu — tức là những tài khoản đăng bài được phê duyệt và trả phí có thể gửi tin, chứ không thể lấy dữ liệu ra. Bất kỳ sản phẩm “Craigslist API” nào bạn thấy trên nền tảng bên thứ ba đều là scraper không chính thức, không phải endpoint được Craigslist công nhận. Nếu bạn muốn dữ liệu hàng loạt, bạn phải scrape.
Vì sao nên scrape Craigslist? Các ứng dụng thực tế
Craigslist không chỉ là nơi tìm ghế sofa cũ. Đây là một tập dữ liệu khổng lồ, liên tục được cập nhật trên hàng chục nhóm nội dung. Đây là những nhóm người thực sự hưởng lợi từ việc scrape nó:
| Ứng dụng | Ai hưởng lợi | Bạn trích xuất gì |
|---|---|---|
| Theo dõi giá căn hộ & nhà thuê | Môi giới bất động sản, người thuê, công ty PropTech | Giá, diện tích, số phòng ngủ, khu vực, tọa độ |
| Phân tích thị trường xe cũ | Đại lý xe, ứng dụng tiêu dùng, nhà nghiên cứu | Giá, hãng, mẫu xe, năm, số km, tình trạng |
| Nghiên cứu thị trường việc làm | Nhà tuyển dụng, nhà kinh tế lao động, chuyên gia nhân sự | Tiêu đề, mức lương, loại hình việc làm, ngày đăng |
| Tạo lead | Đội sales, nhà cung cấp dịch vụ | Thông tin liên hệ, tên doanh nghiệp, khu vực phục vụ |
| So sánh giá cạnh tranh | Nhà cung cấp dịch vụ địa phương, vận hành ecommerce | Bảng giá dịch vụ, mô tả, khu vực phục vụ |
Ví dụ học thuật được nhắc đến nhiều nhất là bộ dữ liệu Kaggle "Used Cars Dataset" — khoảng 500.000 tin xe cũ tại Mỹ với 26 biến, và đây đã là nền tảng cho hàng chục nghiên cứu, bao gồm cả một nghiên cứu trên ResearchGate năm 2024 về động lực thị trường xe cũ ở Mỹ. Các quỹ hedge fund còn mua dữ liệu thuê nhà tổng hợp từ Craigslist để nghiên cứu xu hướng giá thuê. Và các đội sales thì thường xuyên scrape mục services và gigs để tạo lead.
Bài toán rất đơn giản: 8 giờ copy-paste thủ công so với khoảng 10 phút bằng một scraper được xây dựng tốt.

Scrape Craigslist bằng Python: tất cả danh mục, không chỉ xe
Hầu hết các hướng dẫn scrape Craigslist tôi thấy chỉ nói về cars-for-sale — giống như viết hướng dẫn Google mà chỉ đề cập đến tìm kiếm hình ảnh. Craigslist có hàng chục danh mục, và mỗi danh mục lại có cấu trúc URL khác nhau.
Cấu trúc luôn là: https://{city}.craigslist.org/search/{category_slug}
Chỉ cần đổi subdomain của thành phố và slug, bạn sẽ scrape sang một mảng nội dung hoàn toàn khác. Đây là bảng tham chiếu các danh mục phổ biến nhất (đã xác minh vào tháng 4/2025):
| Danh mục | URL Slug | Các trường thường trích xuất |
|---|---|---|
| Căn hộ / nhà ở | /search/apa | Giá, diện tích, số phòng ngủ, vị trí, quy định thú cưng |
| Ô tô & xe tải | /search/cta | Giá, hãng, mẫu, năm, số km |
| Việc làm | /search/jjj | Tiêu đề, công ty, lương, loại hình việc làm |
| Dịch vụ | /search/bbb | Tiêu đề, mô tả, số điện thoại, khu vực |
| Gigs | /search/ggg | Tiêu đề, mức thù lao, ngày, danh mục |
| Đồ rao bán (chung) | /search/sss | Tiêu đề, giá, tình trạng, vị trí |
Bạn cũng có thể kết hợp tham số truy vấn để lọc dữ liệu:
| Tham số | Mục đích | Ví dụ |
|---|---|---|
query | Từ khóa tìm kiếm toàn văn | ?query=studio |
min_price / max_price | Khoảng giá | &min_price=1500&max_price=3000 |
hasPic | Chỉ hiển thị bài có hình ảnh | &hasPic=1 |
postedToday | Trong 24 giờ gần nhất | &postedToday=1 |
sort | Cách sắp xếp | &sort=priceasc |
s | Offset phân trang (120 kết quả mỗi trang) | ?s=120 |
Vì vậy, một URL như https://newyork.craigslist.org/search/apa?min_price=1500&max_price=3000&hasPic=1 sẽ cho bạn danh sách căn hộ ở New York từ 1.500 đến 3.000 USD kèm ảnh. Mọi scraper Python trong bài này đều áp dụng được cho các danh mục đó — bạn chỉ cần đổi slug.
CSS selector của Craigslist năm 2025: cũ vs. mới (và lối tắt JSON)
Lý do số một khiến scraper Craigslist bị hỏng là vì HTML thay đổi. Nếu bạn đang làm theo một bài hướng dẫn từ năm 2022 bảo target .result-row hay .result-info, thì scraper của bạn coi như đã “chết” rồi.
Craigslist đã viết lại markup của trang kết quả tìm kiếm vào giai đoạn 2023–2024. Các class cũ vẫn tồn tại nhưng nằm sâu trong wrapper mới; nếu target trực tiếp từ đầu cây DOM thì bạn sẽ nhận về danh sách rỗng. Đây là những gì đã thay đổi:
| Phần tử | Selector cũ (trước 2024) | Selector hiện tại (2025) |
|---|---|---|
| Khung tin | .result-info | .cl-search-result |
| Link tiêu đề | .result-title | .posting-title a |
| Giá | .result-price | .priceinfo |
| Metadata (khu vực) | .result-hood | .meta |
Nhưng đây mới là điểm mấu chốt — và cũng là thứ phân biệt một scraper cập nhật năm 2025 với phần còn lại: bạn không cần parse HTML ở trang kết quả tìm kiếm.
Craigslist giờ nhúng toàn bộ danh sách hiển thị vào thẻ <script id="ld_searchpage_results"> dưới dạng dữ liệu JSON-LD có cấu trúc. Chỉ một lệnh requests.get() là đã lấy được schema.org ItemList đầy đủ của cả trang — tiêu đề, giá, tiền tệ, vị trí, URL ảnh, link chi tiết. Không cần render JavaScript. Không sợ selector CSS bị đổi.
Cách làm với JSON-LD nhanh hơn, ổn định hơn, và ít bị hỏng khi Craigslist chỉnh giao diện. Đây cũng là phương pháp mà các repo GitHub còn được bảo trì tích cực đang dùng, và là cách tôi sẽ dùng trong phần hướng dẫn bên dưới.
Một lưu ý: khối JSON-LD này thường có ở các danh mục có giá — căn hộ (apa), đồ rao bán (sss), xe (cta), nhà ở (hhh). Nó thường không có hoặc rất sơ sài ở jobs (jjj), gigs (ggg), community (ccc) và services (bbb) vì các tin này không có schema.org/Offer pricing. Với các danh mục đó, hãy quay về đường HTML bằng .cl-search-result.
Chọn stack Python: Requests + BS4, Selenium hay Playwright?
Đây là câu hỏi xuất hiện ở mọi diễn đàn scraping: “Nên dùng thư viện nào?” Với Craigslist, câu trả lời rõ ràng hơn nhiều website khác.
| Tiêu chí | requests + BeautifulSoup | Selenium | Playwright |
|---|---|---|---|
| Tốc độ | 5–15 trang/giây (bị giới hạn bởi mạng) | 0.3–1 trang/giây | 0.5–2 trang/giây |
| Nội dung render bằng JS | Không | Có | Có |
| Bộ nhớ | ~30–60 MB | ~400–700 MB | ~300–500 MB |
| Độ phức tạp thiết lập | Thấp | Trung bình | Trung bình |
| Khả năng chống bot | Thấp (cần headers/proxy) | Trung bình (trình duyệt thật) | Trung bình-cao |
| Trường hợp dùng tốt nhất cho Craigslist | Kết quả tìm kiếm (JSON-LD) | Trang chi tiết có nội dung động | Scraping async quy mô lớn |
| Độ dễ học | Dễ cho người mới | Trung bình | Trung bình |
Trang Craigslist được render phía server. Khối JSON-LD nằm ngay trong HTML ban đầu. Không có thử thách JavaScript nào ở luồng đọc dữ liệu. Mọi Craigslist scraper trên GitHub còn duy trì đều dùng requests + BeautifulSoup hoặc Scrapy. Không có Selenium hay Playwright. Điều đó không phải ngẫu nhiên — automation bằng trình duyệt sẽ ngốn thêm hàng trăm MB RAM, chậm hơn 10–100 lần, và để lại dấu vết dễ bị phát hiện hơn mà chẳng mang lại lợi ích gì.
Khuyến nghị của tôi:
- requests + BS4: Hãy bắt đầu từ đây. Nó khớp hoàn hảo với cách trích JSON-LD và xử lý được 95% nhu cầu scrape Craigslist.
- Selenium: Chỉ dùng nếu bạn cần tương tác với nội dung động trên một số trang chi tiết cụ thể (trường hợp này hiếm trên Craigslist).
- Playwright: Dùng khi bạn phải mở rộng lên hàng nghìn trang với concurrency async — nhưng thực ra, nút thắt của Craigslist là rate limiter, không phải tốc độ thư viện.
Nếu muốn xem phân tích đầy đủ, chúng tôi đã có bài so sánh Playwright vs. Puppeteer và bài tổng hợp các công cụ web scraping Python tốt nhất.
Scrape Craigslist mà không cần viết Python Get Started Free
Giải pháp không cần code: scrape Craigslist mà không viết Python
Trước khi vào phần code, mình rẽ nhẹ một chút — phần này dành cho những ai không phải developer. Môi giới bất động sản, đội sales, quản lý vận hành — nếu bạn chỉ cần dữ liệu chứ không muốn viết Python, thì có một cách nhanh hơn.
Thunderbit là một AI web scraper hoạt động dưới dạng tiện ích Chrome extension. Nó có thể scrape Craigslist chỉ trong khoảng 2 cú click, không cần code. Quy trình như sau:
- Mở bất kỳ trang kết quả tìm kiếm nào của Craigslist (căn hộ, xe, việc làm — bất kỳ danh mục nào).
- Bấm "AI Suggest Fields" trên thanh bên của Thunderbit. AI sẽ đọc trang và tự nhận diện các cột như tiêu đề tin, giá, vị trí và link.
- Bấm "Scrape" — dữ liệu sẽ được trích ra trong vài giây.
- Dùng Subpage Scraping để vào từng trang chi tiết của tin và làm giàu dữ liệu với mô tả đầy đủ, số điện thoại, hình ảnh và thuộc tính.
- Xuất thẳng sang Google Sheets, Excel, Airtable hoặc Notion — hoàn toàn miễn phí.
Nếu nhu cầu của bạn lặp lại đều đặn — ví dụ theo dõi giá căn hộ mỗi ngày hoặc chụp snapshot tin việc làm mỗi tuần — thì Scheduled Scraper của Thunderbit cho phép bạn mô tả lịch chạy bằng ngôn ngữ tự nhiên và hệ thống sẽ tự động chạy. Không cần cron job, không cần dựng server.
Thunderbit cũng xử lý các biện pháp chống bot bằng chế độ Cloud Scraping, nên bạn không phải lo xoay proxy hay tự tạo header. Nếu muốn thử, hãy cài Thunderbit Chrome Extension và tự trải nghiệm.
Nếu bạn muốn kiểm soát và tùy biến hoàn toàn, hãy tiếp tục xem phần hướng dẫn Python từng bước bên dưới.
Từng bước: Cách scrape Craigslist bằng Python (hướng dẫn đầy đủ)
- Độ khó: Trung bình
- Thời gian cần thiết: ~30 phút (thiết lập + scrape lần đầu)
- Bạn cần: Python 3.8+, trình duyệt Chrome (để kiểm tra trang), một terminal
Bước 1: Thiết lập môi trường Python
Cài các thư viện cần thiết:
pip install requests beautifulsoup4 lxml
lxml là tùy chọn nhưng giúp BeautifulSoup parse nhanh hơn đáng kể. Nếu sau này bạn gặp vấn đề TLS fingerprinting (mình sẽ nói kỹ hơn ở phần chống chặn), bạn cũng có thể cài curl_cffi:
pip install curl_cffi
Phần import của bạn:
import requests
from bs4 import BeautifulSoup
import json
import csv
import time
import random
Giờ bạn đã có một môi trường Python sạch với đầy đủ dependencies.
Bước 2: Tạo URL Craigslist cho mọi danh mục
Tạo URL mục tiêu động bằng city + category slug + bộ lọc tùy chọn:
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)}"
# Ví dụ: căn hộ ở New York, giá 1500-3000, có ảnh
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
Đổi "apa" thành "cta" (xe), "jjj" (việc làm), "bbb" (dịch vụ), hoặc bất kỳ slug nào từ bảng danh mục ở trên. Đổi "newyork" thành "sfbay", "chicago", "losangeles", v.v.
Bước 3: Gửi request và trích JSON nhúng
Gửi GET request với header phù hợp, rồi parse khối 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": []}
Nếu tag là None, nghĩa là khối JSON-LD không có ở danh mục đó — hãy chuyển sang parse HTML (xem bảng selector ở trên). Với căn hộ, xe và các danh mục đồ rao bán, khối JSON-LD gần như luôn xuất hiện.
Bước 4: Parse dữ liệu tin đăng thành bản ghi có cấu trúc
Lặp qua các item JSON và trích các trường cần thiết:
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"Found {len(listings)} listings")
Bạn sẽ thấy kiểu như “Found 120 listings” (Craigslist hiển thị 120 kết quả mỗi trang). Một số tin có thể có None ở trường giá nếu người đăng không nhập giá — hãy xử lý tình huống đó mềm dẻo trong logic phía sau.
Bước 5: scrape trang chi tiết để lấy dữ liệu đầy đủ hơn
Trang kết quả chỉ cho bạn thông tin tóm tắt. Để lấy mô tả đầy đủ, thuộc tính (số phòng ngủ, diện tích, quy định thú cưng), tọa độ lat/long và hình ảnh, bạn cần vào từng URL chi tiết của tin.
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)) # cực kỳ quan trọng: jitter chống chặn
time.sleep(random.uniform(3, 6)) là không được bỏ qua. Bỏ nó đi là bạn rất dễ ăn 403 chỉ sau vài chục request. Các trang chi tiết được render phía server với selector ổn định (#titletextonly, #postingbody, #map) và hầu như không đổi từ khoảng 2017 — một trong số ít điều đáng tin cậy trên Craigslist.
Bước 6: Xử lý phân trang để scrape toàn bộ kết quả
Craigslist dùng tham số offset ?s=120 cho phân trang. Mỗi trang có 120 kết quả, và offset tối đa thường là 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))
Đừng cố scrape hàng nghìn trang liên tục trong thời gian ngắn. Rate limiter của Craigslist hoạt động theo IP, và thông lượng bền vững với một IP thường chỉ khoảng 0.3–0.5 request/giây bất kể bạn dùng thư viện nào. Giới hạn đó do Craigslist đặt ra, không phải do Python.
Bước 7: Xuất dữ liệu Craigslist sang CSV, JSON hoặc Google Sheets
Lưu kết quả của bạn:
# 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)
Nếu bạn muốn bỏ qua luôn phần code xuất file, Thunderbit hỗ trợ xuất miễn phí trực tiếp sang Google Sheets, Excel, Airtable hoặc Notion ngay từ trình duyệt. Còn trong pipeline Python, CSV và JSON vẫn là định dạng tiêu chuẩn. Bạn cũng có thể đẩy dữ liệu thẳng vào pandas để phân tích hoặc vào database bằng sqlite3.
Cách tránh bị chặn khi scrape Craigslist bằng Python
Phần lớn các bài hướng dẫn thường nói lướt qua đoạn này. Hệ thống chống bot của Craigslist là tự xây, không phải hàng mua sẵn, và nó có vài đặc điểm khá riêng.

Dùng header request thật giống người dùng thật
Craigslist kiểm tra thứ tự và độ đầy đủ của header. Một request thiếu Sec-Fetch-Dest hoặc dùng User-Agent cũ sẽ bị gắn cờ trước khi chạm vào nội dung. Bộ header đầy đủ kiểu Chrome 120+ (đã hiển thị ở Bước 3) là mức tối thiểu. Có thể xoay User-Agent theo từng session giữa 5–10 chuỗi Chrome/Firefox desktop mới — nhưng đừng đổi giữa chừng trong cùng một session, vì nhìn sẽ rất giả.
Thiếu các header Sec-Fetch-* là nguyên nhân phổ biến nhất khiến scraper mới bị chặn ngay lập tức.
Thêm độ trễ ngẫu nhiên giữa các request
Kinh nghiệm cộng đồng từ nhiều nguồn (ScrapingBee, Scraperly, Oxylabs, Multilogin) đều hội tụ về mức ngẫu nhiên 2–5 giây giữa các lần fetch trang kết quả và 3–6 giây giữa các trang chi tiết. Khoảng cách cố định trông rất “bot”. Hãy dùng time.sleep(random.uniform(2, 5)) — đừng dùng time.sleep(2).
Xoay proxy nếu scrape ở quy mô lớn
Craigslist chặn sẵn cả dải IP từ AWS, GCP và Azure. Proxy datacenter thường chết ngay từ request đầu tiên. Nếu vượt quá vài trăm trang, bạn sẽ cần residential rotating proxy, xoay mỗi 20–30 request. Proxy mobile có rủi ro bị phát hiện thấp nhất nhưng giá có thể từ $8–30/GB.
| Loại proxy | Rủi ro bị phát hiện trên Craigslist | Chi phí (2025) |
|---|---|---|
| Datacenter | Rất cao — thường bị chặn ngay request đầu | $0.50–2/GB |
| Residential rotating | Thấp — được khuyến nghị | $5–15/GB |
| Mobile | Thấp nhất | $8–30/GB |
Chế độ Cloud Scraping của Thunderbit sẽ tự xử lý xoay proxy nếu bạn không muốn tự quản lý phần này.
Xử lý CAPTCHA một cách mềm dẻo
CAPTCHA trên Craigslist khá hiếm ở luồng đọc dữ liệu — chúng thường xuất hiện ở luồng đăng bài hoặc phản hồi. Nếu gặp CAPTCHA: dừng ít nhất 60 giây, đổi IP, xóa cookie và giảm tốc độ. CAPTCHA xuất hiện liên tục là dấu hiệu cho thấy nhịp truy cập của bạn quá gắt, chứ không phải một câu đố cần brute-force bằng solver.
Tôn trọng rate limit và triển khai backoff
Craigslist trả về 403 (không phải 429) khi bạn chạm rate limit. 403 nghĩa là IP hiện tại đã vào danh sách chặn — đừng retry mù quáng. Hãy đổi IP, đổi UA và chờ.
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=5,
backoff_factor=1.5, # 1.5, 3, 6, 12, 24s
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods={"GET"},
respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("https://", adapter)
Một mẹo nữa: các báo cáo từ cộng đồng thường chỉ ra khung giờ 2–6 giờ sáng theo giờ địa phương của thành phố mục tiêu là cửa sổ scrape an toàn nhất, với tỷ lệ bị chặn thấp hơn ban ngày khoảng 30–40%.
TLS fingerprinting — cái bẫy ẩn
Lớp bot của Craigslist còn kiểm tra TLS ClientHello. Thư viện requests của Python (dựa trên OpenSSL) có JA3 fingerprint không giống bất kỳ trình duyệt thật nào. Một User-Agent hoàn hảo nhưng đi kèm TLS fingerprint không giống browser là dấu hiệu dễ bị phát hiện. Cách khắc phục là dùng curl_cffi với impersonate="chrome124", mô phỏng bắt tay TLS của Chrome:
from curl_cffi import requests as cffi_requests
r = cffi_requests.get(url, headers=HEADERS, impersonate="chrome124")
Nếu bạn vẫn bị 403 không rõ lý do dù đang dùng IP residential sạch và header đúng, thì gần như chắc chắn thủ phạm là TLS fingerprinting.
robots.txt, Điều khoản sử dụng và scraping có trách nhiệm trên Craigslist
Nhiều hướng dẫn либо bỏ qua hoàn toàn phần này, либо nhét một dòng vào FAQ. Nhưng vì Craigslist đã từng thắng một bản án trị giá $60.5 triệu trong vụ tranh chấp với một scraper (RadPad, 2017), nên nó xứng đáng được nói kỹ hơn.
robots.txt của Craigslist thực sự nói gì?
File robots.txt của Craigslist khá ngắn. Nó chỉ có một block User-agent: * với 7 đường dẫn bị cấm:
Disallow: /reply
Disallow: /fb/
Disallow: /suggest
Disallow: /flag
Disallow: /mf
Disallow: /mailflag
Disallow: /eaf
Cả 7 đều là các endpoint tương tác/thay đổi trạng thái: trả lời, gắn cờ, đề xuất, email cho bạn bè. Các trang tin đăng (/search/..., URL từng bài riêng lẻ) không bị cấm. Không có chỉ thị Crawl-delay, dù Craigslist vẫn tự áp đặt giới hạn bằng cách chặn IP.
Các subdomain theo thành phố có public sitemap — ví dụ https://newyork.craigslist.org/sitemap/index.xml — đây là đường dẫn chính thức để khám phá tin đăng.
Tiền lệ pháp lý: những vụ đáng chú ý
Craigslist v. 3Taps (2013, dàn xếp 2015): 3Taps scrape tin Craigslist rồi bán lại. Khi Craigslist gửi yêu cầu chấm dứt và chặn IP, 3Taps tìm cách đi vòng qua chặn bằng proxy xoay. Tòa án kết luận rằng việc lách chặn IP sau khi đã bị thu hồi quyền truy cập rõ ràng là “không được ủy quyền” theo CFAA. 3Taps dàn xếp ở mức 1 triệu USD.
Meta v. Bright Data (2024): Một phán quyết gần đây hơn cho rằng điều khoản sử dụng của Meta không thể cấm việc scrape dữ liệu công khai khi người dùng đã đăng xuất. Tòa cho rằng scraper đang đăng xuất “đứng ở vị trí như một người truy cập thông thường.” Đây là phán quyết quan trọng nhất cho giới scraper giai đoạn 2024–2025 — nếu bạn không tạo tài khoản Craigslist, không đăng nhập, và chỉ truy cập các trang công khai, thì điều khoản sử dụng có thể không đủ sức ràng buộc bạn theo hợp đồng.
Kết luận thực tế: Rủi ro theo CFAA giảm đáng kể sau Van Buren (2021) và hiQ v. LinkedIn (2022) đối với các trang công khai. Nhưng các khiếu nại theo luật dân sự bang (như trespass-to-chattels, misappropriation) vẫn còn — đó là nền tảng dẫn tới cả vụ dàn xếp 3Taps và bản án 60.5 triệu USD với RadPad.
Đây chỉ là thông tin tham khảo, không phải tư vấn pháp lý. Nếu bạn scrape Craigslist cho mục đích thương mại, hãy trao đổi với luật sư.
Checklist thực hành cho scraping có đạo đức
- ✅ Tôn trọng mọi mục
Disallowtrong robots.txt — đặc biệt là 7 endpoint hành động - ✅ Giữ dưới 1.000 trang trong mỗi 24 giờ cho mỗi IP (Điều khoản sử dụng của Craigslist áp mức $0.25 mỗi trang nếu vượt ngưỡng này như một khoản bồi thường ấn định)
- ✅ Luôn ở trạng thái đăng xuất — không tạo tài khoản Craigslist chỉ để scrape
- ✅ Không lách lệnh chặn IP bằng proxy sau khi đã bị chặn rõ ràng (đây là lý do 3Taps thất bại)
- ✅ Thêm độ trễ giữa các request — tối thiểu 2–5 giây
- ✅ Không scrape thông tin liên hệ cá nhân để spam
- ✅ Không phân phối lại dữ liệu thô của Craigslist hoặc biến nó thành nền tảng của riêng bạn
- ✅ Chỉ dùng dữ liệu cho mục đích nghiên cứu, phân tích hoặc sử dụng cá nhân hợp pháp
- ✅ Ưu tiên sitemap đã công bố thay vì brute-force crawl khi có thể
- ✅ Loại bỏ PII (email, số điện thoại) khi lưu dữ liệu
Chúng tôi cũng có một hướng dẫn sâu hơn về hệ quả pháp lý của web scraping nếu bạn muốn nhìn toàn cảnh hơn.
Python vs. no-code: cách nào phù hợp với bạn?
| Tiêu chí | Python (requests + BS4) | Thunderbit (No-Code) |
|---|---|---|
| Thời gian thiết lập | 30–60 phút (cài đặt, viết code) | 2 phút (cài Chrome extension) |
| Kỹ năng kỹ thuật cần có | Python mức trung bình | Không cần |
| Tùy biến | Toàn quyền kiểm soát logic, trường dữ liệu, luồng xử lý | AI tự nhận diện trường; người dùng có thể chỉnh |
| Quy mô | Không giới hạn (với proxy, lịch chạy) | Có Scheduled Scraper cho tác vụ lặp lại |
| Xử lý chống chặn | Tự làm (headers, delays, proxy, TLS) | Có sẵn (Cloud Scraping) |
| Tùy chọn xuất dữ liệu | CSV, JSON (tự code) | Google Sheets, Excel, Airtable, Notion — miễn phí |
| Phù hợp nhất cho | Developer, data scientist, pipeline tùy chỉnh | Đội sales, môi giới BĐS, quản lý vận hành |
Hãy dùng Python nếu bạn cần tùy biến sâu, muốn tích hợp vào một pipeline dữ liệu lớn hơn, hoặc muốn hiểu chính xác điều gì đang diễn ra bên trong. Hãy dùng Thunderbit nếu bạn muốn có kết quả nhanh mà không cần viết hay bảo trì code. Cả hai đều hợp lý. Tất cả phụ thuộc vào nhu cầu của bạn và việc bạn muốn dành thời gian cho terminal hay trình duyệt.
Kết luận
Craigslist là một nguồn dữ liệu phong phú, liên tục cập nhật ở các mảng nhà ở, xe cộ, việc làm, dịch vụ, gigs và nhiều hơn nữa — và vì không có API công khai, scraping là cách duy nhất để lấy dữ liệu có cấu trúc ở quy mô lớn. Cách làm hiệu quả trong năm 2025 là: trích JSON-LD nhúng từ trang kết quả tìm kiếm (thay vì selector CSS dễ gãy), dùng requests + BeautifulSoup (không phải Selenium), thêm header thật với các trường Sec-Fetch-*, random hóa độ trễ, và dùng proxy residential nếu bạn vượt quá vài trăm trang.
Phương pháp JSON-LD là cải tiến lớn nhất so với các hướng dẫn cũ. Nó nhanh hơn, bền hơn trước thay đổi giao diện, và không cần render JavaScript. Kết hợp với các chiến lược chống chặn ở trên, bạn sẽ tránh được các lỗi 403 mà phần lớn scraper hay gặp.
Nếu bạn muốn bỏ qua hoàn toàn phần code, Thunderbit Chrome Extension có thể scrape bất kỳ danh mục Craigslist nào chỉ với vài cú click và xuất thẳng sang bảng tính hoặc database bạn thích. Nếu muốn đào sâu hơn, các hướng dẫn của chúng tôi về cách web scrape bằng Python và best practices cho web scraping sẽ đi vào nền tảng chi tiết hơn.
Dùng thử Thunderbit cho việc scrape Craigslist
Dùng AI Web Scraper để lấy dữ liệu Craigslist Get Started Free
Câu hỏi thường gặp
Scrape Craigslist có hợp pháp không?
Điều khoản sử dụng của Craigslist cấm scraping tự động và có điều khoản bồi thường ấn định ($0.25/trang nếu vượt 1.000 trang/ngày). Tuy nhiên, các phán quyết gần đây — đặc biệt là Meta v. Bright Data (2024) và hiQ v. LinkedIn (2022) — đã thu hẹp trách nhiệm theo CFAA đối với việc scrape dữ liệu công khai khi đã đăng xuất. Các khiếu nại theo luật dân sự bang (trespass-to-chattels) vẫn là rủi ro, nhất là nếu bạn tái phân phối dữ liệu cho mục đích thương mại. Hãy tôn trọng robots.txt, luôn đăng xuất, thêm độ trễ và không phân phối lại dữ liệu thô. Đây là thông tin chung, không phải tư vấn pháp lý.
Craigslist có API công khai không?
Không. Craigslist chỉ cung cấp Bulk Posting Interface (BAPI) dạng chỉ-ghi cho các tài khoản đăng bài được phê duyệt và trả phí. Không có public read API, không có developer portal, và cũng không có tầng giới hạn tốc độ để lấy dữ liệu. Mọi sản phẩm “Craigslist API” trên nền tảng bên thứ ba đều là scraper không chính thức.
Vì sao scraper Craigslist của tôi cứ bị hỏng?
Gần như luôn là do HTML thay đổi. Craigslist đã viết lại markup của trang kết quả tìm kiếm trong giai đoạn 2023–2024, và các hướng dẫn dùng selector cũ như .result-row hoặc .result-info giờ không còn hoạt động. Hãy chuyển sang cách trích JSON-LD nhúng (parse script#ld_searchpage_results) để có cách làm bền hơn nhiều. Đồng thời kiểm tra xem header của bạn có bao gồm các trường Sec-Fetch-* hay không — thiếu chúng là bị chặn ngay.
Tôi có thể scrape Craigslist mà không cần Python không?
Có. Tiện ích Chrome AI web scraper của Thunderbit hoạt động trên mọi trang Craigslist — căn hộ, xe, việc làm, dịch vụ. Chỉ cần bấm "AI Suggest Fields" để tự nhận diện cột, bấm "Scrape" để trích dữ liệu, rồi xuất sang Google Sheets, Excel, Airtable hoặc Notion miễn phí. Không cần code, không cần thiết lập, không cần quản lý proxy.
Tôi có thể scrape Craigslist thường xuyên đến mức nào mà không bị chặn?
Với một IP residential, thông lượng bền vững khoảng 0.3–0.5 request mỗi giây, kèm độ trễ ngẫu nhiên 2–5 giây giữa các trang. Nên giữ dưới 1.000 trang trong mỗi 24 giờ cho mỗi IP để tránh cả việc bị chặn lẫn ngưỡng bồi thường ấn định trong điều khoản Craigslist. Scrape vào giờ thấp điểm (2–6 giờ sáng theo giờ địa phương của thành phố mục tiêu) sẽ giảm tỷ lệ bị chặn khoảng 30–40%. Nếu cần khối lượng lớn hơn, hãy xoay residential proxy mỗi 20–30 request.
Tìm hiểu thêm


