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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM