如何抓取Craigslist列表:车辆、租赁和兼职

Craigslist是一个数据宝库,但受到严格的IP速率限制。这里是每个城市的URL结构、每页120条的分页数学、许多人忽略的RSS快捷方式,以及让你不被封禁的代理设置。

Craigslist拥有大量可用的公共数据——二手车、公寓租赁、兼职、待售物品、服务——分布在数百个城市网站上。对于初学者来说,它也是CAPTCHA和IP封禁的障碍,因为Craigslist对IP地址进行严格的速率限制。好消息是:网站的URL结构简单且可预测,有一个大多数指南都跳过的RSS快捷方式,封禁问题几乎完全是IP卫生问题。本指南涵盖了每个城市的URL模式、分页数学、解析、RSS路径和保持抓取器不被封禁的代理设置。用于个人或研究用途时,请遵守Craigslist的条款,并将此视为一般信息而非法律建议。

URL模式是整个游戏的关键

Craigslist按城市子域和类别代码划分,其他都是查询参数。学会一次,就可以针对任何城市和板块:https://{city}.craigslist.org/search/{category}?query={q}。类别代码很短——sss代表所有待售物品,cto代表车主出售的车辆,apa代表公寓,ggg代表兼职。添加min_pricemax_price和其他过滤器作为参数。分页使用s参数,每页显示120个结果——所以第二页是s=120,第三页是s=240,以此类推。

import requests
from bs4 import BeautifulSoup

PROXY = "http://USER:PASS@rotating.quantumproxies.io:8000"
PROXIES = {"http": PROXY, "https": PROXY}
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
      "(KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36")

def build_url(city, category, query, page=0, min_price=None, max_price=None):
    base = f"https://{city}.craigslist.org/search/{category}"
    params = [f"query={query}"]
    if min_price: params.append(f"min_price={min_price}")
    if max_price: params.append(f"max_price={max_price}")
    if page:      params.append(f"s={page * 120}")   # 120 results per page
    return base + "?" + "&".join(params)

url = build_url("minneapolis", "cto", "bmw", page=0, max_price=8000)
r = requests.get(url, headers={"User-Agent": UA}, proxies=PROXIES, timeout=20)

解析列表

搜索结果行标记有一组稳定的类——列表块是.result-info,内部有.result-title.result-price.result-date。保护每个字段,因为并不是每个列表都有价格:

def parse_listings(html):
    soup = BeautifulSoup(html, "html.parser")
    out = []
    for row in soup.select(".result-info"):
        title_el = row.select_one(".result-title")
        price_el = row.select_one(".result-price")
        date_el  = row.select_one(".result-date")
        if not title_el:
            continue
        out.append({
            "title": title_el.get_text(strip=True),
            "url":   title_el.get("href"),
            "price": price_el.get_text(strip=True) if price_el else None,
            "date":  date_el.get("datetime") if date_el else None,
        })
    return out

listings = parse_listings(r.text)
print(len(listings), "listings on this page")

对于更丰富的记录——如车辆的里程数、租赁的卧室数——请跟随每个列表URL并解析详细页面。这会使请求数量翻倍,因此正是节奏和轮换开始重要的地方。

图解Craigslist搜索URL分解为城市子域、类别代码、查询和s分页参数
Craigslist URL是完全可预测的:城市子域、类别代码,然后是查询和120步的s参数。

大多数指南跳过的RSS快捷方式

Craigslist没有通用的公共API,但它确实为搜索结果提供了一个RSS订阅——在任何搜索URL后附加&format=rss,你就会得到一个结构化的XML订阅,而不是需要解析的HTML。它更轻,不太可能触发反机器人启发式算法,比反复加载完整结果页面更理想,适合监控:按计划轮询保存的搜索并对比新项目。它返回的字段比HTML页面少(标题、链接、时间戳),所以用于新鲜度监控,当需要价格和详细信息时再回到HTML。

import feedparser

# Any Craigslist search becomes a feed with &format=rss
feed_url = build_url("sfbay", "apa", "loft", max_price=3500) + "&format=rss"
raw = requests.get(feed_url, headers={"User-Agent": UA}, proxies=PROXIES, timeout=20)
feed = feedparser.parse(raw.content)

for entry in feed.entries[:5]:
    print(entry.updated, entry.title, entry.link)

Craigslist为何封禁你,以及如何避免被封禁

Craigslist堆叠了几种防御措施,但它们共享一个根本:它跟踪每个IP地址的请求,并封禁过于频繁的地址。具体措施包括IP速率限制、对可疑流量的CAPTCHA挑战、User-Agent监控、会话跟踪和临时IP封禁。每一个都可以通过看起来像许多普通访客而不是一个不懈的机器来击败。

因为封禁是基于IP的,出口IP做了大部分工作。一个干净的住宅代理池看起来像真实的家庭连接,每个请求轮换分散多城市抓取,以至于Craigslist的每IP计数器从未触发。如果你在多个城市收集,我们的修复429速率限制指南涵盖了节奏和并发预算方面,反封禁清单将其全部结合在一起。

在干净的住宅IP上抓取Craigslist

跨城市扩展

Craigslist数据的真正力量在于跨城市比较——相同的二手车或租赁在多个大都市的定价。循环你的城市列表,为每个城市绑定一个新的轮换出口,限制并发,并将城市与每条记录一起存储。对于车辆和租赁研究,使用相同的模式来提供比较管道;参见我们的抓取汽车列表房地产数据指南,将原始列表转化为市场信号。

清单映射Craigslist反抓取防御措施,如IP速率限制和CAPTCHA,以及解决方案如住宅轮换和节奏控制
每个Craigslist防御都是IP和节奏问题——轮换住宅出口并保持每秒1-2个请求以下。

常见问题解答

你能抓取Craigslist吗?

是的——公共搜索和列表页面是可以抓取的,Craigslist甚至为搜索提供了一个RSS订阅。障碍是激进的每IP速率限制,而不是解析。轮换住宅IP,将请求控制在每秒1-2个,你可以可靠地收集车辆、租赁、兼职和待售数据。尊重Craigslist的条款,只抓取公共数据。

Craigslist有API吗?

没有通用的公共数据API,但每个搜索URL可以通过附加&format=rss返回一个RSS订阅。这为你提供了一个结构化的XML订阅,包含标题、链接和时间戳——非常适合监控新列表——而HTML页面则包含更完整的字段,如价格和描述。

如何避免抓取Craigslist时被IP封禁?

Craigslist通过IP速率封禁,因此解决方案都是关于分散和减缓负载:每个请求轮换住宅IP,保持每秒1-2个请求并添加抖动,轮换你的User-Agent,每个城市使用一个新的会话,一旦看到CAPTCHA或403立即退避。一个数据中心IP猛击一个城市最快被封禁。

Craigslist的分页如何工作?

Craigslist每页显示120个结果,并使用s查询参数作为偏移进行分页。第一页省略s,第二页是s=120,第三页是s=240,以此类推。以120的步长递增,直到某页返回少于120个结果,这标志着结束。

Craigslist易于阅读且易于被封禁——而第二个问题完全是关于IP卫生。掌握URL模式,使用RSS进行监控,以120的步长分页,并以人类的速度通过轮换的住宅IP路由所有内容。这样做,CAPTCHA就会停止出现。

从轮换的住宅代理开始