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