Python爬虫实战:中国土地市场网公告数据采集与增量更新实现
简介资源是一套面向Python爬虫学习者与土地数据研究者的实战项目围绕中国土地市场网土地公告公示信息实现自动化采集、解析与分布式存储。项目针对公告页面动态加载、多字段提取及大量链接去重等难点给出完整代码与配置方案帮助读者快速搭建属于自己的土地数据抓取流程。压缩包共7个文件大小约300KB包含2个Python爬虫脚本、3个XML工程配置、1个XLS格式的结果样例及1个IDE工程描述文件既能查看解析逻辑也能直接查看抓取后的结构化数据。代码中演示了如何利用常用爬虫库模拟浏览器获取页面通过选择器或XPath提取公告标题、时间、位置、面积等关键信息并将待抓取任务写入Redis队列实现多节点分布式协作与去重显著提升采集效率。目前已有2499人学习下载适合网络爬虫初学者进阶、土地研究人员获取数据样本以及想了解分布式爬虫架构的开发者学习参考。 算上列表明细光中国土地市场网一个站点的公告信息就能撑起一个区域的拿地情报分析。这个项目其实不复杂但绝对是个典型的“看着简单、做起来一堆细节”的爬虫工程。我花了一周左右把整套流程跑通从列表页到详情页从单次抓取到增量更新中间踩了不少坑这篇就把整个实现思路和完整代码拆开讲清楚。1.1 这个网站为什么值得爬中国土地市场网landchina.com主要发布国有建设用地使用权的出让公告、成交公示、供地计划等官方信息。对于做土地评估、房地产投资、市场研究的朋友来说这些数据是硬通货——比如一个区域近期有哪些地块挂牌、起拍价多少、最终成交楼面价多少都能直接反映市场热度。人工一条条翻网页不是不行但数据量大、更新频繁而且公告字段多地块编号、坐落位置、出让面积、容积率、起始价、保证金等复制粘贴容易出错一套可复用的爬虫脚本能省下大量重复劳动。1.2 项目范围与预期产出这次的目标很明确把网站上“土地出让公告”和“成交公示”两类核心信息抓下来保存为结构化数据。字段至少包括公告标题、公告类型、行政区、发布时间、公告链接、地块编号、出让面积、起始价、成交价等。产出物是 CSV 文件和 SQLite 数据库方便后续用 Excel 透视表或者直接 SQL 查询做分析。整个过程基于 Python 3.9 requests parsel 实现不需要 Selenium因为目标站点的数据是服务端渲染的直接请求 HTML 就能拿到内容效率更高。2. 技术选型与合规边界2.1 为什么选 requests parsel 而不是 Scrapy很多人一上来就上 Scrapy但我觉得这个项目用 requests parsel 反而更顺手。原因是目标网站结构相对规整不需要分布式爬取单机单线程足够用 Scrapy 的中间件、Pipeline 反而增加学习成本。requests 负责发 HTTP 请求parsel 负责解析 HTML它的 XPath 选择器跟 Scrapy 是同源的以后真要换 Scrapy解析逻辑可以直接迁移。对于这种中小规模采集任务轻量组合更灵活。2.2 合规性提醒别碰红线爬虫合规这块务必要重视。中国土地市场网的数据本身是政府公开公示信息目的在于公众查询和监督爬取公开公告内容用于个人研究、行业分析是没问题的但需要注意三件事请求频率务必克制。建议每次请求间隔 2~3 秒不要短时间高频并发以免对服务器造成压力。不采集非公开数据。登录后才可见的接口、后台管理接口一律不碰。只爬公开页面。数据使用需自律。抓下来的数据用于个人学习和市场分析不要用于商业转售或侵犯网站权益的用途。提示爬虫工具本身是中性的关键看使用方式和目的。遵守 robots.txt、设置合理限速、尊重目标站点服务能力是每个爬虫开发者应有的基本素养。3. 站点结构分析与核心字段提取3.1 分析列表页 URL 规律打开中国土地市场网找到“土地供应计划”或“出让公告”栏目后能看到一个列表页展示公告标题、所在地区、公告类型、发布时间等信息。直接浏览器的开发者工具F12看网络请求会发现列表页的 URL 是带查询参数的核心参数一般包括当前页数page每页条数limit行政区编码region公告类型category这意味着列表页的翻页非常方便——只需要循环修改 page 参数就能拿到所有分页数据。这一点对爬虫来说是最理想的情况不需要模拟点击不需要处理复杂的 JavaScript 渲染。3.2 详情页是数据金矿列表页只有标题和发布时间真正的核心数据在详情页里包括地块编号、土地面积、容积率、建筑密度、起始价、保证金、成交价等关键字段。所以爬虫思路是第一步抓列表页拿到公告链接第二步逐个访问详情页抽取结构化字段第三步汇总存储。这个“两段式”设计几乎是所有公告类网站的通用打法。3.3 字段映射表字段名列表页可取详情页可取说明公告标题是是用于去重和展示公告类型是是出让公告 / 成交公示所在地区是是省市区级联用行政区字段发布时间是是列表页就有的摘要时间公告链接是否作为唯一标识地块编号否是详情页核心字段出让面积否是带单位需清洗起始价否是金额字段需转型容积率否是部分公告里有成交价否是仅成交公示有表中可以看到列表页承担“发现”详情页承担“提取”两者配合才能拿到完整的数据。4. 核心代码实现从列表到详情的完整链路4.1 请求 session 与公共头设置import requests from parsel import Selector import time import pandas as pd import sqlite3 import random 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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Referer: https://www.landchina.com/, } session requests.Session() session.headers.update(HEADERS)这里统一用一个 Session 对象有三个好处自动携带 Cookie、保持连接复用TCP 连接复用能减少握手开销、header 统一管理。User-Agent 务必设置成常见浏览器的完整 UA否则部分反爬策略会直接拒绝响应。Referer 也建议设置模拟从首页点进来的正常访问路径。4.2 列表页抓取与翻页控制def fetch_list_page(page): list_url https://www.landchina.com/default.aspx params { tabid: 261, paging: str(page), page: str(page), gonggaoleixing: , province: , city: , district: , } resp session.get(list_url, paramsparams, timeout15) resp.encoding utf-8 if resp.status_code ! 200: print(f第{page}页请求失败状态码: {resp.status_code}) return [] sel Selector(resp.text) items [] # 这里需要根据实际页面结构调整选择器 rows sel.xpath(//table[contains(class, table)]/tr) for row in rows: title row.xpath(.//a/text()).get().strip() link row.xpath(.//a/href).get().strip() publish_time row.xpath(.//td[last()]/text()).get().strip() items.append({ title: title, link: https://www.landchina.com link, publish_time: publish_time, }) return itemsXPath 选择器必须根据目标站点实际 HTML 结构调整。我第一次跑的时候就是直接照搬想象的结构结果解析出来全是空列表。调试技巧很简单先用浏览器把列表页源码复制下来用 parsel 慢慢调 XPath确认能拿到数据再写循环。这里分享一个心得//table[contains(class, table)]/tr这种写法可以避免 class 名称的精确匹配问题因为很多网站的 class 会有动态后缀。翻页控制上建议加一个动态间隔避免固定频率被识别for page in range(1, 21): items fetch_list_page(page) # 处理当前页... time.sleep(random.uniform(2.0, 4.0))不要小看这个随机延时。固定间隔 2 秒的请求模式在服务端日志里看就是一串规律电波很容易被识别为自动化脚本而 2~4 秒的随机抖动更接近人工浏览的节奏。另外如果某一页返回空数据建议直接中断分页循环因为公告类网站的分页一般是有终点的。4.3 详情页解析与字段清洗详情页的解析是整个项目的核心。公告详情页的结构通常是一个信息表格左侧是字段名右侧是字段值。这种结构非常适合用 XPath 的属性配对逻辑来提取。def fetch_detail(url): resp session.get(url, timeout15) resp.encoding utf-8 sel Selector(resp.text) result {url: url} # 公告标题 result[title] sel.xpath(//h1/text() | //div[contains(class, title)]/text()).get().strip() # 以地块编号为例抓取表格中对应行 row sel.xpath(//tr[td//text()[contains(., 地块编号)]]) if row: result[block_no] row.xpath(.//td[2]//text()).get().strip() # 出让面积 row sel.xpath(//tr[td//text()[contains(., 出让面积)]]) if row: result[area] row.xpath(.//td[2]//text()).get().strip() # 起始价 row sel.xpath(//tr[td//text()[contains(., 起始价)]]) if row: result[start_price] row.xpath(.//td[2]//text()).get().strip() # 成交价 row sel.xpath(//tr[td//text()[contains(., 成交价)]]) if row: result[deal_price] row.xpath(.//td[2]//text()).get().strip() return result这个“按字段名定位行取第二列值”的思路能够极大提高字段提取的鲁棒性。即使字段顺序换了只要表格结构是“名称-值”模式就不会出错。但这里有一个隐藏的坑contains(., 地块编号)可能匹配到“地块编号”和“地块编号2”两个节点导致取到的值不对。解决办法是用精确匹配或者检查结果是否包含逗号、单位等特征再进行正则清洗。4.4 字段清洗函数def clean_area(text): 清洗面积字段保留数字 import re match re.search(r(\d\.?\d*), text.replace(,, ).replace(, )) return float(match.group(1)) if match else None def clean_price(text): 清洗价格字段单位可能为万元 match re.search(r(\d\.?\d*), text.replace(,, ).replace(, )) if match: return float(match.group(1)) return None清洗逻辑看起来简单但实际踩坑最多的地方就在这。因为网站上很多数字是全角逗号或中文逗号分隔比如“4,520.00”中间那个逗号是英文的但“4520.00”中间可能是中文逗号、甚至是全角空格。所以清洗的第一步一定是把各种逗号统一替换掉再做正则匹配。价格字段的单位也要注意有的是“万元”有的是“元/平方米”清洗时至少要把单位原样保留一份避免后续分析时搞混量纲。5. 数据存储与增量更新5.1 SQLite 建表与插入数据量不大没必要上 MySQLSQLite 单文件就能解决方便迁移和备份。建表语句如下conn sqlite3.connect(land_data.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS land_announcement ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, block_no TEXT, area REAL, start_price REAL, deal_price REAL, publish_time TEXT, url TEXT UNIQUE, crawl_time TEXT ) ) conn.commit()URL 字段加 UNIQUE 约束是实现增量更新的关键。每次插入前直接用INSERT OR IGNORE如果 URL 已存在就自动跳过这样重复抓取同一页不会产生脏数据。这个方案比“先查再插”的写法规整很多也更快。5.2 增量爬取策略增量更新的核心问题是如何知道哪些公告是新增的哪些已经入库我的做法是维护一个“已抓取链接”集合每次跑之前先查数据库已有的 URL然后只处理新链接。但还有一种更轻量的方式按发布时间过滤。列表页上有公告发布时间只要记录上次爬取的时间点只抓这个时间之后发布的公告即可。def get_existing_urls(cursor): cursor.execute(SELECT url FROM land_announcement) return set(row[0] for row in cursor.fetchall()) existing get_existing_urls(cursor) for item in items: if item[link] not in existing: detail fetch_detail(item[link]) save_to_db(detail)如果列表页有明确的发布时间建议直接按“今天发布”的公告来筛这样每天定时跑一次增量更新效率极高不会爬到重复内容。5.3 定时任务自动化手动跑脚本没意义真正有价值的是每天自动抓取。我用系统的 crontab 实现定时调度每天上午 9 点定时爬取前一天的公告数据0 9 * * * cd /path/to/project /usr/bin/python3 crawler.py crawler.log 21Windows 环境下可以用任务计划程序或者直接用 APScheduler 在 Python 内部实现定时调度。我实际跑的时候用的是 crontab因为服务器是 Linux 云主机日志重定向也方便排查问题。6. 常见问题与排查技巧实录6.1 问题速查表现象可能原因解决方法返回 403 或 302请求头不全被 WAF 拦截补全 UA、Referer检查是否触发频率限制列表页解析为空页面结构调整XPath 失效用真实 HTML 调试选择器不要猜结构详情页某些字段缺失部分公告不包含该字段用.get()兜底加 try/except中文乱码站点更新了编码声明先resp.apparent_encoding检测再强制指定抓了几页后报超时频率过高被限流增加随机 sleep必要时换代理 IP数据重复缺少去重机制给 URL 字段加 UNIQUE 约束6.2 实战踩坑动态加载导致的“假列表”我自己踩过一个大坑以为列表页是纯静态的结果仔细看网络请求才发现翻页用的参数不是普通的page而是 POST 请求体里的一个加密字段。当然这是稍复杂的情况后来我通过在页面底部找到分页参数规律把 POST 改成对应的参数模拟同样能解决。核心经验是不要想当然认为网站结构跟你预期一致一切以浏览器的网络请求面板为准。我第一次写的时候没有看网络请求就直接用 GET 请求访问列表页结果返回的 HTML 里确实有表格但翻页怎么都不生效原因就是实际翻页走的是 POST 接口。后来老老实实打开 F12点翻页按钮看请求方式、请求头和表单数据才找到了正确路径。6.3 如何应对页面结构调整这应该是所有爬虫项目逃不开的宿命网站改版后原来的 XPath 全部失效。应对思路有三个层次选择器等定位逻辑尽量写宽泛一些比如用contains(class, title)而不是精确匹配classtitle这样页面微调时不容易挂。记录抓取日志和失败率。比如每隔一段时间统计解析为空的详情页数量一旦异常增多立刻检查选择器是否需要更新。核心字段提取失败时直接把详情页 HTML 存一份快照方便事后离线调试不至于重跑整个流程。我专门写了一个 debug 函数当某个字段解析不到时把整个 HTML 落盘到debug/目录文件名带上时间戳。这样即使第二天醒来看代码也能快速定位问题。跑完整个流程后我自己留了一套可以复用的爬虫模板列表页用统一的翻页循环详情页用字段-值表格的通用解析函数入库统一走INSERT OR IGNORE。这种结构在应对其他政府公开信息网站时也很通用——规划公示、招投标公告、成交信息公开基本都能套用同一套打法。最后提醒一点爬虫代码写好之后不要只顾着跑一定给每个请求设置超时和重试否则任何一次网络抖动都会让整个任务挂掉。多踩几次坑把这些边界情况都补上之后这个爬虫才能真正稳定地每天自动运行变成一个可靠的数据源。本文还有配套的精品资源点击获取

相关新闻

给Quartz.Net定时任务配个管理UI:从设计到落地

给Quartz.Net定时任务配个管理UI:从设计到落地

简介:这是一套基于 .NET Core 3.1 与 Quartz.Net、Vue、IView 构建的开箱即用定时任务管理界面,面向需要在 Web 端快速配置和监控作业的 .NET 开发者。该方案不依赖数据库,只需在界面简单配置即可完成作业新建、修改、启停与日志查看&#xf…

2026/9/4 21:54:45 阅读更多 →
.NET Core定时任务管理:Quartz.Net UI可视化部署实战

.NET Core定时任务管理:Quartz.Net UI可视化部署实战

简介:一套基于.NetCore 3.1与Quartz.Net、前端采用Vue和IView构建的定时任务管理UI方案,面向需要快速集成分布式调度能力的.NET开发者。无需数据库,仅通过界面完成作业创建、修改、日志查看等配置,附带run.bat可直接运行&#xff…

2026/9/4 21:55:19 阅读更多 →
用Three.js打造3D爱心动画:从数学建模到粒子渲染的完整实践

用Three.js打造3D爱心动画:从数学建模到粒子渲染的完整实践

简介:一份基于HTML5与CSS3的3D爱心动画特效源码,适合前端开发者、设计爱好者及需要在活动页面中快速融入浪漫交互氛围的人群。整个压缩包仅3KB,共包含7个文件:css样式文件、haml模板、html页面、scss源文件、txt说明文档及ruby配置…

2026/9/4 21:22:48 阅读更多 →

最新新闻

别让AI太顺滑:AI编程与Agent落地中的验证、审计和回滚控制

别让AI太顺滑:AI编程与Agent落地中的验证、审计和回滚控制

AI 编程工具可以让我在十分钟内生成一个接口的骨架,AI Agent 能自动整理邮件、调用 API、批量产出文件。这种“无摩擦”体验,对开发者的吸引力确实很大。真正进入 AI 应用开发和模型部署后,我却见过大量事故:代码能跑但没人说得清…

2026/9/4 23:03:37 阅读更多 →
Grok 4.6开发者工具链实战:CLI安装与API集成指南

Grok 4.6开发者工具链实战:CLI安装与API集成指南

这次我们来看的,不是又一条模型资讯,而是围绕 “Grok 4.6 上牌桌” 这件事在工程侧带来的连锁反应。标题里其实装了两条线:一条是模型本身的能力迭代,另一条是“还差一部《奥德赛》”所指的生态工具缺口。对技术人来说&#xff0c…

2026/9/4 23:03:37 阅读更多 →
WPF自定义MessageBox:基于消息中介模式实现UI与业务逻辑解耦

WPF自定义MessageBox:基于消息中介模式实现UI与业务逻辑解耦

简介:本资源是一个面向WPF开发者的自定义消息框解决方案,适用于需要解耦UI与业务逻辑的中高级C#开发者,解决传统MessageBox界面僵化、难以定制及跨项目复用困难的问题。资源包共21个文件,包含9个C#逻辑类(如MessageBox…

2026/9/4 23:03:37 阅读更多 →
苹果 · 颜色/容量/型号查询API

苹果 · 颜色/容量/型号查询API

通过该API接口可以查询到苹果设备的型号、颜色、容量信息。 一、使用API 1、请求信息 (1)请求地址: https://open-api.51gcc.com (2)请求方法: POST (3)请求参数 参数名 是否必填…

2026/9/4 23:03:37 阅读更多 →
数字员工与语音智能体是什么?它们在企业运营中的主要价值与功能有哪些?

数字员工与语音智能体是什么?它们在企业运营中的主要价值与功能有哪些?

数字员工通过优化业务流程,实现了企业在降本提效方面的显著成效。它们能够自动处理大量的重复性任务,从而解放人力资源,使员工可以专注于更具战略性的工作。以语音智能体为例,这一工具不仅能及时获取和分析客户反馈,还…

2026/9/4 23:03:37 阅读更多 →
python的图论工业场景模拟第六十九篇:设备物理坐标映射与布局可视化规范,任务:给设备网络的每个节点添加实际的物理坐标属性,并使用kamada-kawai布局算法,生成一张清晰无重叠的拓扑可视图,奠

python的图论工业场景模拟第六十九篇:设备物理坐标映射与布局可视化规范,任务:给设备网络的每个节点添加实际的物理坐标属性,并使用kamada-kawai布局算法,生成一张清晰无重叠的拓扑可视图,奠

设备物理坐标映射与布局可视化规范:给工业拓扑一张"看得懂"的图 "车间有 60 台设备,我们好不容易把拓扑关系理清楚了——谁连谁,一清二楚。但画出来的图却是一团乱麻:节点叠在一起分不清,边交叉得像蜘蛛…

2026/9/4 23:02:36 阅读更多 →

日新闻

ESP32S2嵌入式收音机全栈开发实战指南

ESP32S2嵌入式收音机全栈开发实战指南

简介:本资源是一个基于ESP32-S2芯片的嵌入式综合实践项目,面向本科毕业设计、课程设计及实训开发人员,聚焦网络收音机与FM收音机双模功能实现,融合ESP-IDF框架、ESP-ADF音频开发库与LVGL图形界面库,具备完整软硬件协同…

2026/9/4 0:00:28 阅读更多 →
WorkBuddy+Python实战:从零搭建商品库存管理系统

WorkBuddy+Python实战:从零搭建商品库存管理系统

最近想自己动手做一个“商品库存管理系统”的人变多了。很多开网店、做小团队ERP选型、或者刚学Python的读者,不是不想用系统,而是被传统开发路径劝退了:要装数据库,要写后端接口,要学前端页面,还要考虑多人…

2026/9/4 0:00:28 阅读更多 →
旅游情感分析:基于Python的垂直场景深度解析

旅游情感分析:基于Python的垂直场景深度解析

简介:本资源是一份面向计算机专业本科生的毕业设计实践项目,聚焦旅游行业真实场景,解决旅游平台对用户评论情感倾向自动识别与管理的需求。系统基于Python 3.9.11与Anaconda环境构建,集成携程、马蜂窝双平台爬虫模块,并…

2026/9/4 0:00:28 阅读更多 →

周新闻

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

每年校招季我都会接触不少准备数据库方向笔试的同学,看到最多的状态就是:简历上写着“熟悉 MySQL”“了解索引优化”,一碰到数据库管理工程师的笔试卷,却在索引、事务、锁、备份恢复这些题目上翻车。网易这套 2018 校园招聘数据库…

2026/9/4 10:54:27 阅读更多 →
数字电路时序基石:深入理解建立时间与保持时间

数字电路时序基石:深入理解建立时间与保持时间

1. 这不是“背公式”的事:时间参数到底在约束什么你翻过数字电路教材,一定见过这两个词:建立时间(Setup Time)和保持时间(Hold Time)。它们常被并列写在触发器(Flip-Flop&#xff09…

2026/9/4 14:20:02 阅读更多 →
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

1. 项目缘起:从赛题到超声波测距机的诞生第八届蓝桥杯单片机设计与开发国赛的题目,我至今记忆犹新。它没有直接给出一个花哨的名字,而是用“超声波测距机”这个朴实无华的功能描述,精准地勾勒出了考核的核心。对于当时备赛的我而言…

2026/9/4 20:51:50 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/3 4:17:49 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/3 4:18:56 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/4 9:37:01 阅读更多 →