前两天一个邻居发出了灵魂质问:“为什么我买的180平和你的169平看上去一样大?”

“因为咱俩的套内面积都是138平......”

我们去看房子,比较不同楼盘的价格,看的都是单价,可这个单价,却是用(总价 ÷ 建筑面积)计算的。而我们实际买到手里的,是套内面积。

套内面积 = 使用面积+墙体厚度+阳台建筑面积

建筑面积 = 套内面积+分摊面积

(这里要吐槽一下不合理公摊制度,由霍英东发明,大陆1993年从香港引入,但香港2012年就取消了)

所以我们买房子,自然是希望公摊越小越好的,花同样的钱,公摊越小,买到的套内面积越大嘛。

可你知道西安哪个区域的预售新房平均公摊最小吗?于是我又花了三天时间,写了700多行代码做了一个统计,得到下面的结论:

(数据来源:西安住建局官网 数据范围:2021-01-01 ~ 至今)

碑林区公摊最低,周至县公摊最高,高新区第二高

曲江新区,124.5㎡最大套内榜夺冠

建筑面积分布,主力段为100㎡-150㎡

公摊分布 主力段为20%-24%

面积越小,公摊越大,150-160平性价比较高

总体放量平稳

祖传手艺

这个手艺我之前已经在《用1000行代码统计西安新房价格后,我有一个惊人的发现》这篇文章中展示过了,那里我们简单介绍了如何统计最准确的西安新房平均价格。

今天我们用类似的方法,从西安市房建局获取公摊数据,统计西安新房的平均公摊。

幸好当时的代码还在,主要逻辑差不多,但获取难度比上次要大,所以我们还需要将代码稍作改善。

对了,之前有粉丝留言,问我能不能把代码开源?我觉得很刑啊,这是让我的小日子过得越来越有判头了嘛。

我这里只讲技术和思路,作为学习使用,代码的话可以自己实现,一共也就几百行,只要你请求的不是那么的频繁就好。

言归正传,今天我们的页面流程是这样的,和上次的稍有不同:

具体的页面分析我今天就不讲了,和上次差不多,主要讲一下和上次不同的点。

1

我们请求的时候,多了一个区域选择:

可以看到下面的这些区域也是西安的区县,所以我们在请求预售证信息页面的时候,需要加上区域编号,然后去根据区域遍历:

2

由于我们希望统计每个区域的公摊嘛,但是上面“城六区”说的太宽泛,页面上也没有更详细的区域信息。所以我们需要提取“项目坐落”里的区域信息,对地址开头字符和区域做一个映射:

然后就可以根据映射关系获取区域信息了。

3

由于我们的房屋公摊信息,没有直接展示在页面上,需要我们一个一个去点击获取:

所以这次我们要请求的接口量比上次获取价格要大几百倍,我们不能再用单线程请求了,需要上多线程。

我的电脑cpu是20核的,网络请求是典型的IO密集型操作,线程池大小参考值应该设置成2N,N是cpu核数,本场景IO占时间比重更高,所以线程池大小最佳值可能是3N、4N、5N。。。

但是因为我们是学习使用,不能给人家网站增加太大的负担,所以我们只启10个线程就好,等待队列大小10万,而且每请求一栋楼的数据,就让程序休眠10秒:

这样可以让请求时间缩短10倍左右,也不会给网站增加太大压力。

4

因为这次请求的接口过多,网络可能会发生抖动,接口就会调用失败。如果不处理异常,程序就直接停止了,如果忽略异常,数据又会缺失,造成统计不准确。

所以我们还需要调用接口失败时的重试机制。

我选择引入 spring-retry 来做重试机制,同时还需要aop的包:

这样我们就可以在需要进行失败重试的方法上加上 @Retryable 注解了:

这里的意思是,如果发生异常,重试9次,每次间隔1秒、2秒、4秒、8秒。。。如果9次重试都失败了,会调用 @Recover 注释的恢复方法,记录日志,人工处理。

5

这次我们直接计算总页数,不再用上次那种页面返回是空就停止循环的偷懒办法。

直接在首页就可以获取总数量 totalCount:

然后我们利用公式:

totalPageNum = (totalCount + pageSize - 1) / pageSize

已知 pageSize = 10,可以计算出总页数,作为循环的停止条件。

这样也就绕过了上一次说的第一个坑。

6

这个网站的请求参数,不需要我们再单独获取了,而是直接再href参数里,所以我们可以直接获取href参数里的值,去请求下一级接口:

这样也就绕过了上一次说的第二个坑。

7

这个网站的数据一直要追溯到2005年。

而我们并不想统计这么久远的数据,参考价值不大,只想统计2021-2022这两年的数据。

所以我们给循环停止条件上,额外加上时间判断,早于某个时间 after ,就不获取了。这里我设置的是 2021-01-01 。

这里有个小细节,我用了已经不推荐使用的java标签写法,break two;因为我这里嵌套了三层for循环,我只希望跳出其中两层。不推荐这么写,我这里这么写只是因为想提一下这个知识点。

写sql

好了,主要不同就是上面这些,其他的逻辑和上次差别不大,就不赘述了。

最后跑一下程序:

数据库里的数据:

数据库有数据了,大概30w条

接下来就是写sql查询的事了,我们只统计住宅,公寓和商业过滤掉。

西安各区新房平均公摊率:

西安公摊最高的20个新房楼幢:

西安公摊最高的20个新房楼幢:

西安新房各个建筑面积段房源数:

西安新房各个公摊率段房源数:

西安新房各个建筑面积段平均公摊:

西安每个月预售证发证房源数:

ok,到这里就统计完了,如果有想知道其他维度统计结果的,欢迎留言。

最后,愿公摊制度早日取消。

我分析30w条数据后发现,西安新房公摊最低的竟是这里?的更多相关文章

  1. 关于BufferedWriter.write超过30W条数据写入过慢问题。

    原创文章,转载请注明出处! ------------------------------------------------------------ 今天接到一个项目需求变更,是关于从数据库查询到30 ...

  2. easyui datagrid 每条数据后添加操作按钮

    easyui datagrid 每条数据后添加“编辑.查看.删除”按钮 1.给datagrid添加操作字段:字段值 <table class="easyui-datagrid" ...

  3. sql server中新增一条数据后返回该数据的ID

    开发中遇到的问题:在新增一条数据后往往不需要返回该数据的ID,但是有的时候可能需要返回该数据的ID以便后面的编程使用. 在这里介绍两种方法: 其一:使用存储过程: create procedure a ...

  4. 关于如何在mysql中插入一条数据后,返回这条数据的id

    简单的总结一下如何在mysql中出入一条数据后,返回该条数据的id ,假如之后代码需要这个id,这样做起来就变得非常方便,内容如下: <insert id="insertAndGetI ...

  5. SQL Server 2008 R2【SET ANSI_PADDING填充属性】插入一条数据后,为何每一列都默认的在字符后多了几个空格

    当加入空格后查出 解决: 导致出现这样的现象的原因就是SET ANSI_PADDING选项. 这个选项只在数据表的字符串字段被更新或者新的数据行插入到表中的时候作用.它控制着SQL Server在遇到 ...

  6. hibernate向mysql插入数据后,得到该条数据主键的方法

    hibernate向MySQL插入一条数据后,得到该条数据主键的方法.主键是自增长的. 保存完成后,直接用该实体的getId的方法就可以得到.因为保存完成后,hibernate会自动将id赋值给实体. ...

  7. mysql插入数据后返回自增ID的方法

    mysql和oracle插入的时候有一个很大的区别是,oracle支持序列做id,mysql本身有一个列可以做自增长字段,mysql在插入一条数据后,如何能获得到这个自增id的值呢? 方法一是使用la ...

  8. mysql insert一条记录后怎样返回创建记录的主键id,last_insert_id(),selectkey

    mysql插入数据后返回自增ID的方法 mysql和oracle插入的时候有一个很大的区别是,oracle支持序列做id,mysql本身有一个列可以做自增长字段,mysql在插入一条数据后,如何能获得 ...

  9. 【JAVA - SSM】之MyBatis插入数据后获取自增主键

    很多时候,我们都需要在插入一条数据后回过头来获取到这条数据在数据表中的自增主键,便于后续操作.针对这个问题,有两种解决方案: (1)先插入,后查询.我们可以先插入一条数据,然后根据插入的数据的各个字段 ...

随机推荐

  1. Ubuntu16.04编译OpenJDK7u40

    下图是OpenJDK6.OpenJDK7.OpenJDK7u和OpenJDK8这几个项目之间的关系: 下面把编译步骤记录下来: 编译环境 Ubuntu 16.04.4 LTS jdk版本 openjd ...

  2. 随笔总结:8086CPU的栈顶超界问题

    我们学习编程都知道栈的超界限问题是非常严重的问题,他可能会覆盖掉其他数据,并且我们不知道这个数据是我们自己保存的用于其他用途的数据还是系统的数据,这样常常容易引发一连串的问题. 在学习汇编的时候,我们 ...

  3. Ubuntu的一些软件源

    参考别人的,自己记录一下,怕丢失 修改方法:vim /etc/apt/sources.list,然后添加下面对应的代码区 台湾的官方源 deb http://tw.archive.ubuntu.com ...

  4. [C++STL] 迭代器 iterator 的使用

    定义 迭代器是一种检查容器内元素并遍历元素的数据类型,表现的像指针. 基本声明方式 容器::iterator it = v.begin();//例:vector<int>::iterato ...

  5. Vue项目中的接口进阶使用

    创建services文件夹 1.文件夹apis.index.request的三个文件. 2.apis文件放接口 export const apis = { checkDeviceNo: '/api/c ...

  6. 直观比较 popcount 的效率差异

    问题 求 \(\sum\limits_{i=1}^{3\times 10^8} popcount(i)\) . 仅考虑在暴力做法下的效率. 枚举位 __builtin_popcount #includ ...

  7. 渗透开源工具之sqlmap安装配置环境变量教程

    由于计算机安全牵涉到很多方面,建议自己在服务器上搭建自己的靶场,如何搭建靶场请订阅并查看作者上期教程,这里作者先为大家推荐一个免费开源升级靶场:https://hack.zkaq.cn/   在封神台 ...

  8. NPM Error:gyp: No Xcode or CLT version detected!

    问题 最近在macOS Catalina中使用npm安装模块,经常会出现如下错误: > node-gyp rebuild No receipt for 'com.apple.pkg.CLTool ...

  9. 1.设计模式第一步-《设计模式从头到脚舔一遍-使用C#实现》

    更新记录: 完成第一次编辑:2022年4月23日20:29:33. 加入小黄人歌曲:2022年4月23日21:45:36. 1.1 设计模式(Design Pattern)是什么 设计模式是理论.是前 ...

  10. 数字格式化的 js 库

    数字格式化的 js 库 Numeral.js,是一个用于格式化数字和处理数字的 js 库. Tip:目前 Star 有 9.2k,5年以前就没有在更新.其文档写得不很清晰,比如它提供了多语言,但如何切 ...