Python爬虫+数据分析——简易分析楼盘均价和总价&分析PM和气温变化

1.预处理新房数据

通过爬虫爬取链家的新房数据https://bj.fang.lianjia.com/loupan/,并进行预处理。
• 最终的csv文件,应包括以下字段:名称,地理位置(3个字段分别存储),房型(只保留最小房型),面积(按照最小值),总价(万元,整数),均价(万元,保留小数点后4位);
• 对于所有字符串字段,要求去掉所有的前后空格;
• 如果有缺失数据,不用填充。
• 找出总价最贵和最便宜的房子,以及总价的中位数
• 找出单价最贵和最便宜的房子,以及单价的中位数

1.设计爬虫

关于如何设计爬虫,前一篇博文详细讲述了设计爬虫获取链家二手房数据,在此简略讲述。

链家的页面可以静态获取,因此可以结合Xpath来获取网页内容。设计Item如下:

import scrapy


class LianjiaItem(scrapy.Item):
    name = scrapy.Field()  # 名字
    area = scrapy.Field()  # 位置
    area_detail = scrapy.Field()
    position = scrapy.Field()
    type = scrapy.Field()  # 房型
    square = scrapy.Field()  # 面积
    total = scrapy.Field()  # 总价
    average = scrapy.Field()  # 均价
    pass

然后是设计spider.py文件:

from lianjia.items import LianjiaItem
import scrapy
import re


class mySpider(scrapy.spiders.Spider):
    name = "lianjia"
    allowed_domains = ["bj.lianjia.com/"]
    url = "https://bj.fang.lianjia.com/loupan/pg{}"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
        'Pragma': 'no-cache',
        'Cache-Control': 'no-cache',
    }

    def start_requests(self):
        for page in range(1, 6):
            yield scrapy.FormRequest(
                url=self.url.format(page),
                method="GET",
                headers=self.headers,
                callback=self.parse
            )

    def parse(self, response):
        for each in response.xpath("/html/body/div[4]/ul[2]/li"):
            item = LianjiaItem()
            item['name'] = each.xpath("div[1]/div[1]/a[1]/text()").extract()[0]
            item['area'] = each.xpath("div[1]/div[2]/span[1]/text()").extract()[0]
            item['area_detail'] = each.xpath("div[1]/div[2]/span[2]/text()").extract()[0]
            item['position'] = each.xpath("div[1]/div[2]/a[1]/text()").extract()[0]
            item['type'] = each.xpath("div[1]/a[1]/span[1]/text()").extract()
            if len(item['type']) > 0:
                item['type'] = item['type'][0]  # 取户型的最小值
            else:
                item['type'] = ''  # 考虑为空的情况
            square = each.xpath("div[1]/div[3]/span/text()").extract()
            if len(square) > 0:
                temp = square[0].split('-')
                item['square'] = re.findall(r'\d+', temp[0].split()[1])[0]  # 取面积的最小值
            else:
                item['square'] = ''  # 考虑为空的情况
            price = each.xpath("div/div[6]/div[1]/span[2]/text()").extract()[0].strip()
            if price == "元/㎡(均价)":  # 考虑有些数据有总价和均价,而有些只有总价
                item['average'] = each.xpath("div/div[6]/div[1]/span[1]/text()").extract()[0]
                total = each.xpath("div/div[6]/div[2]/text()").extract()
                if len(total) > 0:
                    total = total[0]
                    item['total'] = re.findall(r'\d+', total)[0]
                else:
                    item['total'] = ''
            else:
                item['average'] = ''
                item['total'] = each.xpath("div/div[6]/div[1]/span[1]/text()").extract()[0]
            item['total'] = format(float(item['total']), '.4f')  # 按要求将总价保留4位小数,即精确到元
            yield item

然后是设计管道文件,将获取的数据保存为csv文件:

import csv


class LianjiaPipeline(object):
    def open_spider(self,spider):
        try:
            self.file = open('data.csv', 'w', encoding='utf-8', newline='')
            self.csv = csv.writer(self.file)
            self.csv.writerow(['name', 'area', 'area_detail',
                               'position', 'type', 'square', 'average', 'total'])
            # 这里是设置表格的抬头,方便后续的数据处理
        except Exception as err:
            print(err)

    def process_item(self, item, spider):
        self.csv.writerow(list(item.values()))
        return item

    def close_spider(self, spider):
        self.file.close()

这里有个有意思的坑,item的写入文件是按代码的处理顺序进行的,也就是说在spider.py中如果先赋值total项(即第一次出现),再赋值average项,那么csv文件中就是这个顺序,反过来处理则在csv文件中先是average项,后是total项。正常来说不会有太大影响,但如果在编写选择结构时,两个项目的赋值顺序在不同分支中有不同,这会导致csv文件里的数据顺序产生不一致,影响后续的处理。

2.处理数据

至此已将数据保存下来,接下来进行数据处理,这里需要使用pandas库:

import numpy as np
import pandas as pd

filename = 'data.csv'
data_df = pd.read_csv(filename, encoding='utf-8', dtype=str)

# 去掉所有字符串的前后空行
data_df['name'] = data_df['name'].str.strip()
data_df['area'] = data_df['area'].str.strip()
data_df['area_detail'] = data_df['area_detail'].str.strip()
data_df['position'] = data_df['position'].str.strip()
data_df['type'] = data_df['type'].str.strip()

# 修改价格和面积为数字类型
data_df['total'] = data_df['total'].astype(np.float)
data_df['average'] = data_df['average'].astype(np.float)
data_df['square'] = data_df['square'].astype(np.float)

# 找出总价最贵和最便宜的房子,以及总价的中位数
print("总价最贵的房子:")
s = data_df['total']
print(data_df.iloc[s.idxmax()])
print("------------------------------")
print("总价最便宜的房子:")
print(data_df.iloc[s.idxmin()])
print("------------------------------")
print("总价的中位数:")
print(s.median())
print("\n--------------------------------------------\n")

# 找出单价最贵和最便宜的房子,以及单价的中位数

print("单价最贵的房子")
s = data_df['average']
print(data_df.iloc[s.idxmax()])
print("------------------------------")
print("单价最便宜的房子:")
print(data_df.iloc[s.idxmin()])
print("------------------------------")
print("总价的中位数:")
print(s.median())

输出结果如下:

总价最贵的房子:
name                            润泽御府
area                              朝阳
area_detail                       北苑
position       北京市朝阳区北五环顾家庄桥向北约2.6公里
type                              4室
square                           540
average                       100000
total                           5000
Name: 36, dtype: object
------------------------------
总价最便宜的房子:
name                    K2十里春风
area                        通州
area_detail               通州其它
position       永乐店镇漷小路百菜玛工业园对面
type                        2室
square                      74
average                  24500
total                      185
Name: 25, dtype: object
------------------------------
总价的中位数:
725.0

--------------------------------------------

单价最贵的房子
name                                  北京书院
area                                    朝阳
area_detail                           惠新西街
position       北三环以北,惠新东街与北土城东路交汇处西行200米路北
type                                    1室
square                                  67
average                             112000
total                                  750
Name: 11, dtype: object
------------------------------
单价最便宜的房子:
name           奥园北京源墅
area               密云
area_detail      密云其它
position         溪翁庄镇
type               3室
square            120
average         24000
total             270
Name: 27, dtype: object
------------------------------
总价的中位数:
50900.0

进程已结束,退出代码0

2.计算北京空气质量数据

要求:
1.汇总计算PM指数年平均值的变化情况
2.汇总计算10-15年PM指数和温度月平均数据的变化情况

同前一道问题,需要使用pandas对读取到的csv文件进行处理。设计的代码如下:

import numpy as np
import pandas as pd
from pandas import DataFrame

# 读取文件
filename = 'BeijingPM20100101_20151231.csv'
data_df = pd.read_csv(filename, encoding='utf-8', dtype=str)
# 转换部分列的数据为数字
data_df['month'] = data_df['month'].astype(np.float)
data_df['TEMP'] = data_df['TEMP'].astype(np.float)
data_df['PM_Dongsi'] = data_df['PM_Dongsi'].astype(np.float)
data_df['PM_Dongsihuan'] = data_df['PM_Dongsihuan'].astype(np.float)
data_df['PM_Nongzhanguan'] = data_df['PM_Nongzhanguan'].astype(np.float)
data_df['PM_US Post'] = data_df['PM_US Post'].astype(np.float)
# 求出每行的平均PM,加入数据中成为新的一列
temp_df = data_df.loc[:, ['PM_Dongsi', 'PM_Dongsihuan', 'PM_Nongzhanguan', 'PM_US Post']]
data_df['PM'] = temp_df.apply(lambda x: x.mean(), axis=1)
# print(data_df)

# 利用处理好的PM数据,求出各年的PM变化量
temp_df = data_df.loc[:, ['year', 'PM']]
years = ['2010', '2011', '2012', '2013', '2013', '2014', '2015']
result1 = {'year': [], 'PM': []}
for year in years:
    df = temp_df[temp_df.year == year]
    # print(df['PM'].mean(axis=0))
    result1['year'].append(year)
    result1['PM'].append(df['PM'].mean(axis=0))
f1 = DataFrame(result1)
print("汇总计算PM指数年平均值的变化情况,可查看problem1.csv:")
print(f1)
f1.to_csv('problem1.csv')
print("\n---------------------------------------\n")

# 计算各年各月的PM指数和温度的平均数据
temp_df = data_df.loc[:, ['year', 'month', 'TEMP', 'PM']]
result2 = {'year': [], 'month': [], 'TEMP': [], 'PM': []}
for year in years:
    df_year = temp_df[temp_df.year == year]
    for month in range(1, 13):
        df = df_year[df_year.month == month]
        result2['year'].append(year)
        result2['month'].append(month)
        result2['TEMP'].append(df['TEMP'].mean(axis=0))
        result2['PM'].append(df['PM'].mean(axis=0))
f2 = DataFrame(result2)
print('汇总计算各月气温和PM指数平均值的变化情况,可查看problem2.csv:')
print(f2)
f2.to_csv('problem2.csv')

打印结果如下:

汇总计算PM指数年平均值的变化情况,可查看problem1.csv:
   year          PM
0  2010  104.045730
1  2011   99.093240
2  2012   90.538768
3  2013   98.402664
4  2013   98.402664
5  2014   93.917704
6  2015   85.858942

---------------------------------------

汇总计算各月气温和PM指数平均值的变化情况,可查看problem2.csv:
    year  month       TEMP          PM
0   2010      1  -6.162634   90.403670
1   2010      2  -1.922619   97.239940
2   2010      3   3.293011   94.046544
3   2010      4  10.806944   80.072423
4   2010      5  20.831989   87.071913
..   ...    ...        ...         ...
79  2015      8  25.829071   45.896057
80  2015      9  20.408333   50.924769
81  2015     10  13.827957   77.257841
82  2015     11   2.897079  125.803125
83  2015     12  -0.617766  162.178987

[84 rows x 4 columns]

进程已结束,退出代码0

posted @ 2020-11-23 22:55  平信文  阅读(741)  评论(1编辑  收藏  举报