
做租房爬虫,本地跑没问题,一上多城市就废。我前段时间抓上海、杭州、南京、成都的租金行情,用混拨代理采上海浦东,IP,一会儿跳北京、一会儿跳深圳,房源城市都对不上;跑半小时开始超时;接口还返回 200,但租金、户型、看房时间这些字段是空的。后面排查发现,不是代码问题,也不是 sleep 写得不对,核心是代理 IP 的地域不对。房产平台会看请求 IP 的归属地和房源城市是否一致,跨城请求很容易被判异常。
前提:只采公开房源,遵守平台规则和 robots,别碰登录后数据、隐私数据,也别高频打。
通用混拨、免费代理,基本不适合房产采集:
- 平台会校验 IP 归属地。你采上海房源,IP 在杭州,可能直接 403、验证码,或者返回空数据。
- 混拨代理全国随机跳,固定不了城市。多城采集时,脏数据会非常多。
- 低价代理存活差,断线重连多。重连多了,反而更容易触发平台频控。
所以别一上来就改 UA、加 sleep。先看 IP 城市对不对。
测试场景:上海、杭州、南京、成都,多城市租房房源采集,包括基础信息、租金、配套、发布时间。
测试对象:通用混拨动态代理、普通城市定向代理、住宅级稳定定向代理。
测试时长:连续 8 小时。
看这几个指标:地域准确率、连接成功率、延迟波动、封禁报错率、有效数据完整率。
目标城市匹配率只有 35%,大量请求跨城。
8 小时里断线超时频繁,整体连接成功率 68%。
跑 2 小时开始弹验证码,3 小时后 403 报错率超过 40%。
核心字段缺失严重,有效可用数据不到 50%。
这个只能临时手动看看,不适合正式采集。
地域问题好一些,精准度能到 92%,基础地域风控能过。
但节点存活短,IP 轮换频繁,延迟 80ms 到 400ms 乱跳,会话偶尔断。
整体连接成功率 85%。
连续跑 4 小时后开始零星验证码,封禁率 8%,有效数据率 82%。
适合短时、单城市、小批量。长时间多城批量,容易掉链子。
真实住宅 IP,归属地干净,实测表现最好:
地域精准度 100%,没出现跨城错乱;
8 小时连续跑,连接成功率 98% 以上,几乎不断线;
延迟稳定在 90-150ms,没有突发卡顿;
全程没有 403、没有强制验证码,封禁率接近 0;
房源核心字段完整,有效数据率 100%,不用反复补采。
租房采集不是比谁请求快,IP 城市错了,采回来也是废数据,还会把 IP 拉黑。
有些代理刚连上很快,跑一小时就开始轮换、断线、封禁。批量采集要看 8 小时以上的表现。
这类 IP 很多被爬虫滥用过,早进平台风控名单了。省的是代理费,赔的是漏采、重跑、清洗时间。
上海任务只用上海 IP,杭州任务只用杭州 IP。一个线程固定一个城市,禁止跨城市混用节点。
用粘性会话,别每个请求都换 IP。比如 10-30 分钟换一次,或者跑几百个请求再换。模拟真人长时间浏览。
换 IP 后校验一次,之后每 20-50 个请求抽检。遇到 403、验证码、空数据,立刻重新校验。免费 IP 查询接口有限速,别每次请求都查。
随机间隔 0.8-2 秒,带正常 UA、Referer,复用 cookie。单城并发别太高,先 2-5 个跑稳,再慢慢加。
租金、户型、发布时间这些字段,至少校验两个。缺字段就当失败,重试或换 IP。
记录每个城市的成功率、403 率、验证码率、空字段率。某个城市掉到 90% 以下,就降速、换池、换节点。
注意:`switch_proxy()` 必须接你代理商的换 IP API,否则重试还是同一个 IP。
import requests
import time
import random
PROXY_URL = "http://用户名:密码@代理host:端口"
TARGET_CITY = "上海"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Referer": "https://fang.com/"
}
PROXIES = {
"http": PROXY_URL,
"https": PROXY_URL
}
session = requests.Session()
session.headers.update(HEADERS)
_last_check_ts = 0
CHECK_EVERY_SECONDS = 60 # 或按请求数,比如每30个请求查一次
_req_count = 0
def get_ip_city():
"""查当前代理IP归属城市。ip-api免费版有限速,别查太勤。"""
try:
r = session.get(
"http://ip-api.com/json/?lang=zh-CN",
proxies=PROXIES,
timeout=5
)
data = r.json()
if data.get("status") != "success":
return False, data.get("message", "查询失败")
city = data.get("city") or ""
return TARGET_CITY in city, city
except Exception as e:
return False, str(e)
def switch_proxy():
"""
这里必须接代理商的换IP接口,或者用支持粘性会话的代理参数。
不换IP的话,重试大概率还是同一个节点。
"""
# 示例思路:
# new_ip = requests.get("代理商的提取API").text
# 更新全局 PROXIES
pass
def need_check(force=False):
global _last_check_ts, _req_count
now = time.time()
if force or now - _last_check_ts > CHECK_EVERY_SECONDS:
_last_check_ts = now
_req_count = 0
return True
_req_count += 1
return False
def is_valid_rent_html(html):
"""
这里别只判断200。按你页面结构加:
租金、户型、发布时间至少两个存在。
"""
if not html:
return False
# 示例:return "租金" in html and "户型" in html
return True
def fetch_rent(url, max_retry=3):
for attempt in range(max_retry):
if need_check(force=(attempt == 0)):
ok, city = get_ip_city()
if not ok:
print(f"IP地域不对,当前:{city},目标:{TARGET_CITY},准备换IP")
switch_proxy()
time.sleep(random.uniform(1, 2))
continue
try:
time.sleep(random.uniform(0.8, 2.0))
resp = session.get(url, proxies=PROXIES, timeout=8)
if resp.status_code in (403, 429, 502):
print(f"触发风控或限流:{resp.status_code},换IP重试")
switch_proxy()
time.sleep(random.uniform(2, 4))
continue
resp.raise_for_status()
if not is_valid_rent_html(resp.text):
print("页面字段不完整,当失败处理")
switch_proxy()
time.sleep(random.uniform(1, 3))
continue
return resp.text
except Exception as e:
print(f"请求异常:{e},重试")
switch_proxy()
time.sleep(random.uniform(1, 3))
return None
if __name__ == "__main__":
rent_url = "目标租房房源链接"
html = fetch_rent(rent_url)
if html:
print("采集成功,可以解析户型、租金、发布时间等字段")
else:
print("采集失败,检查代理城市、稳定性或换节点")最后一句:租房采集先解决 IP 城市,再谈稳定性和速度。IP 地域不对,代码写得再漂亮也白搭。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。