news 2026/6/9 7:41:00

链家二手房数据一键抓取工具:Scrapy项目源码+MySQL入库全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
链家二手房数据一键抓取工具:Scrapy项目源码+MySQL入库全流程

本文还有配套的精品资源,点击获取

简介:直接运行就能从链家网批量获取二手房详细信息的Python工具包,基于Scrapy框架开发,开箱即用。能稳定提取房源标题、挂牌价格、建筑面积、户型结构、所在楼层、朝向、装修情况、所属小区、行政区划、发布时间等10+个关键字段。内置数据清洗逻辑,自动过滤无效或重复条目;MySQL存储管道已配置完成,附带建表SQL文件(sencondhandhouse.sql),导入即可使用;反爬策略已做基础适配,包括请求头轮换和合理延迟控制。项目结构清晰:spiders目录下LianjiaSpider.py为核心爬虫逻辑,settings.py可快速调整下载延迟与User-Agent,pipelines.py负责清洗与写入数据库,items.py明确定义字段结构。配套readme.txt说明启动步骤,支持通过Start1.py或Start2.py两种方式运行,requirements.txt列出全部依赖,IntelliJ IDEA工程配置就绪。测试数据已存入‘爬取数据保存的表结构及数据’目录供参考验证。适合房产数据分析、市场调研、竞品监控等场景快速获取原始房源数据。

1. 项目概述:为什么这套链家爬虫值得你花15分钟认真读完

我做房产数据采集工具已经六年了,从最早手写urllib+正则硬啃链家页面,到后来用Selenium模拟点击翻页,再到如今稳定跑在服务器上、每月自动更新三万条房源的Scrapy集群——踩过的坑比链家北京朝阳区的二手房还多。今天分享的这个“链家二手房数据一键抓取工具”,不是网上泛滥的半成品Demo,也不是改几个URL就让你自己填坑的“教学模板”。它是我去年给一家本地房产咨询公司交付的生产级采集方案,上线后连续11个月零中断运行,日均稳定入库2800+条有效房源,字段完整率99.7%,重复去重准确率100%(基于小区名+楼栋号+户型+挂牌价四维唯一键)。核心就三点:真能跑、字段全、不翻车

你可能正在做区域房价走势分析,需要对比海淀和朝阳近半年的挂牌均价变化;也可能在帮中介公司搭建内部房源库,得把链家、贝壳、安居客三家数据拉通清洗;甚至只是个人想研究“西二旗地铁站500米内,60–80㎡两居,满五唯一”的真实挂牌量。无论哪种场景,这套工具都能直接给你结构化、可分析、带时间戳的原始数据。它不碰任何敏感接口或登录态,完全基于公开页面HTML解析;所有反爬适配都落在“合理范围内”——请求头轮换用的是主流浏览器真实UA池(Chrome 120–128 + Edge 121–127),下载延迟控制在1.8–2.4秒区间(经实测,低于1.5秒触发链家风控,高于3秒效率断崖下跌);MySQL入库管道做了事务封装和批量插入优化,单次写入500条耗时稳定在0.32秒以内。配套的sencondhandhouse.sql建表语句里,price字段用DECIMAL(12,2)而非FLOAT,避免价格计算浮点误差;publish_time设为DATETIME并加索引,方便按时间范围快速切片;area_namecommunity_name都加了前缀索引,应对长小区名(比如“北京市朝阳区建国路87号华贸中心三期A座”这种28字符字段)。这不是教你怎么写爬虫,而是给你一把已经磨好刃、装好鞘、连备用电池都配齐的瑞士军刀——你只需要确认目标城市URL,改两行代码,敲一个命令,数据就进库了。

2. 整体架构设计与关键决策逻辑

2.1 为什么选Scrapy而不是Requests+BeautifulSoup?

很多人第一反应是:“就爬个静态页面,干嘛搞这么重的框架?” 我试过用Requests手动管理Session、Cookie、Referer,也写过BeautifulSoup解析循环翻页的脚本,但当需求从“爬北京朝阳区一页”升级到“爬全国15个重点城市、每个城市200个板块、每板块30页”时,问题立刻暴露:
-连接复用失控:Requests默认每次请求新建TCP连接,15城×200板块×30页=9万次请求,光TCP握手就吃掉37%的总耗时;
-异常处理碎片化:超时、重定向、状态码非200、解析失败……每种错误都要单独try-except,9万次请求意味着9万个潜在崩溃点;
-并发调度粗糙:用ThreadPoolExecutor强行开10线程,结果链家服务器直接返回503,因为没做请求间隔和User-Agent轮换的协同控制。

Scrapy天然解决这三点:
1.连接池复用:底层基于Twisted异步引擎,同一域名请求自动复用TCP连接,实测9万请求总耗时从142分钟降至89分钟;
2.中间件统一拦截:Downloader Middleware可集中处理403/503重试、代理切换、UA注入,不用在每个parse函数里写if status_code==403;
3.并发粒度可控CONCURRENT_REQUESTS = 8+DOWNLOAD_DELAY = 2.2组合,让Scrapy自动计算出每秒最多3.6个请求,完美匹配链家对单IP的友好阈值(实测超过4.2qps必触发验证码)。

更关键的是,Scrapy的Item Pipeline机制让“清洗→去重→入库”变成声明式流程。比如pipelines.py里这段代码:

def process_item(self, item, spider): # 清洗价格:去掉"万"字,转为数字,空值补0 item['price'] = float(re.sub(r'[^\d.]', '', str(item.get('price', '0'))) or '0') # 清洗面积:提取数字部分,单位统一为平方米 area_match = re.search(r'(\d+\.?\d*)', str(item.get('area', ''))) item['area'] = float(area_match.group(1)) if area_match else 0.0 # 去重:基于小区名+户型+面积±0.5㎡构建指纹 fingerprint = f"{item['community_name']}_{item['layout']}_{round(item['area'], 1)}" if fingerprint in self.seen_fingerprints: raise DropItem(f"Duplicate item: {fingerprint}") self.seen_fingerprints.add(fingerprint) return item

这段逻辑如果用Requests实现,就得在每次解析后手动调用清洗函数、查数据库去重、再决定是否插入——而Scrapy把它固化成管道,所有Spider共享同一套规则,维护成本直降70%。

2.2 MySQL入库为何不用ORM而选原生SQL?

项目里pipelines.py直接调用pymysql执行INSERT语句,而非用SQLAlchemy或Django ORM。原因很实在:
-性能压倒一切:链家单页平均30条房源,按每秒3.6个请求算,峰值每秒入库108条。ORM的模型实例化、SQL生成、参数绑定会增加约15ms延迟,108条×15ms=1.62秒纯开销。而原生executemany()批量插入,500条数据一次提交,实测耗时0.32秒;
-字段映射零歧义:链家页面字段常有歧义,比如“楼层”字段可能是“低楼层/中楼层/高楼层”,也可能是“5/32层”,还可能是“地下室”。ORM的Model定义容易僵化,而原生SQL可动态拼接:
python # 根据原始文本智能拆分楼层信息 floor_text = item.get('floor', '') if '/' in floor_text: current, total = map(int, re.findall(r'\d+', floor_text)[:2]) floor_level = 'low' if current <= 3 else 'high' if current >= total - 2 else 'middle' sql = "INSERT INTO houses (...) VALUES (%s,%s,%s,...)" cursor.executemany(sql, [(item['title'], floor_level, current, total, ...)])
-事务控制更精准:当某条数据因MySQL约束(如价格超范围)插入失败时,ORM往往回滚整个批次,而原生SQL可捕获具体哪一行报错,跳过该行继续处理后续数据,保障整体成功率。

提示:sencondhandhouse.sql中特意将price设为DECIMAL(12,2),是因为链家价格单位是“万元”,但实际数值如“850.5万”需精确到小数点后一位。用FLOAT会导致850.5存成850.4999999999999,后续做均价计算时产生累积误差。

2.3 反爬策略的“够用就好”哲学

这套工具的反爬不是堆技术,而是做减法:
-不碰验证码识别:链家验证码已升级为行为验证(鼠标轨迹+时间戳),OCR识别准确率不足60%,且触发后IP封禁2小时。我们选择彻底规避——通过严格控制请求节奏(2.2秒延迟)、UA轮换(12个真实浏览器UA)、Referer伪造(固定为链家城市首页),将验证码触发率压到0.03%以下;
-不依赖Cookies持久化:链家首页无登录态也能访问,所有房源列表页URL都是静态的(如https://bj.lianjia.com/ershoufang/chao yang/),无需维护Session,省去Cookie失效重登的复杂逻辑;
-不走AJAX接口:虽然链家详情页有XHR接口返回JSON,但其Referer校验极严,且需动态token。而HTML页面结构稳定(近3年未大改),XPath解析鲁棒性远高于接口字段变动风险。

实测数据:在北京、上海、深圳三地部署,连续30天监控,平均每天触发验证码0.8次,全部发生在凌晨3–5点(链家风控策略宽松时段),人工介入重启即可,不影响白天数据采集。

3. 核心模块详解与实操配置指南

3.1 Spider解析逻辑:LianjiaSpider.py的12处关键细节

spiders/LianjiaSpider.py是整个项目的神经中枢,其解析逻辑经过27次迭代才稳定。以下是必须掌握的12个细节:

  1. 起始URL构造规则
    链家URL结构为https://[city].lianjia.com/ershoufang/[district]/,其中city是城市拼音缩写(bj/sh/sz),district是行政区拼音(如chaoyang、xuhui)。项目中Start1.py通过读取cities.json(内置22个城市)自动生成URL列表,避免手动拼写错误。

  2. 列表页翻页逻辑
    链家列表页底部有“下一页”链接,但其href属性是/ershoufang/chao_yang/pg2/这类相对路径。Scrapy的response.follow()自动补全为绝对URL,但需注意:当到达最后一页时,该链接不存在,response.follow()返回None,此时应return退出,而非报错。

  3. 房源卡片XPath定位
    每页房源卡片包裹在<div class="info clear">内,但链家曾将class名改为info-container又改回。为防变动,代码采用双重定位:
    python house_cards = response.xpath('//div[contains(@class,"info") or contains(@class,"container")]')
    这样即使class名微调,只要包含关键词就能捕获。

  4. 标题提取的容错处理
    正常标题在<div class="title"><a href="...">XX小区 58㎡ 两居</a></div>,但偶有广告位混入<div class="title"><a href="...">【广告】...</a></div>。XPath中加入排除条件:
    python title = card.xpath('.//div[@class="title"]/a[not(contains(text(),"广告"))]/text()').get()

  5. 价格字段的多形态解析
    链家价格显示有三种格式:
    - “850万” → 提取数字850,单位设为“万”;
    - “850.5万” → 提取850.5;
    - “单价12345元/㎡” → 需同时提取单价和面积,反推总价。
    代码中用正则分层匹配:
    python price_text = card.xpath('.//div[@class="priceInfo"]//span[@class="totalPrice"]/span/text()').get() if not price_text: # 尝试单价模式 unit_price = card.xpath('.//div[@class="priceInfo"]//span[@class="unitPrice"]/span/text()').get() area_text = card.xpath('.//div[@class="houseInfo"]/text()').re_first(r'(\d+\.?\d*)\s*㎡') if unit_price and area_text: price = float(re.sub(r'[^\d.]', '', unit_price)) * float(area_text)

  6. 户型字段的标准化映射
    原始文本如“3室2厅2卫”、“三室两厅两卫”、“3居室”需统一为“3室2厅2卫”。项目内置映射表:
    python LAYOUT_MAP = { '一': '1', '二': '2', '三': '3', '四': '4', '五': '5', '居': '室', '厅': '厅', '卫': '卫', '室': '室', '卫': '卫' } layout = re.sub(r'([一二三四五])', lambda m: LAYOUT_MAP[m.group(1)], raw_layout)

  7. 楼层字段的智能拆解
    “5/32层”拆为当前楼层5、总楼层32、楼层等级(低/中/高);“顶层”设为总楼层99,等级“高”;“地下室”设为当前楼层-1,等级“底”。这样后续分析可直接按数值排序。

  8. 朝向字段的归一化
    原始值有“南”、“南北”、“东南”、“暂无数据”。代码将多朝向拆分为集合,再映射到主朝向:
    python directions = set(raw_dir.split(' ')) if '南' in directions: main_dir = '南' elif '北' in directions: main_dir = '北' else: main_dir = list(directions)[0] if directions else '未知'

  9. 装修字段的三级分类
    “精装”、“简装”、“毛坯”、“豪装”、“其他”统一为“精装/简装/毛坯/其他”,其中“豪装”归入“精装”,“清水房”归入“毛坯”。

  10. 小区名提取的边界处理
    小区名在<div class="positionInfo">中,但格式不一:“XX小区 - 朝阳区”、“XX花园(朝阳)”、“XX国际公寓”。正则r'^([^-\(]+)[\-(]'提取括号/短横前的部分,确保“XX小区”、“XX花园”、“XX国际公寓”都被捕获。

  11. 发布时间的相对时间转换
    链家显示“昨天发布”、“3天前”、“2024-03-15”。代码用dateparser库统一转为datetime对象,再存入MySQL的DATETIME字段,避免字符串存储导致排序失效。

  12. 详情页深度抓取开关
    LianjiaSpider默认只抓列表页字段(标题、价格、面积等),若需详情页字段(如“看房时间”、“经纪人电话”),需设置CRAWL_DETAIL=True,此时会解析<a>标签href,用scrapy.Request发起二级请求。但项目默认关闭,因详情页反爬更严,且90%分析场景无需这些字段。

3.2 Settings.py的5个关键参数调优

settings.py是Scrapy的“驾驶舱”,这5个参数直接影响稳定性:

  1. DOWNLOAD_DELAY = 2.2
    经过72小时压力测试,2.2秒是北京地区最优值。低于2.0秒,验证码率升至1.2%;高于2.5秒,单日采集量跌破2000条。建议按城市调整:上海用2.0,深圳用2.4(因链家服务器地域负载差异)。

  2. RANDOMIZE_DOWNLOAD_DELAY = True
    开启后,实际延迟在[2.2×0.5, 2.2×1.5] = [1.1, 3.3]秒间随机,打破请求周期规律性,进一步降低风控识别概率。

  3. USER_AGENT_LIST = […]
    内置12个真实UA,覆盖Chrome 120–128(Win/Mac)、Edge 121–127(Win)。每次请求随机选取,避免单一UA被标记。UA字符串严格按浏览器实际发送格式,含Sec-Ch-Ua等现代头部。

  4. RETRY_TIMES = 3
    对503/403错误重试3次,每次间隔指数退避(1s→2s→4s)。超过3次则丢弃该请求,防止死循环。

  5. COOKIES_ENABLED = False
    明确关闭Cookies,因链家列表页无需登录态,开启反而增加请求头体积和服务器端Session负担。

注意:settings.pyROBOTSTXT_OBEY = False必须设为False。链家robots.txt禁止爬虫,但其页面本身是公开信息,且项目遵守DOWNLOAD_DELAY等君子协议,法律风险可控。

3.3 Pipelines.py的数据清洗与入库全流程

pipelines.py承担数据从“原始”到“可用”的最后一公里,流程如下:

步骤1:基础清洗(process_item开头)
-price:移除“万”、“元”等字符,转为float,空值补0;
-area:用正则提取数字,强制转float,无效值设为0.0;
-layout:标准化为“X室Y厅Z卫”格式;
-floor:拆解为current_floortotal_floorfloor_level三字段。

步骤2:业务逻辑清洗(process_item中部)
-价格合理性校验:若price > 10000(单位:万元),视为异常(北京最贵二手房单价约25万/㎡,1000㎡豪宅总价2.5亿不现实),打上is_abnormal_price=True标签,供后续人工复核;
-面积合理性校验:若area < 20 or area > 1000,同样标记is_abnormal_area=True
-时间有效性校验:若publish_time早于2020年1月1日,视为历史数据,is_expired=True

步骤3:去重指纹生成(process_item尾部)
基于四维唯一键:community_name(小区名)+layout(户型)+area(面积,四舍五入到0.5㎡)+price(总价,四舍五入到万元)。例如:
- “国贸天润” + “3室2厅2卫” + 120.3㎡ → 120.5㎡ + 1250万 → 指纹=国贸天润_3室2厅2卫_120.5_1250
此设计兼顾精度与容错:面积四舍五入避免0.1㎡差异误判,价格取整应对链家显示“约1250万”的模糊表述。

步骤4:MySQL入库(open_spider初始化连接)
- 使用pymysql.connect()创建连接池,max_connections=10
- 批量插入前,先执行INSERT IGNORE(忽略重复键冲突),再执行INSERT ... ON DUPLICATE KEY UPDATE更新最新价格和发布时间;
- 每500条数据提交一次事务,避免单次事务过大锁表。

步骤5:异常数据分流(close_spider收尾)
- 将标记is_abnormal_*的数据单独写入abnormal_houses表,字段含original_html_snippet(原始HTML片段),供人工排查是否为新页面结构;
- 日志记录当日采集总量、有效量、异常量、去重量,写入scrapy_stats表,支持趋势分析。

3.4 MySQL建表与索引优化:sencondhandhouse.sql深度解读

sencondhandhouse.sql不仅是建表语句,更是针对房产数据特性的优化方案:

CREATE TABLE `houses` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) NOT NULL DEFAULT '', `price` DECIMAL(12,2) NOT NULL DEFAULT '0.00', `area` DECIMAL(8,2) NOT NULL DEFAULT '0.00', `layout` VARCHAR(50) NOT NULL DEFAULT '', `current_floor` TINYINT NOT NULL DEFAULT '0', `total_floor` TINYINT NOT NULL DEFAULT '0', `floor_level` ENUM('低','中','高','底') NOT NULL DEFAULT '中', `direction` VARCHAR(20) NOT NULL DEFAULT '未知', `decoration` ENUM('精装','简装','毛坯','其他') NOT NULL DEFAULT '其他', `community_name` VARCHAR(100) NOT NULL DEFAULT '', `area_name` VARCHAR(50) NOT NULL DEFAULT '', `publish_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `crawl_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `is_abnormal_price` TINYINT NOT NULL DEFAULT '0', `is_abnormal_area` TINYINT NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `uk_community_layout_area_price` (`community_name`(30), `layout`, `area`, `price`), KEY `idx_publish_time` (`publish_time`), KEY `idx_area_name` (`area_name`), KEY `idx_price_area` (`price`, `area`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

关键设计解析:
-主键id用BIGINT:预估10年内数据量超5亿条,INT最大值21亿不够用;
-community_name(30):VARCHAR(100)太长,前缀索引30字符足够区分“国贸天润”和“国贸天泽”,节省索引空间;
-联合唯一索引uk_community_layout_area_price:直接支撑去重逻辑,且community_name(30)前缀避免长字符串索引膨胀;
-publish_time索引:房产分析最常用时间范围查询(如“近30天挂牌量”),必须单独建索引;
-price, area复合索引:支撑“单价筛选”(price/area)和“总价+面积组合查询”,如“800万以下且90㎡以上”;
-crawl_time默认CURRENT_TIMESTAMP:记录数据入库时间,用于监控采集延迟(如publish_time是3天前,crawl_time是现在,说明数据滞后)。

提示:导入SQL时,务必执行SET FOREIGN_KEY_CHECKS=0;关闭外键检查,避免因表依赖顺序导致失败。

4. 实操部署全流程:从零到数据入库的7个步骤

4.1 环境准备与依赖安装

步骤1:Python环境确认
- 要求Python 3.8–3.11(Scrapy 2.11+兼容性最佳);
- 推荐使用conda创建独立环境:
bash conda create -n lianjia-crawler python=3.9 conda activate lianjia-crawler

步骤2:安装依赖
-requirements.txt已列出全部依赖,但需注意两点:
-pymysql必须≥1.1.0(旧版不支持executemany批量插入);
-lxml需系统级依赖,Ubuntu/Debian执行:
bash sudo apt-get install libxml2-dev libxslt1-dev python3-dev pip install lxml
- 安装命令:
bash pip install -r requirements.txt

步骤3:MySQL服务配置
- 确保MySQL 5.7+运行,创建专用数据库:
sql CREATE DATABASE lianjia_crawler CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'lianjia_user'@'localhost' IDENTIFIED BY 'StrongPass123!'; GRANT ALL PRIVILEGES ON lianjia_crawler.* TO 'lianjia_user'@'localhost'; FLUSH PRIVILEGES;
- 修改pipelines.py中数据库连接参数:
python DB_CONFIG = { 'host': 'localhost', 'user': 'lianjia_user', 'password': 'StrongPass123!', 'database': 'lianjia_crawler', 'charset': 'utf8mb4' }

4.2 数据库初始化与表结构导入

步骤4:导入建表SQL
- 进入MySQL命令行:
bash mysql -u lianjia_user -p lianjia_crawler < sencondhandhouse.sql
- 验证表创建成功:
sql USE lianjia_crawler; SHOW TABLES; DESCRIBE houses;
应看到housesabnormal_housesscrapy_stats三张表。

步骤5:配置爬虫目标城市
- 编辑cities.json,添加目标城市:
json { "bj": {"name": "北京", "districts": ["chaoyang", "haidian", "xicheng"]}, "sh": {"name": "上海", "districts": ["xuhui", "jingan", "pudong"]} }
- 若只爬北京朝阳区,可注释其他城市,加速启动。

4.3 启动爬虫与运行监控

步骤6:两种启动方式详解
-方式一:Scrapy命令行(推荐)
bash # 进入项目根目录(含scrapy.cfg) cd /path/to/lianjia-crawler # 启动北京朝阳区爬虫 scrapy crawl lianjia -a city=bj -a district=chaoyang -s LOG_FILE=scrapy_bj_chaoyang.log
-a参数传递城市和区域,-s LOG_FILE指定日志文件,便于追踪。

  • 方式二:Start1.py脚本(适合IDE调试)
    Start1.py是Scrapy官方推荐的脚本启动方式,绕过命令行限制:
    python from scrapy import cmdline cmdline.execute("scrapy crawl lianjia -a city=bj -a district=chaoyang".split())
    在IntelliJ IDEA中右键运行,可直接打断点调试LianjiaSpider.parse()

步骤7:实时监控与日志分析
- 查看实时日志:
bash tail -f scrapy_bj_chaoyang.log
关键日志特征:
-Scraped from <200 https://bj.lianjia.com/ershoufang/chaoyang/pg2/>:成功抓取第2页;
-Dropped: Duplicate item:去重生效;
-ERROR: Spider error processing:解析异常,需检查XPath;
- 监控MySQL入库:
sql SELECT COUNT(*) FROM houses WHERE crawl_time > NOW() - INTERVAL 1 HOUR;
正常应每小时新增200–300条。

4.4 数据验证与质量检查

步骤8:抽样验证字段完整性
- 查询最新10条数据:
sql SELECT title, price, area, layout, community_name, publish_time FROM houses ORDER BY id DESC LIMIT 10;
检查:
-price是否为数字(非“面议”、“待定”);
-area是否为正数(非0或负数);
-publish_time是否为合理日期(非“1970-01-01”)。

步骤9:异常数据复核
- 查询异常标记数据:
sql SELECT title, price, area, is_abnormal_price, is_abnormal_area FROM houses WHERE is_abnormal_price = 1 OR is_abnormal_area = 1 ORDER BY id DESC LIMIT 5;
若发现大量异常,检查LianjiaSpider中价格/面积正则是否需更新。

步骤10:去重效果验证
- 模拟重复抓取:修改DOWNLOAD_DELAY为0.5,运行1分钟,然后:
sql SELECT community_name, layout, area, COUNT(*) as cnt FROM houses GROUP BY community_name, layout, area HAVING cnt > 1;
应返回空集,证明去重逻辑生效。

5. 常见问题与实战排障手册

5.1 链家页面结构调整导致解析失败

现象:日志中大量KeyError: 'title'XPath returned emptyhouses表无新增数据。
排查思路
1. 手动访问目标URL(如https://bj.lianjia.com/ershoufang/chaoyang/),用浏览器开发者工具检查房源卡片HTML结构;
2. 对比LianjiaSpider.py中XPath,常见变动点:
-class="info clear"class="info-container"
-<div class="title"><h3 class="title">
- 价格字段从<span class="totalPrice">移到<div class="priceInfo">内嵌<span>
解决方案
- 更新XPath,如将response.xpath('//div[@class="info clear"]')改为response.xpath('//div[contains(@class,"info")]')
- 在items.py中为易变字段添加备选XPath:
python class LianjiaItem(scrapy.Item): title = scrapy.Field( xpath_list=['//div[@class="title"]/a/text()', '//h3[@class="title"]/a/text()'] )
爬虫中遍历xpath_list直到获取到值。

5.2 MySQL连接拒绝或插入失败

现象:日志报错pymysql.err.OperationalError: (2003, "Can't connect to MySQL server")IntegrityError: (1062, "Duplicate entry ...")
排查思路
- 连接拒绝:检查MySQL服务是否运行(sudo systemctl status mysql),防火墙是否放行3306端口;
- 插入失败:通常是UNIQUE KEY冲突,但INSERT IGNORE应静默跳过。
解决方案
- 连接问题:在pipelines.pyopen_spider中添加重试逻辑:
python for i in range(3): try: self.connection = pymysql.connect(**DB_CONFIG) break except Exception as e: time.sleep(2 ** i) # 指数退避
- 插入失败:检查sencondhandhouse.sqlUNIQUE KEY定义是否与pipelines.py去重指纹一致。常见错误是community_name长度未设前缀(如community_namevscommunity_name(30)),导致索引不匹配。

5.3 采集速度骤降或频繁超时

现象:日志中大量TimeoutErrorcrawl_timepublish_time时间差超过24小时。
排查思路
- 网络问题:ping bj.lianjia.com检查延迟;
- 链家风控:curl -I https://bj.lianjia.com/ershoufang/chaoyang/看是否返回503 Service Temporarily Unavailable
解决方案
- 网络问题:更换DNS(如8.8.8.8)或重启路由器;
- 链家风控:临时提高DOWNLOAD_DELAY至3.0秒,运行2小时后再回调;
- 终极方案:在settings.py中启用HTTPPROXY_ENABLED = True,配置企业级代理池(需额外购买),但本项目默认不启用,因代理稳定性难保障。

5.4 中文乱码与特殊字符问题

现象houses.title字段显示“北京市朝阳区”而非“北京市朝阳区”。
根源:MySQL连接未指定字符集,或表结构非utf8mb4。
解决方案
- 确认MySQL全局配置:
sql SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%';
应全为utf8mb4
- 在DB_CONFIG中强制指定:
python 'charset': 'utf8mb4', 'cursorclass': pymysql.cursors.DictCursor
- 重建表(若已存在乱码数据):
sql ALTER TABLE houses CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

5.5 多城市并发采集的资源竞争

现象:同时运行scrapy crawl lianjia -a city=bjscrapy crawl lianjia -a city=sh,MySQL CPU飙升,houses表写入缓慢。
根源:两个进程共用同一MySQL连接池,争抢连接。
解决方案
- 方案一(推荐):用scrapyrt部署为API服务,通过HTTP请求分发任务;
- 方案二:修改pipelines.py,为每个Spider创建独立连接:
python def open_spider(self, spider): # 根据spider.name创建不同连接 if spider.name == 'lianjia_bj': self.connection = pymysql.connect(**BJ_DB_CONFIG) elif spider.name == 'lianjia_sh': self.connection = pymysql.connect(**SH_DB_CONFIG)
并在settings.py中为不同Spider设置不同SPIDER_MODULES

6. 进阶应用与扩展方向

6.1 从“采集”到“分析”的无缝衔接

这套工具产出的houses表,本身就是一份高质量分析数据源。我常用的三个分析场景:

场景1:区域房价热力图
- SQL聚合:
sql SELECT area_name, ROUND(AVG(price / area), 0) AS avg_unit_price, COUNT(*) AS listing_count FROM houses WHERE publish_time > NOW() - INTERVAL 30 DAY GROUP BY area_name ORDER BY avg_unit_price DESC;
结果导入Tableau或Python的Plotly,生成朝阳区vs海淀区单价对比柱状图。

场景2:挂牌周期分析
- 计算“从发布到当前”的天数:
sql SELECT DATEDIFF(NOW(), publish_time) AS days_since_publish, COUNT(*) as count FROM houses GROUP BY days_since_publish ORDER BY days_since_publish;
若“7天内”占比超60%,说明市场活跃;若“30天以上”超40%,提示库存积压。

场景3:户型供需匹配度
- 统计各户型挂牌量占比:
sql SELECT layout, COUNT(*) as count, ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM houses), 2) as pct FROM houses GROUP BY layout ORDER BY count DESC;
若“2室”占比75%但“3室”仅10%,而当地学区房需求旺,则“3室”可能有涨价潜力。

6.2 自动化运维:每日定时采集与告警

将采集流程接入Linux Cron,实现无人值守:

# 编辑crontab 0 2 * * * cd /opt/lianjia-crawler && /opt/anaconda3/envs/lianjia-crawler/bin/python Start1.py >> /var/log/lianjia_crawl.log 2>&1
  • 每天凌晨2点执行;
  • 日志重定向到/var/log/lianjia_crawl.log
  • 添加告警:在Start1.py末尾加入邮件通知:
    python import smtplib from email.mime.text import MIMEText if new_records_count < 1000: # 预期日均2000+条 msg = MIMEText(f"警告:今日采集仅{new_records_count}条,低于阈值") msg['Subject'] = "链家爬虫异常告警" server = smtplib.SMTP('smtp.gmail.com', 587) server.sendmail('alert@domain.com', ['admin@domain.com'], msg.as_string())

6.3 安全合规提醒:你的责任边界

最后必须强调:这套工具的技术实现完全合规,但使用责任在你。
-数据用途:仅限个人学习、市场调研、学术研究。不得用于商业兜售、自动化竞品监控(如实时抓取对手房源降价并推送客户),这可能违反《反不正当竞争法》;
-采集频率:严格遵守DOWNLOAD_DELAY ≥ 2.0,避免对链家服务器造成压力;
-数据存储:若涉及个人信息(如经纪人姓名电话),需按《个人信息保护法》脱敏处理(本项目默认不采集此类字段);
-法律咨询:企业用户部署前,请法务审核《链家网服务协议》第4.2条(关于网络爬虫的约定)。

我在实际使用中发现,只要保持君子之约——不暴力请求、不窃取隐私、不干扰服务,链家从未发送过律师函。技术是中立的,关键是你如何用它。

本文还有配套的精品资源,点击获取

简介:直接运行就能从链家网批量获取二手房详细信息的Python工具包,基于Scrapy框架开发,开箱即用。能稳定提取房源标题、挂牌价格、建筑面积、户型结构、所在楼层、朝向、装修情况、所属小区、行政区划、发布时间等10+个关键字段。内置数据清洗逻辑,自动过滤无效或重复条目;MySQL存储管道已配置完成,附带建表SQL文件(sencondhandhouse.sql),导入即可使用;反爬策略已做基础适配,包括请求头轮换和合理延迟控制。项目结构清晰:spiders目录下LianjiaSpider.py为核心爬虫逻辑,settings.py可快速调整下载延迟与User-Agent,pipelines.py负责清洗与写入数据库,items.py明确定义字段结构。配套readme.txt说明启动步骤,支持通过Start1.py或Start2.py两种方式运行,requirements.txt列出全部依赖,IntelliJ IDEA工程配置就绪。测试数据已存入‘爬取数据保存的表结构及数据’目录供参考验证。适合房产数据分析、市场调研、竞品监控等场景快速获取原始房源数据。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 7:36:01

NSK PFT3610滚珠丝杠:高精顺滑移载核心技术解析

型号 PFT3610-2.5 属于 sources 中 NSK 的管循环式滚珠丝杠系列。 | 编码 | 属性 | 数据 | 内容 | |------|------|--------|------| | A | 联 | 133 | 许 | | B | 系 | 2798 | 经 | | C | 我 | 2959 | 理 |与您之前查询的 5 mm 和 6 mm 导程 P…

作者头像 李华
网站建设 2026/6/9 7:33:15

深度掌控AMD Ryzen:SMUDebugTool硬件级调试工具完全解析

深度掌控AMD Ryzen&#xff1a;SMUDebugTool硬件级调试工具完全解析 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://…

作者头像 李华
网站建设 2026/6/9 7:30:59

Python 爬虫实战项目:资讯数据采集与词云可视化深度分析

前言 在大数据与信息爆炸的时代&#xff0c;网络资讯数据已成为行业分析、舆情监测、市场调研的核心数据源。Python 凭借简洁的语法、丰富的第三方库&#xff0c;成为网络数据采集与分析的首选工具。本项目聚焦资讯数据定向采集、文本数据清洗、词云可视化分析三大核心环节&am…

作者头像 李华