Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]

给出GitHub链接 click here

一、通过Scrapy框架获取数据

(1)、设置item

class HangzhouHouseItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()# 存放名字
    district = scrapy.Field()# 存放城市区域,上城区、下城区。。。。
    loc = scrapy.Field()# 具体位置
    area = scrapy.Field()# 面积
    price = scrapy.Field()# 价格(元/m^2)
    danwei = scrapy.Field()# 价格单位

(2)、price.py

这部分是获取数据的主要、核心部分。
我爬取的连接是here
首先,我们来看下这个网页界面。
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第1张图片
每一个红色方框就是一个独立的例子,我们主要爬取的是黄色的部分,主要包括:小区名字、区、地址、价格这几个方面。

*1、获取这一页的所有房子

我使用的是chrome浏览器,在这个网页上,首先按住F12,然后按下图操作:
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第2张图片
我们首先点按钮1,我们可以看到每一个小区都对应右边代码

...

所以我们需要选出这一页所有的房子。
我们可以使用下面代码实现:

base_url = 'https://hz.focus.cn/loupan/p'
house_list = response.xpath('//div[@class="s-lp-all "]')

*2、小区名字

对每一个房子,在这个网页上,首先按住F12,然后按下图操作:
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第3张图片

按照步骤1–》步骤2–》步骤三–》,我们关键看步骤三,我们可以看到,这个小区的名字在

的标签下的 标签中的字符串中,
在这里插入图片描述
所以我们使用下面的代码实现:

name = each.xpath('.//div[@class="title"]/a/text()').extract()

*3、区位+地址

同样按照小区名字的方式来提取信息
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第4张图片
后面,需要将区位和具体的地址分离。

loc = each.xpath('.//p[@class="location"]/span/text()').extract()

*4、面积

Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第5张图片
这里,我在price.py文件中选取的是汉字+数字,后期处理。

area = each.xpath('.//span[@class="building-area"]/text()').extract()

*5、价格

Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第6张图片

price = each.xpath('.//span[@class="price"]/text()').extract()

后期也是要处理的,主要是因为有些给的价格是元每平方米,二有的是多少万元每栋的,所以单位要统一,我同意换成前者。

*6、单位

Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第7张图片

danwei = each.xpath('.//span[@class="danwei"]/text()').extract()

*7、将上面的数据放入到item中,来传输信息

item['name'] = name[0]
item['loc'] = loc[0]
item['area'] = area[0] if area else 'NAN'
item['price'] = price[0]
item['danwei'] = danwei[0] if danwei else 'NAN'
yield item

*8、获取多页爬取

for page in range(1, 25):
	url_ = ''.join([base_url,str(page), '/'])
	# print()
	# print('*' * 50)
	# print(url_)
	# print()
	next_request = Request(url_, callback=self.parse)
	yield next_request

(3)、pipeline.py

*1、这里主要是处理面积和价格。

对于面积来说,主要是通过提取的面积,来split,然后求取面积的范围的中位数。
对于价格,首先,是根据单位来处理,如果单位是元每平方米,就直接存放,否则就除以面积然后存放。

 # deal with area, get a number
if item['area'] != 'NAN':
    item['area'] = item['area'].split(':')[-1]
    item['area'] = item['area'][:-3]
    temp = item['area'].split('-')
    if len(temp) > 1:
        item['area'] = int((int(temp[0]) + int(temp[1])) / 2)
    else:
        item['area'] = temp[0]

# for split district and loc
temp = item['loc'].split(' ')
item['district'] = temp[0]
item['loc'] = temp[1]

# # deal with  price
# curr_danwei = item['danwei']
# if curr_danwei == ' 万元 / 套':
if re.search(r'.*万元 / 套.*', item['danwei']):
    if item['area'] == 'NAN':
        item['price'] = ' NAN'
    else:
        item['price'] = ' ' + str(int(item['price']) * 10000 // int(item['area']))
    # print('*************')
elif item['price'] == ' 待定':
    item['price'] = ' NAN'

*2、存入数据库

更详细的数据库连接,可以看这篇blog

self.cursor.execute(
    """insert into price(name_, district, loc, area, price)
    value (%s, %s, %s, %s, %s)""",
    (item['name'],
     item['district'],
     item['loc'],
     item['area'],
     item['price']))

# 提交sql语句
self.connect.commit()

二、数据分析

(1)导入数据

  
import pymysql
import pandas as pd


def load_data_from_sql(sql, host, db, user, passwd, charset ='utf8', use_unicode=True):
    """load data frin mysql, and use pandas to store data"""

    # bulid connect
    con = pymysql.connect(
        host=host,
        db=db,
        user=user,
        passwd=passwd,
        charset=charset,
        use_unicode=use_unicode)

    # build cursor
    cursor = con.cursor()
    house_price = pd.read_sql(sql, con)
    con.close()
    return house_price


if __name__ == '__main__':
    print(load_data_from_sql('select * from price', 'localhost', 'house_price_HZ', 'root', '123456'))

(2)清洗数据

  
import pandas as pd
import numpy as np
from load_data import load_data_from_sql


# house_price.info()
def clearn_data():

    house_price = load_data_from_sql('select * from price', 'localhost', 'house_price_HZ', 'root', '5119550')

    # 选择有用的信息
    df = pd.DataFrame(house_price, columns=['name_', 'district', 'area', 'price'])


    # latitude dict
    lat_dict = {
     '上城': [120.17, 30.25], '下城': [120.17, 30.28], '拱墅': [120.13, 30.32],
                '西湖': [120.13, 30.27], '滨江': [120.20, 30.20], '萧山': [120.27, 30.17],
                '余杭': [120.30, 30.42], '桐庐': [119.67, 29.80], '淳安': [119.03, 29.60],
                '建德': [119.28, 29.48], '江干': [120.21, 30.2572], '富阳': [119.95, 30.05],
                '临安': [119.72, 30.23], '金华': [119.65, 29.09],'海宁': [120.68, 30.51],
                '湖州': [120.10, 30.87]}


    # a = df.groupby('district')
    # # print(type(df['price'][0]))
    df['price'] = pd.to_numeric(df['price'], errors='coerce')
    df['area'] = pd.to_numeric(df['area'], errors='coerce')
    # # print(type(df['price'][0]))
    # avg_price_each_dis = dict(a['price'].agg(np.mean))
    #
    #
    # val = df['price'].isnull()
    # print(avg_price_each_dis)

    # 下面实现缺省值用同一district的均值填充,这个代码要领会
    # 需要处理的列
    cols = ['area', 'price']
    # 分组的列
    gp_col = 'district'
    # 查询对应列的nan
    df_na = df[cols].isnull()
    # 根据分组计算均值
    df_mean = df.groupby(gp_col)[cols].mean()

    # print(df.tail())

    for col in cols:
        na_saries = df_na[col]
        districts_ = list(df.loc[na_saries, gp_col])
        # print(districts_)
        t = df_mean.loc[districts_, col]
        t.index = df.loc[na_saries, col].index

        df.loc[na_saries, col] = t
    return df

if __name__ == '__main__':
    df = clearn_data()
    print(df.tail())

(3)绘图分析

import pandas as pd
from clearn_data import clearn_data
import matplotlib.pyplot as plt
from pylab import mpl
import pyecharts
mpl.rcParams['font.sans-serif'] = ['simhei'] # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题


df = clearn_data()
# print(df.tail())

def plot_district_(type = 'price'):
    """绘制区域与价格或面积图. type = 'price' 或者 ‘area’"""
    each_dist_price = df.groupby('district')['price'].mean().sort_values(ascending=False)
    each_dist_area = df.groupby('district')['area'].mean().sort_values(ascending=False)

    if type == 'price':
        each_dist_price.plot(kind='bar',color='b')
        plt.ylim(0,70000,10000)
        plt.title("杭州市各区房价平均价格")
        plt.ylabel("房屋平均价格(元/平方米)")
    elif type == 'area':
        each_dist_area.plot(kind='bar', color= 'g')
        plt.ylim(0, 600, 100)
        plt.title('杭州市各区房价平局面积')
        plt.ylabel('房屋平均面积(平方米)')
    plt.xlabel("杭州行政区划")
    plt.xticks(rotation = 60)
    plt.rcParams['figure.dpi'] = 300
    plt.show()


def plot_box_district_(type = 'price'):
    """箱型图"""
    if type == 'price':
        df.boxplot(column = 'price', by='district')
        plt.title('杭州市各区房价箱型图')
        plt.ylabel("房屋平均价格(元/平方米)")
    elif type == 'area':
        df.boxplot(column = 'area', by ='district')
        plt.title('杭州市各区面积箱型图')
        plt.ylabel('房屋面积(平方米)')
    plt.xlabel("杭州行政区划")
    plt.show()


def plot_scatter():
    plt.figure(figsize=(10,8),dpi=256)
    colors = ['red', 'red', 'red', 'red',
              'blue', 'blue', 'blue', 'blue',
              'green', 'green', 'green', 'green',
              'gray', 'gray', 'gray', 'gray']
    addr_dist = ['上城','下城','拱墅','西湖',
                 '滨江','萧山','余杭','桐庐',
                 '淳安', '建德','江干','富阳',
                 '临安','金华', '海宁', '湖州']
    markers = ['o','s','v','x',
               'o', 's', 'v', 'x',
               'o', 's', 'v', 'x',
               'o', 's', 'v', 'x']

    for i in range(len(addr_dist)):
        x = df.loc[df['district'] == addr_dist[i]]['area']
        y = df.loc[df['district'] == addr_dist[i]]['price']
        plt.scatter(x, y, c = colors[i], label = addr_dist[i], marker=markers[i])

    plt.legend(loc=1, bbox_to_anchor=(1.138, 1.0), fontsize=12)
    plt.xlim(0, 600)
    plt.ylim(0, 100000)
    plt.title('杭州各区二手房面积对房价的影响', fontsize=20)
    plt.xlabel('房屋面积(平方米)', fontsize=16)
    plt.ylabel('房屋单价(元/平方米)', fontsize=16)
    plt.show()
    print(df['area'].max())

# plot_scatter()

# c = df.loc[:, 'price']
# print(c)
# fre = df.price.apply(lambda x: 1 if 0 < x < 20000 else 0).sum()
# print(fre)

def pie_price():
    # level_price = {'区间价格: 【0~1W】': 0,
    #                '区间价格: 【1~2W】': 0,
    #                '区间价格: 【2~3W】': 0,
    #                '区间价格: 【3~4W】': 0,
    #                '区间价格: 【4~5W】': 0,
    #                '区间价格: 【5~6W】': 0,
    #                '区间价格: 【6~7W】': 0,
    #                '区间价格: 【7~8W】': 0}
    level_price = pd.Series()
    def count_range(low = None, high = None):
        if not high:
            fre_num = df.price.apply(lambda x: 1 if low <= x else 0).sum()
        elif not low:
            fre_num = df.price.apply(lambda x: 1 if x < high else 0).sum()
        elif high and low:
            fre_num = df.price.apply(lambda x: 1 if low <= x < high else 0).sum()
        else:
            print('please enter right number')
        return fre_num

    level_price['区间价格: 【0~1W】'] = count_range(0, 10000)
    level_price['区间价格: 【1~2W】'] = count_range(10000, 20000)
    level_price['区间价格: 【2~3W】'] = count_range(20000, 30000)
    level_price['区间价格: 【3~4W】'] = count_range(30000, 40000)
    level_price['区间价格: 【4~5W】'] = count_range(40000, 50000)
    level_price['区间价格: 【5~6W】'] = count_range(50000, 60000)
    level_price['区间价格: 【6~7W】'] = count_range(60000, 70000)
    level_price['区间价格: 【7~8W】'] = count_range(70000, 80000)
    level_price['区间价格: 【8w +】'] = count_range(80000)
    # print(level_price)

    level_price.plot.pie(figsize=(8, 8), autopct='%.2f')
    plt.title('价格区间饼状图')
    plt.show()


def map_price():
    group_price = df.groupby('district')['price'].mean()

    districts = ['上城区', '下城区', '临安市', '余杭区', '富阳区',
                 '建德市', '拱墅区', '桐庐县', '江干区', '淳安县',
                 '滨江区', '萧山区', '西湖区']
    # print(group_price)
    prices = [65200, 50000, 20738, 28540, 21672,
              9940, 38660, 20780, 40040, 13222,
              42981, 29399, 39175]

    map = pyecharts.Map('杭州地图', width=800, height=600)
    map.add('杭州各区价格(元/平方米)', districts, prices, visual_range=[8000, 100000],
            maptype='杭州', is_visualmap=True, visual_text_color='#000', is_label_show=True)
    map.render('HZ-Price.html')


if __name__ == '__main__':
    # print(pyecharts.__version__)
    # map_price()
    pass

效果图:
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第8张图片
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第9张图片
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第10张图片
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第11张图片
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第12张图片
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第13张图片
Python项目【杭州房价数据分析】—— 爬虫、数据库、数据可视化[完整版]_第14张图片

有什么建议欢迎指出。

你可能感兴趣的:(Python)