python在租房过程中的应用

总第84篇

01|背景介绍:

租房是再普遍不过的一件事情了,我们在租房过程中常考量的两个因素是出租房离公司的远近以及价格,而我们一般都会去链家上看相应的信息,但是链家网只有价格没有距离,对于我这种对帝都不是很熟的人,对各个区域的位置是一脸懵逼,所以我就想着能不能自己计算距离呢,后来查了查还真可以。具体做法就是先获取各个出租房所在地的经纬度和你公司所在地的经纬度,然后进行计算即可。

我们在获取经纬度之前首先需要获取各个出租房所在地的名称,这里获取的方法是用爬虫对链家网上的信息进行获取的。关于爬虫可以先看看这几篇:

爬虫进阶(一)

爬虫进阶(二)

爬虫进阶(三)

爬虫进阶(四)

02|Xpath介绍:

以前解析都是用的BeautifulSoup和正则表达式,见到网上有人说自从用了Xpath以后再也不想用BS了,所以决定这次来尝试一下。

2.1Xpath是什么

XPath 是一门在XML文档中查找信息的语言。XPath 可用来在XML文档中对元素和属性进行遍历。

Xpath是在文档中查找信息的,我们在之前用过的BeautifulSoup也是可以用来在文档中查找信息的。这两者有什么不一样呢,我们来看看。

我们看看这两种方式具体查找信息的过程。

BeautifulSoup在查找信息时,需要利用BeautifulSoup(html,”lxml”)对requests.get()得到的内容进行解析得到一个BeautifulSoup对象soup,然后再利用BeautifulSoup的一些方法去获取对应的信息。

Xpath在查找信息的时候,也是需要先对requests.get()得到的内容进行解析,这里是用lxml库中的etree.HTML(html)进行解析得到一个对象dom_tree,然后利用dom_tree.Xpath()方法进行获取对应的信息。

(疑问:既然Xpath的目标对象是XML,而BeautifulSoup也有XML的解析器,是不是可以用BeautifulSoup的xml进行解析得到xml文档,然后再用Xpath?我试了下好像不可以,同样的response,用xml进行解析数据为空,也不知道是什么原因,是不是这种思路就有问题,求指正。)

关于BeautifulSoup的几种解析器可以看:http://www.cnblogs.com/KoalaDream/p/4706316.html

这里面有提到两种文件类型XML和HTML,那么这两者又有什么区别。 http://blog.csdn.net/liu_yujie2011com/article/details/20284453

03|Xpath怎么用:

Xpath最常用的几个符号就是“/”、“//”这两个符号,“/”表示该标签的直接子节点,就比如说一个人的众多子女,而“//”表示该标签的后代,就比如说是一个人的众多后代(包括儿女、外甥、孙子之类的辈分)。

更多详细内容这里就不Ctrl C/V了,大家直接看官方教程就好啦:http://www.w3school.com.cn/xpath/xpath_syntax.asp 。

04|数据抓取:

在前面也说过,我们本次抓取的流程是先获得url,然后利用requests.get()获得html,然后再利用lxml库中的etree.HTML(html)进行解析得到一个对象dom_tree,然后利用dom_tree.Xpath()方法进行获取对应的信息。

先分析目标网页url的构造,链家网的url构造还是很简单的,页码就是pg后面的数字,在租房这个栏目下一共有100页,所以我们循环100次就好啦。

还有就是明确我们要获取的信息,在前面我们说了是要研究公司附近的租房,但是我们在租房的时候也不是仅仅考虑距离这一个因素,这里我准备获取标题、价格、区域(大概在哪一块)、看房人数(说明该房的受欢迎程度)、第一特征(可以说是该房的一个优势点,有的会写离地铁近,有的会写随时看房之类的),更新日期(判断你看到的信息是不是该房源的最新动态),楼层情况(高楼层还是低楼层),房租建筑时间等等。(就是你能看到的信息差不多都要弄下来哈哈)。

#导入相关库
from lxml import etree
import requests
from requests.exceptions import ConnectionError
import pandas as pd
#获取目标网页的url
def get_page_index():
    base="https://bj.lianjia.com/zufang/pg"
    for i in range(1,101,1):
        url=base+str(i)+"/"
        yield url#yield为列表生成器

得到目标网页的url后,对其进行解析,采用的方法是先用lxml库的etree对response部分进行解析,然后利用xpath进行信息获取。

#请求目标网页,得到response
def get_page_detail(url):
    try:
        response=requests.get(url)
        if response.status_code==200:
            return etree.HTML(response.content.decode("utf-8"))
            #lxml.etree.HTML处理网页源代码会默认修改编码
        return None
    except ConnectionError:
        print ("Error occured")
        return None
#解析目标网页
#title为房屋标题;name为小区名称;catogery为房屋类别(几室几厅)
#size为房屋大小;region为区域;PV为看房人数;
#second_feature为高低楼层;third_feature为房屋建筑时间
def parse_page_detail(dom_tree):
    try:
        title=dom_tree.xpath('//li/div[2]/h2/a/text()')
        name=dom_tree.xpath('//li/div[2]//div/a/span/text()')
        catogery=dom_tree.xpath('//li/div[2]//div//span[1]//span/text()')
        size=dom_tree.xpath('//li/div[2]//div//span[2]/text()')
        region=dom_tree.xpath('//li/div[2]//div[1]//div[2]//div/a/text()')
        PV=dom_tree.xpath("//li/div[2]//div[3]//span[@class='num']/text()")
        price=dom_tree.xpath("//li/div[2]//div[2]//span[@class='num']/text()")
        date=dom_tree.xpath("//li/div[2]//div[2]//div[@class='price-pre']/text()")
        first_feature=dom_tree.xpath('//li/div[2]//div[1]//div[3]//span[@class="fang-subway-ex"]/span/text()')
        other=dom_tree.xpath('//li/div[2]//div[1]//div[2]//div/text()')
        name1=[]
        catogery1=[]
        size1=[]
        second_feature=[]
        third_feature=[]
        for n in name:
            name2=n[0:-2]
            name1.append(name2)
        for c in catogery:
            catogery2=c[0:-2]
            catogery1.append(catogery2)
        for s in range(0,59,2):
            size2=size[s][0:-2]
            size1.append(size2)
            second_feature1=other[s]
            second_feature.append(second_feature1)
        for m in range(1,60,2):
            third_feature1=other[m]
            third_feature.append(third_feature1)
        return {
            "title":title,
            "name":name1,
            "catogery":catogery1,
            "size":size1,
            "region":region,
            "price":price,
            "PV":PV,
            "second_feature":second_feature,
            "third_feature":third_feature,
            "other":other      
        }
    except:
        pass
#对获得目标内容进行整理导出
#建立一个空的DataFrame
df1=pd.DataFrame(columns=["title","name","catogery", "size","region","price","PV",
        "second_feature","third_feature","other" ])
i=0
if __name__=="__main__":
    urls=get_page_index()
for url in urls:
    dom_tree=get_page_detail(url)
    result=parse_page_detail(dom_tree)
    df2=pd.DataFrame(result)
    df1=df1.append(df2,ignore_index=False,verify_integrity=False)
    i=i+1
    print(i) #打印出目前爬取的页数  
#保存数据到本地
df1.to_csv("D:\\Data-Science\\Exercisedata\\lianjia\\result.csv")
print(df1.info)#打印出表的基本信息
df1.head(3)#预览前3行

df1.info(通过下图可以看出,我们一共抓取到2970条房屋信息)。

df1.head(3),预览一下表的基本构成。

05|经纬度的获取:

我们刚刚只是获取了一些出租房的基本信息,但是我们要想计算距离还需要获得这些出租房所在的地理位置,即经纬度信息。

这里的经纬度是获取的区域层级的,即大概属于哪一个片区,本次爬取的2970条房屋信息分布在北京的208个区域/区域。

关于如何获取对应地点的经纬度信息,这里感谢雨哥提供方法,利用的XGeocoding_v2工具,具体的获取方法点击:

https://mp.weixin.qq.com/s/2Y92oxDUnR5VaT2E2Adowg

得到的如下的结果:

06|距离的计算:

#经纬度的计算函数
# input Lat_A 纬度A
# input Lng_A 经度A
# input Lat_B 纬度B
# input Lng_B 经度B
# output distance 距离(km)
def calcDistance(Lat_A, Lng_A, Lat_B, Lng_B):
    ra = 6378.140  # 赤道半径 (km)
    rb = 6356.755  # 极半径 (km)
    flatten = (ra - rb) / ra  # 地球扁率
    rad_lat_A = radians(Lat_A)
    rad_lng_A = radians(Lng_A)
    rad_lat_B = radians(Lat_B)
    rad_lng_B = radians(Lng_B)
    pA = atan(rb / ra * tan(rad_lat_A))
    pB = atan(rb / ra * tan(rad_lat_B))
    xx = acos(sin(pA) * sin(pB) + cos(pA) * cos(pB) * cos(rad_lng_A - rad_lng_B))
    c1 = (sin(xx) - xx) * (sin(pA) + sin(pB)) ** 2 / cos(xx / 2) ** 2
    c2 = (sin(xx) + xx) * (sin(pA) - sin(pB)) ** 2 / sin(xx / 2) ** 2
    dr = flatten / 8 * (c1 - c2)
    distance = ra * (xx + dr)
    return distance
#具体的计算
#Lat_A,Lng_A为你公司地址,这里以望京为例,
#你可以输入你公司所在地
Lat_A=40.0011422082; Lng_A=116.4871328088
Distance0=[]#用于存放各个区域到公司的距离
region=[]
for r in range(0,208,1):
    Lat_B=df3.loc[r][1];Lng_B=df3.loc[r][2]
    distance=calcDistance(Lat_A, Lng_A, Lat_B, Lng_B)
    Distance1='{0:10.3f} km'.format(distance)
    region0=df3.loc[r][0]
    Distance0.append(Distance1);region.append(region0)
date={"region":region,"Distance":Distance0}    
Distance_result=pd.DataFrame(date,columns=["region","Distance"])
#将得到的距离望京最近的十个区域进行输出
Distance_result.sort_values(by="Distance").head(10)

(距离望京最近的十个区域,以及其对应的距离)

(距离望京最近的十个区域对应的雷达图)

获取经纬度信息的地址如下:http://www.gpsspg.com/maps.htm

最后将距离以及区域与对应的小区拼接在一起,得到下面的结果。

由于篇幅问题,怕各位看官看烦了,所以关于链家房屋信息的更多分析留在下一节。

我们通过这些数据还有很多的分析维度,下表是其中一个最基本统计,表头依次为该区域内房屋数量,价格的平均值,标准差,最小值,25%,50%,75%以及最大值。

本次关于数据获取(抓取)的部分并没有太详细的解释,如果你觉得看得不是很懂那就回到文章开头部分看看以往的推送的爬虫文章,有详细的解释。

更多精彩内容,请持续关注。

原文发布于微信公众号 - 张俊红(zhangjunhong0428)

原文发表时间:2017-10-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏游戏开发那些事

【Unity3d游戏开发】UGUI插件入门之游戏菜单

  ugui是unity4.6开始加入的一个新的ui系统,非常强大,下面我们将通过一系列博客的方式一起来学习一下ugui的使用。本篇博客会介绍如何使用ugui制...

33620
来自专栏前端新视界

如何编写通用的 Helper Class

Github: https://github.com/nzbin/snack-helper Docs: https://nzbin.github.io/sn...

22880
来自专栏Android开发实战

Android性能优化-渲染优化

我们在开发的过程中,可能经常会遇到测试的一些反馈,就是APP运行卡顿的问题。我们通常所讲的卡顿问题都是因为渲染掉帧的问题引起视觉上的卡顿感。所以了解渲染机制,我...

16020
来自专栏互联网杂技

20个为前端开发者准备的文档和指南6

1.SitePoint Sass Reference(SitePoint站点的Sass手册) 它是SitePoint自有的项目,是由我们的常驻高手Hugo Gi...

409100
来自专栏数据小魔方

动态图表3|数据有效性+名称管理器

今天要跟大家分享的是动态图表3——数据有效性+名称管理器! 个人感觉,动态图表的练习过程,是最高效的学习excel途径,因为整个过程会使用到函数、控件(开发工具...

43650
来自专栏前端萌媛的成长之路

为什么要使用css-sprite

28630
来自专栏编程

iOS一种动态栅格布局方案

前言 在日常开发过程中,我们会遇到一些需要不定期动态改变布局的页面或视图块,下面用张图展示一下: ? zdm_home.png 我以这张图解释一下需求,图上的几...

39460
来自专栏Material Design组件

Human Interface Guidelines — Steppers

11750
来自专栏阿凯的Excel

在Excel内实现跳跃!

今天和大家分享Excel中跳跃的神器。 想提跳跃,你会想到什么? 嗯 思路对了,我们实现的就是在Excel内实现跳棋。直接说需求吧! 我有好几千行的数据 ...

41630
来自专栏听雨堂

Morris图表使用小记

挺好用的,碰到几个问题,有的是瞎试解决了的: 1、我想折线图能够响应单击事件,即点击某个节点后,就能加载进一步的信息,帮助没找到,参照另外一个地方的写法,居然支...

29780

扫码关注云+社区

领取腾讯云代金券