Python作业2:scrapy爬取链家+数据预处理
一、爬取数据并预处理
1、要求
作业1:通过爬虫爬取链家的新房数据,并进行预处理。
-
最终的csv文件,应包括以下字段:名称,地理位置(3个字段分别存储),房型(只保留最小房型),面积(按照最小值),总价(万元,整数),均价(万元,保留小数点后4位);
-
对于所有字符串字段,要求去掉所有的前后空格;
-
如果有缺失数据,不用填充。
-
找出总价最贵和最便宜的房子,以及总价的中位数
-
找出单价最贵和最便宜的房子,以及单价的中位数
2、实验过程
准备阶段
- 通过查看网页源代码,找出页面对应元素所在的结构xpath,作为爬虫爬取数据的入口。如下图:
- xpath可以直接复制得到,如下图:
- 由[2],同上一次作业,我们需要爬取的元素有:名称,地理位置(3个字段分别存储),房型(只保留最小房型),面积(按照最小值),总价(万元,整数),均价(万元,保留小数点后4位)
代码编写
- item.py:
import scrapy
class NewhouseItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = scrapy.Field() # 名称
location1 = scrapy.Field() # 地址1
location2 = scrapy.Field() # 地址2
locationDetail = scrapy.Field() # 详细地址
houseType = scrapy.Field() # 房型
area = scrapy.Field() # 面积(取最小值)
TTprice = scrapy.Field() # 总价
averagePrice = scrapy.Field() # 均价
- setting.py只需加入下面一句:
ITEM_PIPELINES = {\'newHouse.pipelines.NewhousePipeline\': 300,}
- 最后是spider.py,我将新房页面19页的数据都爬取下来,因为要从其中最贵和最便宜的等待;由于要进行之后的数据分析,所以这里我尽量将面积、总价和单价作为仅有数字的字符串保存而非列表:
import scrapy
from newHouse.items import NewhouseItem
import csv
import re
class MySpider(scrapy.Spider):
name = \'newHouse\'
allowed_domains = [\'bj.lianjia.com\']
start_urls = [\'https://bj.fang.lianjia.com/loupan/\']
start_urls = []
for page in range(1, 19):
"""爬取19页数据"""
url = \'https://bj.fang.lianjia.com/loupan/pg{}\'.format(page)
start_urls.append(url)
def parse(self, response):
item = NewhouseItem()
for each in response.xpath("//div[4]/ul[2]/*"):
"""爬取该路径下的房区信息"""
"""楼盘名称"""
item[\'name\'] = each.xpath("./div[1]/div[1]/a[1]/text()").extract()
"""楼盘地理位置,分三段"""
item[\'location1\'] = each.xpath("./div[1]/div[2]/span[1]/text()").extract()
item[\'location2\'] = each.xpath("./div[1]/div[2]/span[2]/text()").extract()
item[\'locationDetail\'] = each.xpath("./div[1]/div[2]/a[1]/text()").extract()
"""楼盘房型(取最小房型)"""
item[\'houseType\'] = each.xpath("./div[1]/a[1]/span[1]/text()").extract()
"""楼盘面积(取最小值)"""
Area = each.xpath("./div[1]/div[3]/span/text()").extract()[0].split(\'-\')[0].split()[1]
number = \'\'
for i in str(Area):
"""取出其中的数字"""
if i.isdigit():
number += i
item[\'area\'] = number
"""楼盘均价"""
item[\'averagePrice\'] = float(each.xpath("./div[1]/div[6]/div[1]/span[1]/text()").extract()[0]) / 10000
"""楼盘总价"""
ttprice = each.xpath("./div[1]/div[6]/div[2]/text()").extract()
number = \'\'
for i in str(ttprice):
"""取出其中的数字"""
if i.isdigit():
number += i
item[\'TTprice\'] = number
if (item[\'name\'] and item[\'area\'] and item[\'TTprice\'] and item[\'averagePrice\']):
"""去除空值"""
yield(item) # 将item返回到pipeline模块
else:
print(\'-----------ERROR-------------\', item[\'name\'])
3、爬取结果
19页数据,共190组左右
4、数据预处理
代码编写
使用pandas和numpy库函数,比较简单,源代码如下:
import numpy as np
import pandas as pd
filename = \'newHouse.csv\' # 文件名
data_df = pd.read_csv(filename, encoding = \'utf-8\', dtype = str)
# 去掉所有字符串的前后空行
data_df[\'name\'] = data_df[\'name\'].str.strip()
data_df[\'location1\'] = data_df[\'location1\'].str.strip()
data_df[\'location2\'] = data_df[\'location2\'].str.strip()
data_df[\'locationDetail\'] = data_df[\'locationDetail\'].str.strip()
data_df[\'houseType\'] = data_df[\'houseType\'].str.strip()
# 将面积和价格改为浮点型
data_df[\'TTprice/万元\'] = data_df[\'TTprice/万元\'].astype(np.float)
data_df[\'averagePrice/万元\'] = data_df[\'averagePrice/万元\'].astype(np.float)
data_df[\'area/m2\'] = data_df[\'area/m2\'].astype(np.float)
# 找出总价最贵和最便宜的房子,以及总价的中位数
print(\'总价最贵的房子:\')
msg = data_df[\'TTprice/万元\']
print(data_df.iloc[msg.idxmax()])
print(\'**********************************************************\')
print(\'总价最便宜的房子:\')
print(data_df.iloc[msg.idxmin()])
print(\'**********************************************************\')
print(\'总价的中位数:\')
print(msg.median())
print(\'**********************************************************\')
print(\'\n----------------------------------------------------------\n\')
# 找出单价最贵和最便宜的房子,以及单价的中位数
print(\'单价最贵的房子:\')
msg = data_df[\'averagePrice/万元\']
print(data_df.iloc[msg.idxmax()])
print(\'**********************************************************\')
print(\'单价最便宜的房子:\')
print(data_df.iloc[msg.idxmin()])
print(\'**********************************************************\')
print(\'单价的中位数:\')
print(msg.median())
print(\'**********************************************************\n\')
预处理结果
- 总价最贵和最便宜的房子,以及总价的中位数:
- 单价的最贵和最便宜的房子,以及单价的中位数:
二、计算北京空气质量数据
1、作业要求
作业2:计算北京空气质量数据
-
汇总计算PM指数年平均值的变化情况
-
汇总计算10-15年PM指数和温度月平均数据的变化情况
2、代码编写
使用numpy和pandas库函数可以很简单地实现本次作业的汇总,关键代码:
aveY_df = df.groupby(\'year\').mean()
aveM_df = df.groupby([\'year\', \'month\']).mean()
第一句是将数据按照年将其汇总;第二句是将数据按照[年,月]将其汇总。
所有代码如下:
import numpy as np
import pandas as pd
from pandas import DataFrame
# 读取文件
filename = \'BeijingPM20100101_20151231.csv\'
df = pd.read_csv(filename, encoding = \'utf-8\')
# 删除有空PM值的行
df.dropna(axis=0, how=\'all\', subset=[\'PM_Dongsi\',\'PM_Dongsihuan\',\'PM_Nongzhanguan\', \'PM_US Post\'], inplace=True)
# 计算PM平均值
df[\'PMsum\'] = df[[\'PM_Dongsi\',\'PM_Dongsihuan\',\'PM_Nongzhanguan\', \'PM_US Post\']].sum(axis=1)
df[\'PMcount\'] = df[[\'PM_Dongsi\',\'PM_Dongsihuan\',\'PM_Nongzhanguan\', \'PM_US Post\']].count(axis=1)
df[\'PMave\']=round(df[\'PMsum\']/df[\'PMcount\'],2)
aveY_df = df.groupby(\'year\').mean()
aveM_df = df.groupby([\'year\', \'month\']).mean()
# PM年平均值输出到文件
aveY_df.to_csv(\'PMyearAve.csv\')
# PM和TEMP月平均值输出到文件
aveM_df.to_csv(\'PM_TEMP_monthAve.csv\')
# 输出PM年平均值
print(df.groupby(\'year\').mean())
print(\'------------------------------------------\')
# 输出PM和TEMP月平均值
print(df.groupby([\'year\', \'month\']).mean())
3、预处理结果:
PM按年分析
即如下:
|
year |
PMave |
|
2010 |
104.0457 |
|
2011 |
99.09324 |
|
2012 |
90.53877 |
|
2013 |
98.40268 |
|
2014 |
93.91771 |
|
2015 |
85.85894 |
制表如下:
|
year |
month |
TEMP |
PMave |
|
2010 |
1 |
-6.37156 |
90.40367 |
|
2010 |
2 |
-1.91505 |
97.23994 |
|
2010 |
3 |
2.997179 |
94.04654 |
|
2010 |
4 |
10.8078 |
80.07242 |
|
2010 |
5 |
20.85346 |
87.07191 |
|
2010 |
6 |
24.49381 |
109.0389 |
|
2010 |
7 |
27.72984 |
123.4261 |
|
2010 |
8 |
25.34763 |
97.68343 |
|
2010 |
9 |
22.24145 |
122.7927 |
|
2010 |
10 |
12.28302 |
118.7844 |
|
2010 |
11 |
3.308735 |
138.384 |
|
2010 |
12 |
-2.05787 |
97.11575 |
|
2011 |
1 |
-5.54383 |
44.8737 |
|
2011 |
2 |
-0.85417 |
150.2902 |
|
2011 |
3 |
6.966346 |
57.99199 |
|
2011 |
4 |
14.69646 |
91.72067 |
|
2011 |
5 |
20.73315 |
65.10815 |
|
2011 |
6 |
25.64416 |
108.7947 |
|
2011 |
7 |
26.46081 |
107.3865 |
|
2011 |
8 |
25.66375 |
103.7338 |
|
2011 |
9 |
19.2267 |
94.9694 |
|
2011 |
10 |
13.19968 |
145.5568 |
|
2011 |
11 |
5.94965 |
109.435 |
|
2011 |
12 |
-2.30686 |
108.7214 |
|
2012 |
1 |
-4.94328 |
118.9224 |
|
2012 |
2 |
-2.57391 |
84.44203 |
|
2012 |
3 |
5.068919 |
96.47432 |
|
2012 |
4 |
15.46314 |
87.83588 |
|
2012 |
5 |
21.93488 |
90.96671 |
|
2012 |
6 |
24.3291 |
96.63418 |
|
2012 |
7 |
26.55959 |
80.64971 |
|
2012 |
8 |
25.54735 |
81.16533 |
|
2012 |
9 |
20.11517 |
59.95225 |
|
2012 |
10 |
13.30811 |
94.95135 |
|
2012 |
11 |
3.691977 |
87.43696 |
|
2012 |
12 |
-4.35342 |
109.1873 |
|
2013 |
1 |
-5.37568 |
183.1953 |
|
2013 |
2 |
-1.82143 |
113.5665 |
|
2013 |
3 |
5.405914 |
114.5728 |
|
2013 |
4 |
12.24861 |
63.04781 |
|
2013 |
5 |
21.45565 |
89.14853 |
|
2013 |
6 |
23.67778 |
111.3548 |
|
2013 |
7 |
27.08221 |
74.93284 |
|
2013 |
8 |
26.57124 |
67.92363 |
|
2013 |
9 |
20.125 |
85.71787 |
|
2013 |
10 |
12.82124 |
102.2088 |
|
2013 |
11 |
5.913889 |
85.14629 |
|
2013 |
12 |
-0.29301 |
90.31777 |
|
2014 |
1 |
-0.91398 |
107.9117 |
|
2014 |
2 |
-0.70238 |
160.5139 |
|
2014 |
3 |
9.564516 |
103.1833 |
|
2014 |
4 |
16.84444 |
92.16069 |
|
2014 |
5 |
21.6129 |
64.95851 |
|
2014 |
6 |
24.83333 |
59.15464 |
|
2014 |
7 |
28.04435 |
91.79995 |
|
2014 |
8 |
25.79408 |
65.66822 |
|
2014 |
9 |
20.50417 |
68.23267 |
|
2014 |
10 |
13.3414 |
135.2697 |
|
2014 |
11 |
5.676389 |
106.3375 |
|
2014 |
12 |
-1.41935 |
76.62251 |
|
2015 |
1 |
-1.32661 |
110.0228 |
|
2015 |
2 |
0.941964 |
103.4456 |
|
2015 |
3 |
8.265141 |
94.48344 |
|
2015 |
4 |
15.53889 |
79.39706 |
|
2015 |
5 |
21.49328 |
61.16751 |
|
2015 |
6 |
24.67455 |
60.33239 |
|
2015 |
7 |
26.5672 |
60.22953 |
|
2015 |
8 |
25.82907 |
45.89608 |
|
2015 |
9 |
20.40833 |
50.92475 |
|
2015 |
10 |
13.82796 |
77.25774 |
|
2015 |
11 |
2.897079 |
125.8032 |
|
2015 |
12 |
-0.61777 |
162.1789 |