Python爬虫+数据分析——简易分析楼盘均价和总价&分析PM和气温变化
问题1:通过爬虫爬取链家的新房数据,并进行预处理。要求筛选出房价最高和最低的数据。
问题2:分析已给出的北京市气候数据,求出各年PM平均值和逐月气温和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