CF二手房分析
在众多种类的数据分析中,与我们个人生活相关的更让人关注。而二手房市场则是一部分人非常关心的,我们可以尝试获取市面上二手房销售数据以进行分析,在分析的过程中我们将对某个城市的二手房市场或某个城市的房价有更深入的了解与认识。
原始数据获取
找到与该城市对应的链家二手房网页/网站:
- 明确要分析的城市的区 ,根据个人分析的意愿去选择所有或部分区;
- 编写(复制粘贴)爬虫代码 ,爬取目标数据;
这里仅有一点需要注意,爬虫往往会受限,所以我们可以尝试一个区一个区的去爬取,爬取的数据分别保存到不同的csv文件中,最终将爬取的各个区的csv文件进行汇总。
代码
- 代码是复制粘贴别人的,亲测能用;如有侵权,我是不相信原作者是有这个时间区看我的流水账的,感谢这位作者;
- 本人使用的python3.9版本,其他python版本未进行测试过;
import requests
from lxml import etree
from tqdm import tqdm
import time
import random
import pandas as pd
import re
# qu = ['jinjiang', 'wuhou', 'gaoxin', 'qingyang', 'jinniu', 'chenghua', 'tianfuxinqu']
# 将所要获取的区的名称写入数组,这个区的名称要是链家网上点击该区域后的区的拼音(在浏览器的网址上可以看到)
qu = ['songshanqu', 'hongshanqu', 'xinchengqu', 'kalaqinqi']
# 不知道为什么,必须把所有的都写在一行,不然会报错
xiaoqu_list, quyu_list, huxing_list, mianji_list, chaoxiang_list, zhuangxiu_list, louceng_list, build_time_list, price_list, unitPrice_list, intro_list, tihu_list, dianti_list, fangwu_list = [], [], [], [], [], [], [], [], [], [], [], [], [], []
# 获取有“房屋卖点”等数据
def get_intro(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'}
r = requests.get(url, headers=headers).text
s = etree.HTML(r)
intro = str(s.xpath('/html/body/div[3]/div/div/div[1]/div/text()')) # 房屋特色介绍
tihu = str(s.xpath('//*[@id="introduction"]/div/div/div[1]/div[2]/ul/li[10]/text()')) # 房屋梯户比例
fangwu = str(s.xpath('//*[@id="introduction"]/div/div/div[2]/div[2]/ul/li[2]/span[2]/text()')) # 商品房or住宅房
dianti = str(s.xpath('//*[@id="introduction"]/div/div/div[1]/div[2]/ul/li[11]/text()')) # 梯户比例
intro = intro.lstrip('[\'')
intro = intro.rstrip('\']')
tihu = tihu.lstrip('[\'')
tihu = tihu.rstrip('\']')
fangwu = fangwu.lstrip('[\'')
fangwu = fangwu.rstrip('\']')
dianti = dianti.lstrip('[\'')
dianti = dianti.rstrip('\']')
return intro, tihu, fangwu, dianti
def get_content():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'}
# 不能一次性获取,爬到武侯区的第二页就不行了,所以每次都改了range,分区爬取,爬了7次
# 这里是爬取列表中第三个元素(区)的数据
for i in range(3, 4): # 属于哪个区
time.sleep(round(random.uniform(3, 5), 2))
print('现在是{}区的数据'.format(qu[i]))
for j in range(1, 51):
# url = 'https://cd.lianjia.com/ershoufang/' + qu[i] + '/pg' + str(j)
url = 'https://cf.lianjia.com/ershoufang/' + qu[i] + '/pg' + str(j)
r = requests.get(url, headers=headers).text
s = etree.HTML(r)
for k in range(1, 31): # 一页里面有30套房源
xiaoqu = str(s.xpath('//*[@id="content"]/div[1]/ul/li[{}]/div[1]/div[2]/div/a[1]/text()'.format(k)))
xiaoquu = xiaoqu.lstrip('[\'')
xiaoqu = xiaoqu.rstrip('\']')
# print(xiaoqu)
quyu = str(s.xpath('//*[@id="content"]/div[1]/ul/li[{}]/div[1]/div[2]/div/a[2]/text()'.format(k)))
quyu = quyu.lstrip('[\'')
quyu = quyu.rstrip('\']')
# print(quyu)
# 获取房型相关的数据,通过split将数据进行分割,-1表示分割次数(全部分割)
fangxing = str(s.xpath('//*[@id="content"]/div[1]/ul/li[{}]/div[1]/div[3]/div/text()'.format(k))).split(
'|', -1)
huxing = fangxing[0].lstrip('[\'').rstrip()
# print(fangjian)
mianji = fangxing[1].strip()
# print(mianji)
chaoxiang = fangxing[2].strip()
zhuangxiu = fangxing[3].strip()
louceng = fangxing[4].strip()
build_time = fangxing[5].strip()
price = str(s.xpath('//*[@id="content"]/div[1]/ul/li[{}]/div[1]/div[6]/div[1]/span/text()'.format(k)))
price = price.lstrip('[\'')
price = price.rstrip('\']')
# print(price)
unitPrice = str(
s.xpath('//*[@id="content"]/div[1]/ul/li[{}]/div[1]/div[6]/div[2]/span/text()'.format(k)))
unitPrice = unitPrice.lstrip('[\'')
unitPrice = unitPrice.rstrip('\']')
# print(unitPrice)
intro_url = str(s.xpath('//*[@id="content"]/div[1]/ul/li[{}]/div[1]/div[1]/a/@href'.format(k)))
# print(intro_url) #['https://cd.lianjia.com/ershoufang/106110115686.html']
# 一定要将中括号和单引号去掉才能正常爬取数据。
intro_url = intro_url.lstrip('[\'')
intro_url = intro_url.rstrip('\']')
# print(intro_url)
intro, tihu, fangwu, dianti = get_intro(intro_url)
# print(intro,tihu,fangwu)
xiaoqu_list.append(xiaoqu)
# print(xiaoqu_list)
quyu_list.append(quyu)
huxing_list.append(huxing)
mianji_list.append(mianji)
chaoxiang_list.append(chaoxiang)
zhuangxiu_list.append(zhuangxiu)
louceng_list.append(louceng)
build_time_list.append(build_time)
price_list.append(price)
unitPrice_list.append(unitPrice)
intro_list.append(intro)
tihu_list.append(tihu)
dianti_list.append(dianti)
fangwu_list.append(fangwu)
print('现在爬完{}区的第{}页数据'.format(qu[i], j))
# 数据获取并保存
def main():
get_content()
infos = {'小区': xiaoqu_list,
'区域': quyu_list,
'户型': huxing_list,
'面积': mianji_list,
'朝向': chaoxiang_list,
'装修': zhuangxiu_list,
'楼层': louceng_list,
'建造时间': build_time_list,
'总价': price_list,
'单价': unitPrice_list,
'房屋卖点': intro_list,
'梯户比例': tihu_list,
'是否有电梯': dianti_list,
'房屋属性': fangwu_list}
data = pd.DataFrame(infos,
columns=['小区', '区域', '户型', '面积', '朝向', '装修', '楼层', '建造时间', '总价', '单价',
'房屋卖点', '梯户比例', '是否有电梯', '房屋属性'])
# 名称自己随意起,爬取不一样的区记得要修改文件名
data.to_csv("cf二手房d" + '.csv')
if __name__ == '__main__':
main()
数据清洗
这里就是要把数据变成我们最终想分析的样子,所以我的清洗步骤也只是为了想让数据变成我最终想要分析的样子
原始数据&最终数据对比
原始数据(encoding的方式要看csv文件原本是什么编码格式):
最终(目前)数据的样子:
通用步骤
- replace,想replace什么就replace什么
- 将某列的内容进行split,split后将某部分的值赋给新的列
- 列类型转换,多为object转数值类型
以下对几种情况分别进行示例:
# replace
data['面积'] = data['面积'].str.replace("平米","")
# 分割后的数据取某部分赋给新列
data['楼层'] = data['楼层'].str.split("(").str[0]
# 类型转换
data['面积'] = data['面积'].astype(float)
不通用步骤
房屋卖点
除了原始数据每个单独的特性的列的数据外,“房屋卖点”这列可提取的有价值的信息有很多,这里我们按需获取一下:
交通、出行、医院、上学、(一到九)中、地段、配套
data['房屋卖点'].str.split(',')
a = ['交通','出行','医院','上学','一中','二中','三中','四中','五中','六中','七中','八中','九中','地段','配套']
data['卖点提取'] = data['房屋卖点'].apply(lambda x: [item for item in x.split(',') if any(substring in item for substring in a)])
# print(c)
其实“房屋卖点”这列有价值的信息不仅限于这几个,比如还有“房龄”、“税”等扽,但由于包含此类数据的行过少,暂时未对其他类型卖点进行提取。
此外还对房屋类型是否为跃层进行了提取
data['跃层'] = data['房屋卖点'].apply(lambda x: '是' if '跃层' in x else '否')
经纬度
为了后续在地图上绘制热力图,需要得到小区所在位置的经纬度
经纬度由详细地址获得 详细地址 = 市 + 区 + 小区名称
data['详细地址'] = data['城市'] + data['区域'] + data['小区']
由此,在dataframe有了一列“详细地址”,我们根据这列获取所对应的经纬度
import requests
def query(addr):
#查询addr的经纬度
template = 'https://apis.map.qq.com/jsapi?qt=geoc&addr={addr}&key=UGMBZ-CINWR-DDRW5-W52AK-D3ENK-ZEBRC&output=jsonp&pf=jsapi&ref=jsapi&cb=qq.maps._svcb2.geocoder0'
url = template.format(addr=addr)
resp = requests.get(url)
x = re.findall('pointx":"(.*?)",',resp.text)[0]
y = re.findall('pointy":"(.*?)",',resp.text)[0]
return x,y
data['经纬度']=data['详细地址'].apply(query)
获取到的经纬度数据如下:
然后通过replace、split等操作分别将经度和纬度提取到不同的两列并转化为数值类型
至此,已成艺术。
二手房分析
实话实说,有一说一,每个人对数据对于数据分析的需求是不尽相同的,但是,往往由于数据源的数据限制,可数据分析的内容是比较有限的
这里我仅列出部分数据分析的内容:
小结
感觉ES那种加权评分应该应用在这里,根据每个房子所具备的条件进行加权评分;
除了二手房分布的热力图让我感觉有一丢丢新鲜感以外,其他的就是在嗯分析;
再学学再来评价吧,就先酱紫~