短租房源信息结构化:字段拆分、数据建模与Python解析实践
2026/9/18 2:27:46 网站建设 项目流程

一条短租房源信息,比如“碧桂园山湖城观澜1街1座2903,3室1厅1厨1卫2阳台,3床:1.8m+1.5m+1.2m,可日租/周租/月租,欢迎各位邻居咨询”,看起来只是几行文字,但在房东、平台和租客之间流动时,它要承担很多角色:房源唯一标识、展示信息、床型容量、租期模式、联系方式。非结构化文本一旦进入数据库或接口,就会立刻暴露出问题:很难查询、统计和自动计算。如果你正在做民宿短租系统、房源信息聚合平台或本地生活类应用,迟早要处理这类文本到结构化数据的转换。这篇文章以这条真实文本为样例,拆解如何把一条短租房源广告转成字段模型、数据库表、JSON 校验规则和可运行的解析脚本,并讨论日租、周租、月租场景下最容易被忽略的工程细节。

1. 原始房源文本里有价值,但要先拆成可管理字段

1.1 为什么必须做字段结构化

先理解一个事实:人是靠语义阅读文本的,程序只能靠字段、索引和逻辑来理解数据。上面这条房源文本,人一眼能看出是 3 室 1 厅 1 厨 1 卫 2 阳台,有三张床,分别接近 1.8 米、1.5 米和 1.2 米,支持日租、周租、月租。但程序拿到这段字符串时,如果只把它当作标题字段存进数据库,后续所有业务都很难做。

举个例子。租客想筛选“两居室以上”“有 1.8 米大床”“可以月租”的房源。如果原始文本只是被放进一个长文本字段,最常见的做法是使用LIKE '%月租%'模糊查询,结果可能包含“月租金”“月租价”等干扰项。更麻烦的是,日租价格、周租价格、月租价格如果都写在同一段话里,程序无法自动判断哪个价格对应哪个租期。

结构化之后,每个字段都变成独立可校验、可索引、可参与计算的数据。比如卧室数量是bedroom_count = 3,床型列表是[1.8m, 1.5m, 1.2m],租期模式是["day", "week", "month"]。这样筛选条件可以写成 SQL,价格可以通过租金策略表计算,房态可以通过日历表排期。

没有结构化的房源文本,短期看只是维护成本高;长期看会出现同一房源在不同平台展示不一致、价格计算错误、订单冲突等连锁问题。所以第一步不是急着写代码,而是把一条文本里的信息拆成完整字段清单。

1.2 字段拆分:从文本提取的信息清单

把“碧桂园山湖城观澜1街1座2903,3室1厅1厨1卫2阳台,3床:1.8m+1.5m+1.2m,可日租/周租/月租,欢迎各位邻居咨询。”按信息类别拆分,可以得到下面这些字段。

原始片段字段名字段类型示例值
碧桂园山湖城project_namestring碧桂园山湖城
观澜1街block_namestring观澜1街
1座building_nostring1座
2903room_nostring2903
3室1厅1厨1卫2阳台bedroom_count / living_room_count / kitchen_count / bathroom_count / balcony_countint3 / 1 / 1 / 1 / 2
1.8m+1.5m+1.2mbedsarray[{size_m:1.8, count:1}, ...]
日租/周租/月租rental_modesarray[day, week, month]
欢迎各位邻居咨询contact_notestring欢迎各位邻居咨询

这里要区分“地址字段”和“房间字段”。project_nameblock_namebuilding_noroom_no描述的是房源位置;bedroom_countliving_room_count等描述的是户型。真实系统中,地址部分通常要接入地址库做标准化,不能只靠正则把“观澜1街”抽出来就算完成。

room_no也要注意歧义。样例中的“2903”看起来是 29 层 03 号,但在不同楼盘里,它可能表示 2 栋 903 室,也可能是 29 层 03 号。具体含义需要结合项目楼栋规则确认,不要想当然把2903拆成2903两个字段。

1.3 文本里缺失的字段要补什么

这条原始文本并没有包含短租系统需要的全部信息。拆字段时,除了从文本中提取,还要主动补上缺失项。

至少缺少这些字段:

  • 房源唯一 ID:house_code,用于关联订单、价格、日历和平台同步。
  • 价格:日租价、周租价、月租价,分别多少钱,包含哪些费用。
  • 可住人数:每张床对应可住几人,客厅是否有沙发床。
  • 起租天数:月租是否要求至少 30 天,日租是否至少 1 天。
  • 押金和清洁费:是否需要押金,退房清洁费怎么算。
  • 联系人和联系方式:由谁发布,联系方式是什么。
  • 房源状态:上架、下架、停用、审核中。
  • 创建时间和更新时间。

这些字段在设计数据表时都要预留。价格、押金等内容缺失时,可以先允许为空,但不能因为文本没写就不设计对应表结构。否则后续接支付和订单时,要再改表结构,代价会更大。

2. 数据模型要能表达“3室1厅1厨1卫2阳台”和“1.8m+1.5m+1.2m”

2.1 设计房源基础表

先把最核心的房源表设计出来。下面是一份 MySQL 风格的表结构,用来承载样例文本中已经提取到的基础信息。

CREATE TABLE house ( id BIGINT PRIMARY KEY AUTO_INCREMENT, house_code VARCHAR(64) NOT NULL UNIQUE, project_name VARCHAR(128), block_name VARCHAR(128), building_no VARCHAR(32), room_no VARCHAR(32), bedroom_count INT DEFAULT 0, living_room_count INT DEFAULT 0, kitchen_count INT DEFAULT 0, bathroom_count INT DEFAULT 0, balcony_count INT DEFAULT 0, max_guests INT, status VARCHAR(32) DEFAULT 'OFFLINE', source_text TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );

这里有几个关键点。

house_code是业务唯一键,不应该让用户直接输入“碧桂园山湖城观澜1街1座2903”作为唯一键,因为用户可能多写一个空格、少写一个“街”,又或者换一种写法。建议由系统按规则生成,比如BGYYHC-GL-01-2903,并在录入时用地址库或人工确认。

source_text字段很有必要。它保存原始文本,方便以后排查解析问题。解析器改规则后,可以回放历史文本,对比新旧解析结果,减少回归风险。

max_guests没有在原始文本中出现,但它是短租系统必须有的字段。它可以根据床型自动计算,也可以由运营人员手动校正。注意:床型可以决定理论最大人数,但实际能住多少人还会受到沙发、儿童床等因素影响,所以应该单独存储。

2.2 床型和租期模式不能塞进一个字段

床型不能放到house表的一列里。虽然可以存成"1.8m+1.5m+1.2m",但这样的字段无法参与统计。如果运营想统计全平台有大床房的房源数量,或者计算平均床尺寸,字符串字段处理起来非常痛苦。

更合适的做法是建立子表,或者使用兼容 JSON 的数据库字段。如果使用的是 MySQL 5.7 以上版本,也可以用 JSON 类型;但从扩展性和查询灵活性看,独立子表更稳。

CREATE TABLE house_bed ( id BIGINT PRIMARY KEY AUTO_INCREMENT, house_id BIGINT NOT NULL, bed_order INT NOT NULL, size_m DECIMAL(4, 2) NOT NULL, bed_count INT DEFAULT 1, bed_type VARCHAR(32) DEFAULT 'DOUBLE' ); CREATE TABLE house_rental_mode ( house_id BIGINT NOT NULL, mode VARCHAR(16) NOT NULL, price DECIMAL(10, 2), currency VARCHAR(8) DEFAULT 'CNY', min_days INT, max_days INT, is_active BOOLEAN DEFAULT TRUE, effective_date DATE, expired_date DATE, PRIMARY KEY (house_id, mode) );

house_bed表里bed_order用来表示床的顺序,因为展示时通常要保持原始顺序:1.8m、1.5m、1.2m。size_mDECIMAL(4,2),不要用浮点类型存床尺寸和价格,否则计算时容易出现精度问题。

house_rental_mode表把日租、周租、月租拆成独立记录。这样做的原因是:日租、周租、月租往往有不同的价格、折扣和起租天数。如果只在house表里放一个is_day_rentalis_week_rentalis_month_rental布尔字段,后续就没办法表达“日租 300 元/晚,月租 6000 元/月,至少租 30 天”这类完整信息。

2.3 用 JSON Schema 做数据校验

在服务端接收前端提交的房源信息时,可以使用 JSON Schema 做第一道校验。下面是一个针对本例的 JSON 表示。

{ "house": { "house_code": "BGYYHC-GL-01-2903", "project_name": "碧桂园山湖城", "block_name": "观澜1街", "building_no": "1座", "room_no": "2903", "rooms": { "bedroom": 3, "living_room": 1, "kitchen": 1, "bathroom": 1, "balcony": 2 }, "beds": [ { "size_m": 1.8, "count": 1 }, { "size_m": 1.5, "count": 1 }, { "size_m": 1.2, "count": 1 } ], "rental_modes": ["day", "week", "month"] } }

JSON Schema 示例片段:

{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["house"], "properties": { "house": { "type": "object", "required": ["house_code", "rooms", "beds", "rental_modes"], "properties": { "house_code": { "type": "string", "pattern": "^[A-Z0-9\\-]+$" }, "rooms": { "type": "object", "required": ["bedroom", "living_room", "kitchen", "bathroom", "balcony"], "properties": { "bedroom": {"type": "integer", "minimum": 0}, "living_room": {"type": "integer", "minimum": 0}, "kitchen": {"type": "integer", "minimum": 0}, "bathroom": {"type": "integer", "minimum": 0}, "balcony": {"type": "integer", "minimum": 0} } }, "beds": { "type": "array", "minItems": 1, "items": { "type": "object", "required": ["size_m", "count"], "properties": { "size_m": {"type": "number", "minimum": 0.5, "maximum": 3.0}, "count": {"type": "integer", "minimum": 1} } } }, "rental_modes": { "type": "array", "items": { "type": "string", "enum": ["day", "week", "month"] } } } } } }

bedroom这类数量字段设置minimum: 0比较合适,因为有些单间公寓可能没有独立客厅和厨房。size_m的范围可以根据常见床型定义,一般单人床 0.9 米、1.2 米,双人床 1.5 米、1.8 米,最大不会超过 2.2 米。如果出现超过 3 米的尺寸,大概率是数据有问题。

注意:JSON Schema 只能做格式和范围校验,不能保证业务正确。比如某条数据同时标记“可月租”但没有填写月租价格,这类业务一致性需要由服务端代码继续检查。

3. 用 Python 解析器把广告文本自动变成 JSON

3.1 解析前的输入约定

自动解析文本前,先约定输入格式,否则规则会越写越复杂。样例文本算是比较规整的,因为房间和床位信息用数字、量词和分隔符表达。真实业务中还会出现“三室一厅”“1.8米大床+1.5米床”这样的写法。

一个可行的策略是:发布入口尽量使用结构化表单,只把“文本解析”作为辅助录入手段。辅助解析的价值在于减少录入工作量,但结果要落到同一个校验逻辑里。下面先以这条样例文本作为输入:

raw_text = "碧桂园山湖城观澜1街1座2903,3室1厅1厨1卫2阳台,3床:1.8m+1.5m+1.2m,可日租/周租/月租,欢迎各位邻居咨询。"

3.2 正则提取房间、床位、租期等关键信息

这段 Python 代码演示如何从原始文本提取结构,并不代表生产级方案,但足够说明思路。

import re import json def parse_house_text(text: str) -> dict: result = {} room_pattern = re.compile( r"(?P<bedroom>\d+)室" r"(?P<living_room>\d+)厅" r"(?P<kitchen>\d+)厨" r"(?P<bathroom>\d+)卫" r"(?P<balcony>\d+)阳台" ) m = room_pattern.search(text) if m: result["rooms"] = {key: int(value) for key, value in m.groupdict().items()} else: result["rooms"] = None bed_sizes = re.findall(r"(\d+(?:\.\d+)?)m", text) result["beds"] = [{"size_m": float(size), "count": 1} for size in bed_sizes] rental_modes = [] if "日租" in text: rental_modes.append("day") if "周租" in text: rental_modes.append("week") if "月租" in text: rental_modes.append("month") result["rental_modes"] = rental_modes project_match = re.search(r"([\u4e00-\u9fa5A-Za-z0-9]+?)观澜", text) if project_match: result["project_name"] = project_match.group(1) room_match = re.search(r"(\d{3,4})", text) if room_match: result["room_no"] = room_match.group(1) return result if __name__ == "__main__": parsed = parse_house_text(raw_text) print(json.dumps(parsed, ensure_ascii=False, indent=2))

输出结果:

{ "rooms": { "bedroom": 3, "living_room": 1, "kitchen": 1, "bathroom": 1, "balcony": 2 }, "beds": [ { "size_m": 1.8, "count": 1 }, { "size_m": 1.5, "count": 1 }, { "size_m": 1.2, "count": 1 } ], "rental_modes": [ "day", "week", "month" ], "project_name": "碧桂园山湖城", "room_no": "2903" }

正则re.findall(r"(\d+(?:\.\d+)?)m", text)会把文本里所有带m的数字都找出来。这个简单写法在样例里能正确提取 1.8、1.5、1.2,但如果出现“60m² 面积”这类信息,就会误提取。生产环境的正则需要更严格的上下文判断,比如只提取紧跟在“床”或“+”附近的数值。

这里把房间信息放在rooms对象里,而不是拆成bedroom_count等顶层字段,是为了 JSON 展示更清晰。落库时,还是要把它们映射到house表的独立列。

3.3 输出结果和边界情况处理

解析器必须考虑边界情况。常见问题包括:

  • 中文数字:比如“三室一厅”,当前正则匹配不到。
  • 分隔符不统一:有人写1.8m+1.5m+1.2m,有人写1.8m*2,还有人写1.8米和1.5米
  • 房号歧义:2903可能是 29 层 03 号,也可能是 2 栋 903 室。
  • 文本顺序变化:有的文本把“可月租”放在开头,有的放在中间,正则只要定位关键词就不影响。

处理办法不是把所有规则都写进一个巨大的正则,而是先标准化输入,再解析。比如在录入页面规定床尺寸之间用半角加号+分隔,房间数量用阿拉伯数字。对历史数据,可以运行一个一次性清洗脚本,把“三室”转成“3室”,把“1.8米”转成“1.8m”。

关键判断:不要把解析器当成保证数据质量的唯一手段。解析器负责降低录入成本,最终数据质量要靠字段校验、人工确认和定期抽样检查来兜底。

4. 结构化之后才能做发布、计费和房态管理

4.1 生成标准发布文案模板

一旦数据变成结构化,各个发布渠道的展示文案就可以统一生成,不需要各平台分别手写。下面用 Python 生成一套比较标准的发布文案:

def build_publish_text(house: dict) -> str: rooms = house["rooms"] beds = " + ".join( f"{bed['size_m']}m" for bed in house["beds"] ) modes = "/".join(house["rental_modes"]) text = ( f"{house['project_name']} {house.get('block_name', '')}" f"{house.get('building_no', '')}{house.get('room_no', '')}," f"{rooms['bedroom']}室{rooms['living_room']}厅" f"{rooms['kitchen']}厨{rooms['bathroom']}卫{rooms['balcony']}阳台," f"{len(house['beds'])}床:{beds}," f"可{'/'.join(modes)}租,欢迎咨询。" ) return text

这样生成的文本可以保持一致性。但要注意,不同平台的标题长度限制和展示规则不一样。有的平台要求标题不超过 50 字,有的平台允许更宽松。模板化只是基础,还要针对每个平台定义字段拼接规则和长度校验。

4.2 日租/周租/月租的计费逻辑

短租系统的计费不是简单地把日租价乘以天数。日租、周租、月租通常有不同的价格体系。比如日租价是 300 元/晚,周租可能是 1800 元/周,月租可能是 6000 元/月。如果只存日租价,再按“月租价 = 日租价 × 30 × 折扣”计算,遇到淡旺季、含水电费、不含清洁费等情况时,就会非常难维护。

推荐做法是直接在每个house_rental_mode记录中保存对应价格和约束。下面是计算接口的伪代码:

def calc_rental_price(house_id: int, mode: str, days: int) -> float: # 从 house_rental_mode 表查询当前模式记录 # 校验 days 是否满足 min_days 和 max_days # day: 日租价 * days # week: 周租价 * (days // 7) + 日租价 * (days % 7) # month: 月租价 * (days // 30) + 日租价 * (days % 30) pass

示例只做思路说明。真实项目里还要处理月份不固定问题:28 天、29 天、30 天、31 天都不同。如果要精确计算月租,可以按自然月区间处理,比如从 2025-04-10 到 2025-05-09 算一个月,而不是简单除以 30。

4.3 用日历管理房态和订单冲突

日租、周租、月租混合模式下,订单占用的日期粒度不同。日租订单占用一天,周租订单占用连续 7 天,月租订单可能占用 30 天。如果不把订单展开到日期级别,很容易出现两个订单在时间上重叠,但系统没有发现。

一个简单的房态表设计如下:

CREATE TABLE house_calendar ( house_id BIGINT NOT NULL, date DATE NOT NULL, status VARCHAR(16) NOT NULL, order_id BIGINT, PRIMARY KEY (house_id, date) );

当新订单创建后,相关日期写入house_calendar表,状态为BOOKED。检查房源是否可订时,只需要查询目标日期段内是否存在BOOKED记录。月租订单跨月时,要把整个自然月对应的日期全部展开,例如从 4 月 10 日到 5 月 9 日,就写入 30 条日历记录。

这个方案的优点是逻辑简单,缺点是日租、周租、月租比例高的系统,日历表数据量会增长。通常还需要配合索引和分区来优化查询。极端场景下,可以用不展开的区间表加重叠判断,但开发复杂度会更高。

5. 短租系统最常见的三类问题要按链路排查

5.1 床型信息解析错误

现象:录入“1.8m+1.5m+1.2m”,系统只解析出[1.8, 5, 1.2],把1.5当中转文本吃掉了,或者把1.8m+1.5m理解成“1.8 米和 1.5 米”,但最后的1.2m丢失。

可能原因:

  • 正则写得太宽松,优先匹配到错误位置。
  • 输入分隔符不是统一的加号,混用了全角加号、空格、/
  • 床尺寸后面带有其他长度信息,比如“1.8m×2m”被误拆。

检查方式:

  • 打印正则匹配到的中间结果。
  • 准备多组床型用例做单测:1.8m+1.5m+1.2m1.8米+1.5米1.8m*2
  • 检查source_text是否保留了原始文本。

处理建议:

  • 统一录入规范:床尺寸之间只用半角加号。
  • 解析后增加人工确认,或在管理后台展示解析出的床型列表。
  • 对无法识别的文本不强行解析,而是进入待人工处理队列。

5.2 租期价格配置对不上

现象:租客选择“月租”,系统按“日租价 × 30”计算;或者“周租”跨月时,按自然周分段计价,结果和报价不一致。

可能原因:

  • 把租期模式当作布尔字段,只存了“是否支持月租”,没有存月租价格。
  • 计费函数里用固定 30 天代表一个月,忽略了不同月份天数不同。
  • 价格配置已经有生效日期,但查询时没有过滤effective_dateexpired_date

检查方式:

  • 查看house_rental_mode表中该房源的mode = 'month'记录。
  • 检查计费函数传入的起始日期和结束日期。
  • 打印订单快照,看用户下单时看到的报价和最终订单金额是否一致。

处理建议:

  • 每个租期模式独立存价格。
  • 月租按自然月区间计算,不按 30 天硬算。
  • 订单创建时保存价格快照,避免后续改价影响历史订单。

5.3 同一房源多个发布平台状态不一致

现象:运营在后台把房源设置为下架,但外部 OTA 平台仍然展示可预订状态,用户下单后才发现无法入住。

可能原因:

  • 平台只做了单向同步,后台改状态后没有触发推送。
  • 同一个房源在外部平台有不同 ID,没有建立映射关系。
  • 同步任务失败后没有重试和告警。

检查方式:

  • 查看house表的状态字段和外部平台的house_code
  • 查看同步日志中的last_sync_timesync_resulterror_message
  • 在外部平台搜索该房源,确认实际状态。

处理建议:

  • 建立房源平台映射表,保存platform_house_id
  • 增加同步状态表,记录每次同步任务的执行结果。
  • 对同步失败设置重试和告警,避免静默失败。
问题现象可能原因检查方式处理建议
床型解析错误分隔符不统一、正则上下文不严打印正则中间结果,增加单测统一输入规范,解析后人工确认
月租价格错误没有独立保存月租价格查看租金模式表价格按模式独立存储,按自然月计算
多渠道状态不一致同步失败且无重试机制查看同步日志和失败记录增加映射表、状态记录和失败告警

6. 从学习环境到生产环境,短租房源信息模型怎么落地

6.1 学习环境如何快速跑通

如果是学习或验证想法阶段,不需要一上来就引入复杂框架。可以用 Python 加 SQLite 快速把流程跑通。

python -m venv venv source venv/bin/activate pip install pyyaml

然后创建schema.sql,建立househouse_bedhouse_rental_mode三张表,再执行解析脚本,把结构化 JSON 写入 SQLite。这样就能看到一条房源文本从字符串到表记录的全过程。

学习环境的重点是验证解析规则和表结构是否合理,不需要过度关注性能。但至少要做到:多次运行解析脚本不会产生重复数据,或者重复数据能被house_code唯一约束拦住。

6.2 生产环境需要补的工程能力

从学习环境到生产环境,差距不只是换一个数据库。以下这些能力需要补齐:

  • 地址库和地理编码:把“碧桂园山湖城观澜1街1座2903”解析成标准地址和坐标,或者至少与已有的楼盘字典对齐。
  • 房源唯一 ID 生成规则:不要用用户输入文本做主键,要用规则生成并加唯一约束。
  • 权限和审核:房东、店长、客服、运营看到的操作按钮不同,房源上架前需要审核。
  • 日志和监控:记录解析失败率、同步失败率、订单冲突率,出现异常时能及时告警。
  • 回滚和备份:价格、房态、订单表需要保留变更历史,方便追溯。
  • 缓存策略:房源详情页不能每次都全表查,需要缓存热点数据,同时通过版本号或事件失效。
  • 异常重试:外部平台同步、短信通知、支付回调都要有重试和幂等处理。

6.3 从房源字段出发的可复用清单

最后给一份发布前的可复用检查清单,适合在短租或房源管理系统评审时直接使用。

  1. 房源基本信息是否完整:小区、楼栋、房号、户型、朝向、楼层。
  2. 房间字段是否合法:卧室、客厅、厨房、卫生间数量不能为负数,不能超过常见上限。
  3. 床型是否准确:每张床的尺寸、数量、摆放位置是否清楚,是否预留了沙发床。
  4. 租期模式是否可计算:日租、周租、月租是否都有独立价格、起租天数、生效时间。
  5. 联系方式是否按平台规则处理:是否隐藏手机号,是否支持临时虚拟号。
  6. 房源状态是否正确:上架、下架、停用、审核中是否流转正常。
  7. 多渠道同步状态是否正常:每个平台的house_code映射是否存在,最近一次同步是否成功。
  8. 是否保留原始文本:解析出问题时要能回溯到用户填写的原始内容。

单条房源文本只是短租系统里的一个最小输入样例。真正的价值在于把非结构化文本转换成可计算、可追踪、可复用的数据模型,让日期、床型、价格、房态这些关键信息不再依赖人工阅读。接下来可以继续扩展的方向包括自动识别中文数字、接入地址库、生成多平台适配文案、用日历服务处理订单冲突。如果从这条样例开始动手,建议先用一个本地脚本把原始文本解析成 JSON,再把 JSON 写入 SQLite,最后再考虑完整后台。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询