pandas & matplotlib 用于数据分析和可视化
上一个任务通过requests、BeautifulSoup4两个功能强大、用法简洁的函数库已经获取到了楼盘名、地址和价格这些新房信息,并且保存为csv格式文件,csv文件可以用excel打开并进行编辑。
当然爬取数据只是第一步,“白嫖”网站辛苦整理的数据毕竟不太合适,所以这些数据如何产生价值呢?
设置一个场景:
当你想要在一座城市买房,在去各个售楼部踩点之前要做一些准备工作。
现在新房的信息已经收集完了,excel可以很快得出在售楼盘的数量、房价最高和最低,利用手机中的地图app可以很快搜索到楼盘的位置,不过一个一个搜索还是比较麻烦,当数据量很大时对于excel的工作量也会增大。
最关键的是,如果你想了解不同城市的新房情况,每爬虫一次都是新的数据,如何利用python工具来进行自动化的分析呢?
这次的任务是:对采集的新房数据进行分析,包括不限于数量、最大值、最小值、均值、分布等,并通过图表可视化,使数据呈现更加直观。
函数库准备
首先需要导入此次任务用到的函数库:pandas、matplotlib.pyplot,如何装载可以参考我第一篇博客。
import pandas as pd
import matplotlib.pyplot as plt
这里要特别介绍一下pandas,pandas库的数据基本类型为Series(序列),是类似一维数组的对象,由数据和索引组成,索引在左(从0开始),数据在右,索引自动创建。
当然用得比较多的是类似于excel的DataFrame(表),是由多条序列组成的一个数据表,每个序列是数据表的一列,共用一个从0开始的索引,示例见下图:
data = {
'city':['Beijing','Beijing','Shanghai','Shanghai','Shenzhen','Shenzhen','Guangzhou','Guangzhou'],
'year':[2018,2019,2018,2019,2018,2019,2018,2019],
'index':[2,2.1,1.6,1.8,2.3,1.9,1.8,1.7]}
frame = pd.DataFrame(data)
frame
生成结果如下:
DataFrame可以通过上图字典的方式进行创建,也可以读取csv、json或者xlsx文件(excel)得到,本篇的实操部分就通过上一次保存的csv文件得到dataframe表用于数据分析。
(当然上一篇提到的使用pandas的DataFrame功能把爬取到的数据生成csv文件会在后文进行补充,从爬取文件到创建dataframe表到数据分析到可视化一气呵成的代码会放在文章最后)
观察数据
在做数据分析之前首先要检查爬取到的数据是否可以直接进行数学运算、排序等操作,通过excel格式打开csv文件会更直观、且符合看表习惯。
(一)选择适合的数据
图中画红圈的部分有具体价格、价格待定但有往期价格和价格待定三种情况,首先不管三七二十一,先对三种情况分别统计数量。
‘价格待定’的楼盘因为缺失价格信息没有办法对价格进行比较,所以用于数据分析的数据中要把“价格待定”的数据删除,只对有具体价格的楼盘进行比较。
为此,我在第一次爬虫代码的基础上做了一点优化:
(1) 分三种情况进行统计;
(2) 并用pandas保存csv代码;
(3) 为了不使代码看起来臃肿,定义了函数优化结构。
优化后的代码如下,直接复制使用即可。
"""
作者:Michel
功能:获取房地产价格数据
版本:3.0
日期:2020/3/14
2.0增加功能:实时获取房产信息,不需要手动补充信息
3.0增加功能:(1)区分有具体价格、价格待定但有往期价格和价格待定三种情况,分别统计;同时增加district变量表示区县
(2)pandas创建dataframe,保存为csv文件
(3)定义函数,使代码更加简洁
"""
import requests as re
from bs4 import BeautifulSoup
import pandas as pd
# 定义一个用pandas写入csv文件的函数
def dateframe_to_csv(list):
"""
用pandas创建DateFrame,然后写入csv文件
"""
name_list = []
district_list = []
address_list = []
price_list = []
for line in list:
if list.index(line) % 2 == 0:
name_list.append(line[0])
district_list.append(line[1])
address_list.append(line[2])
else:
price_list.append(line)
newhouse_dict = {
'楼盘名': name_list, '区县': district_list, '地址': address_list, '价格': price_list}
df = pd.DataFrame(newhouse_dict)
df.to_csv('ganzhou_newhouse_current_price.csv', index=False, mode='w', encoding='GBK')
return df
def main():
"""
主函数
"""
# 初始化有具体价格、往期价格、价格暂定的三个列表
have_cur_price_list = [] # 楼盘有实时价格
have_pre_price_list = [] # 楼盘有往期价格
have_no_price_list = [] # 楼盘价格待定,且无往期价格供参考
# 分别统计数量,完成爬取后输出信息
is_cur_price_num = 0 # 有实时价格的的楼盘数量
is_pre_price_num = 0 # 无实时但有往期价格的楼盘数量
is_no_price_num = 0 # 价格待定的楼盘数量
# 爬取数据
for i in range(1, 10):
url = "http://ganzhou.newhouse.fang.com/house/s/b9"+str(i)+"/"
html = re.get(url, timeout=30)
html.encoding = 'GBK' # 解决中文乱码问题
soup = BeautifulSoup(html.text, 'lxml')
nlc_details = soup.find_all('div',class_='nlc_details')
for detail in nlc_details:
str_detail = str(detail)
#保证price有具体值,可以实时获取
if str_detail.find('元/㎡') != -1: # 在字符串中查找子字符串,如果找到返回索引值,如果找不到返回-1
if str_detail.find('价格待定') == -1:
name = detail.find('div',{
'class':'nlcd_name'}).text.strip()
#为便于后面分组统计,把地址信息再拆分为”区县”+“详细地址”两条信息,定义一个新变量district接收区县数据
address = detail.find('div',{
'class':'address'}).text.strip().replace('\t','').replace('[','').replace(']','@')
list = address.split('@')
district = list[0]
have_cur_price_list.append([name, district, address])
Price = detail.find('div', {