点击这里进入人工智能 DBD 嘚吧嘚目录, 观看全部文章
前面两篇文章介绍了 58 房产租房信息的爬取和解密, 这次我们把它整理并做初步分析, 再存储为 CSV 备用, 并利用之前的数学知识思考一下如何进行预测.
如果你还没有爬取成功 html 文件, 可以直接从这里下载共 99 页包含 4000 多个租房信息的 HTML 文件压缩包.
百度网盘链接: https://pan.baidu.com/s/1W86kbqZArLKG7v1PTlufjw 密码: k96p
读取原始数据
参考之前文章的代码, 这里为了完整, 从头来一下, 标 * 表示和前一篇文章一样完全没变, 如果看过的可以直接跳过.
字符批量替换函数 *
- # 使用字典批量替换
- import re
- def multReplace(text, rpdict):
- rx = re.compile('|'.join(map(re.escape, rpdict)))
- return rx.sub(lambda match:rpdict[match.group(0)], text)
58 房产专用字体数字解密函数 *
- # 解密 58 房产的字体加密
- from fontTools.ttLib import TTFont
- import base64
- import re
- import io
- def decode58Fangchan(HTML,key):
- glyphdict = {
- 'glyph00001': '0',
- 'glyph00002': '1',
- 'glyph00003': '2',
- 'glyph00004': '3',
- 'glyph00005': '4',
- 'glyph00006': '5',
- 'glyph00007': '6',
- 'glyph00008': '7',
- 'glyph00009': '8',
- 'glyph00010': '9'
- }
- data = base64.b64decode(key) #base64 解码
- fonts = TTFont(io.BytesIO(data)) #生成二进制字节
- cmap = fonts.getBestCmap() #十进制 ascii 码到字形名的对应 {38006:'glyph00002',...}
- chrMapNum = {} #将变为 {'龥':'1',...}
- for asc in cmap:
- chrMapNum[chr(asc)] = glyphdict[cmap[asc]]
- return multReplace(HTML,chrMapNum)
将单个 HTML 页面转为租房信息对象列表的函数
这里有一些说明
对于 3 室 2 厅 1 卫 98㎡这样的字符串要提取室, 厅, 卫和平米数量, 我使用了
re.compile(r'[\d]+ 室').findall(arealine)[0].replace('室', '')
这样的正则表达式方法,[\d]+ 室中 \ d 表示数字,+ 表示数字至少是 1 个, 室表示必须包含室字, 这样得到的就是 ['3 室'], 然后再替换掉室字.
在 jjr 经纪人这行, 后来发现还有 geren 个人或者其他情况, 所以就改为提取倒数第二个 contents 来解决.
有些地方使用了
multReplace(jjr, {'\xa0': '',' ':'', '\n': ''})
方法把冗余内容清理掉.
- # 读取单个页页面
- from bs4 import BeautifulSoup
- import HTML
- import re
- def getpitem(url):
- with open(url, 'r') as f:
- text = HTML.unescape(f.read()) #将 閏 室变为閏室
- key = re.findall(r"base64,(.*)'\).format", text)[0] #用正则表达式提取 AAE..AAA
- dehtml = decode58Fangchan(text, key)
- soup = BeautifulSoup(dehtml)
- li = soup.find('ul', 'listUl').find_all('li')[:-1] #最后一个是页码
- pitems = [] #单页的租赁信息列表
- for item in li:
- title = item.find('h2').text.strip().replace('','')
- money = item.find('b', 'strongbox').text.strip()
- arealine = item.find('p', 'room strongbox').text
- shi = re.compile(r'[\d]+ 室').findall(arealine)[0].replace('室', '')
- ting = re.compile(r'[\d]+ 厅').findall(arealine)[0].replace('厅', '')
- wei = re.compile(r'[\d]+ 卫').findall(arealine)[0].replace('卫', '')
- area = re.compile(r'[\d\.]{1,5}㎡').findall(arealine)[0].replace(
- '㎡', '')
- add = item.find('p', 'add').text.strip()
- add = multReplace(add, {'\xa0': '',' ':'', '\n': ''})
- jjr = item.find('div', 'des').contents[-2:-1][0].text #发布者: 经纪人或个人或...
- jjr = multReplace(jjr, {'\xa0': '',' ':'', '\n': ''})
- info = {
- 'title': title,
- 'money': money,
- 'shi': shi,
- 'ting': ting,
- 'wei': wei,
- 'area': area,
- 'add': add,
- 'jjr': jjr,
- }
- pitems.append(info)
- return pitems
批量读取 HTML 文件
- import os
- files = os.listdir(r'./pages/')
- file_li = [r'./pages/' + s for s in files]
- itemsAll = []
- for f in file_li:
- itemsAll = itemsAll + getpitem(f)
- print(len(itemsAll))
- len(file_li)
上面代码正常的话应该输出 4596 和 99.
如果打印 itemsAll 则会得到如下显示:
用 Pandas 转换为 DataFrame
- import pandas as pd
- col=[k for k in itemsAll[0]]
- rows=[[item[k] for k in item] for item in itemsAll]
- df=pd.DataFrame(rows,columns=col)
- df
注意这里 DataFrame() 方法的第一个参数是 4596 个租房数据对象的列表, 而每个数据对象就是 8 个属性值组成的列表 (是 ['新未... 览中心', '3800', '2'...], 而不是字典 {'title': '新未来... 览中心', 'money': '3800', 'shi': '2', 'ting': '2'...}).
它的第二个参数是表头栏的名称,['title', 'money', 'shi', 'ting', 'wei', 'area', 'add', 'jjr'] 共 8 个列. 如果这个列数和 rows 每个数据的列表长度不一致就会报错.
输出结果如下图:
保存为 CSV
df.to_csv('58suzhou_zufang_4k.csv')
只要这一句就可以了. 用下面代码验证是否已经保存成功.
- df2=pd.read_csv('58suzhou_zufang_4k.csv')
- df2[:10]
正常的话会输出前 10 个数据.
Plotly 绘制散点图
- import plotly.offline as py
- import plotly.graph_objs as go
- py.init_notebook_mode()
- data = go.Scatter(
- x=[m['area'] for m in itemsAll],
- y=[m['money'] for m in itemsAll],
- mode='markers')
- layout = go.Layout( #layout 网格坐标布局
- title='房价与面积',
- autosize=False,
- width=600,
- height=600,
- xaxis=dict( #axis,x,y
- autorange=False,
- range=(0, 200),
- ),
- yaxis=dict( #axis,x,y
- autorange=False,
- range=(0, 12000),
- ),
- )
- fig = go.FigureWidget([data], layout)
- fig
可以输出得到下图:
分析思考
我们的问题需求是: 当房东用户在 58 房产网上录入一个新的房产信息的时候, 我们为他提供一个可靠的参考租价.
假设我们只考虑面积, 认为租价 money 只和房屋的面积 area 有关, 那么我们从上图中可以找到一些规律.
比如横轴 100 平米的房子, 租价都在 2k 上下, 大约都在 1.8k 到 3k 这段, 只有个别的少量飘到上面 6~8k, 也许我们给这个房东建议一个 1.8+3=2.4 比较合理一些.
怎么用算法实现? 可以想出很多办法:
找出和变量 x 面积相近的数据, 比如找出所有都是 100 平方左右的数据点, 然后把它们的房价相加再平均. 如果怕用户是 107.3 平米找不出横轴一样的数据点, 那还可以找一个范围的, 比如把 102.3~112.3 平米之间的都找出再求平均.
找到房价和面积之间的线性关系, 就是下图这个箭头方向的直线的方程, 找出 a 和 b, 然后把 x 面积带进入就能算出 y.
也许还有更多方法, 但别忘了, 我们需要一种具有通用性的方法, 就是这种方法能扩充到房价以外的多个因素才行, 因为我们都知道, 只靠面积得到的结果并不太靠谱.
点击这里进入人工智能 DBD 嘚吧嘚目录, 观看全部文章
每个人的智能新时代
如果您发现文章错误, 请不吝留言指正;
如果您觉得有用, 请点喜欢;
来源: http://www.jianshu.com/p/051686b8fa70