完美收官,本文是爬蟲實戰的最後一章了,所以儘管本文著重呈現爬蟲實戰,但其中有一大部分內容專註於數據分析。爬蟲只是整個過程的起點,其主要目的之一就是為後續數據分析等工作做好準備。通過對爬取的數據進行精確的清洗和分析,可以揭示其中隱藏的規律和趨勢,為決策提供有力支持。因此,爬蟲實戰並不僅僅是技術的展示,... ...
在本篇文章中,爬蟲的講解不僅僅局限於爬蟲本身,還會引申至另一個重要領域:數據分析。對我們而言,爬蟲的核心價值實際上在於獲取數據,一旦獲得了數據,接下來必然是要加以利用。數據分析便是其中關鍵一環,因此在爬蟲的講解之後,我們將會稍作涉及與數據分析相關的知識要點。
今天主要任務是爬取全國消費數據,然後根據過去十年的數據進行深入分析,以便進行未來兩年的消費預測。廢話不多說,讓我們直接開始吧。
全國消費數據
要獲取全國的消費數據,最好前往國家數據統計局進行查詢。因此,在使用爬蟲時,應當謹慎操作,避免對伺服器造成負荷過大的影響。在成功獲取數據後,應當及時保存,而不是過度頻繁地請求數據,以免導致伺服器癱瘓。在開始分析頁面之前,先確認所需的全國消費數據是否已被提供,然後按照常規操作,在頁面下方進行搜索,以確定數據展示形式是靜態頁面還是通過ajax請求獲取的。
為什麼在這裡我搜索的是數字而非文字?這是因為該請求返回到瀏覽器時處於亂碼狀態,因此為了演示,我選擇了數字作為示例,效果是一樣的。一旦找到請求,處理起來就很簡單了,我們只需複製URL,前往線上網站進行處理,然後將代碼複製出來即可。如果線上網站有不清楚的地方,可以參考前幾章的文章。
數據抓取
直接看下爬蟲代碼:
import requests
import re
strdata_code_map = {}
wdcode_name_map = {}
def get_data():
global strdata_code_map,wdcode_name_map
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Connection': 'keep-alive',
'Cookie': 'wzws_sessionid=oGX46GqAMTIzLjE3Mi40OS4yMDKBZDk0YTI3gmZjNWVlMQ==; u=6; experience=show; JSESSIONID=bANUmkmAc_F_FOy-dM-8VqxHEea-dpa39By6stbh14v9_aYXN7HM!1314454129',
'Referer': 'https://data.stats.gov.cn/easyquery.htm?cn=C01',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0',
'X-Requested-With': 'XMLHttpRequest',
'sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Microsoft Edge";v="122"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
params = {
'm': 'QueryData',
'dbcode': 'hgnd',
'rowcode': 'zb',
'colcode': 'sj',
'wds': '[]',
'dfwds': '[{"wdcode":"zb","valuecode":"A0A04"}]',
'k1': '1710816989823',
'h': '1',
}
response = requests.get('https://data.stats.gov.cn/easyquery.htm', params=params, headers=headers, verify=False)
# 解析JSON數據
response_data = response.json()
# 提取datanodes中的strdata和code映射數據列表
datanodes = response_data['returndata']['datanodes']
for node in datanodes:
input_str = node['code']
match = re.search(r'\.(\w+)_sj\.(\d+)', input_str)
if match:
part1 = match.group(1) # A0A0401
part2 = match.group(2) # 2023
if 'year' not in strdata_code_map:
strdata_code_map['year'] = []
if part2 not in strdata_code_map['year']:
strdata_code_map['year'].append(part2)
if part1 not in strdata_code_map:
strdata_code_map[part1] = []
strdata_code_map[part1].append(node['data']['strdata'])
print(strdata_code_map)
# 提取wdnodes中code和name映射列表
wdnodes = response_data['returndata']['wdnodes']
wdcode_name_map = {node['code']: node['name'] for node in wdnodes[0]['nodes']}
print(wdcode_name_map)
這段代碼解析了返回的JSON數據,提取了datanodes中的數據節點和wdnodes中的維度節點信息。對於數據節點,通過正則表達式提取了每個節點的code屬性,解析出數據節點對應的strdata和code映射關係,並將這些信息存儲到strdata_code_map字典中。對於維度節點,將每個節點的code和name屬性映射關係存儲到wdcode_name_map字典中。
數據分析
拿到數據後,我們立即對其進行數據分析。一般來說,在數據分析項目中,我們會首先利用Pandas庫載入數據,進行數據清洗和處理,然後使用Matplotlib庫進行數據可視化,以便更深入地理解數據並有效展示結果。
不多說,直接看下代碼:
import pandas as pd
import matplotlib.pyplot as plt
def get_now_plt():
data = {
'year': ['2023', '2022', '2021', '2020', '2019', '2018', '2017', '2016', '2015', '2014'],
'A0A0401': ['26796', '24538', '24100', '21210', '21559', '19853', '18322', '17111', '15712', '14491'],
'A0A0402': ['9.0', '-0.2', '12.6', '-4.0', '5.5', '6.2', '5.4', '6.8', '6.9', '7.5'],
'A0A0403': ['12114', '10590', '10645', '9037', '9886', '8781', '7803', '7157', '6460', '5842'],
'A0A0404': ['14.4', '-0.5', '17.8', '-8.6', '12.6', '12.5', '9.0', '10.8', '10.6', '11.4'],
'A0A0405': ['7983', '7481', '7178', '6397', '6084', '5631', '5374', '5151', '4814', '4494'],
'A0A0406': ['6.7', '4.2', '12.2', '5.1', '8.0', '4.8', '4.3', '7.0', '7.1', '8.9'],
'A0A0407': ['1479', '1365', '1419', '1238', '1338', '1289', '1238', '1203', '1164', '1099'],
'A0A0408': ['8.4', '-3.8', '14.6', '-7.5', '3.8', '4.1', '2.9', '3.3', '5.9', '7.0'],
'A0A0409': ['6095', '5882', '5641', '5215', '5055', '4647', '4107', '3746', '3419', '3201'],
'A0A040A': ['3.6', '4.3', '8.2', '3.2', '8.8', '13.1', '9.6', '9.6', '6.8', '6.7'],
'A0A040B': ['1526', '1432', '1423', '1260', '1281', '1223', '1121', '1044', '951', '890'],
'A0A040C': ['6.6', '0.6', '13.0', '-1.7', '4.8', '9.1', '7.4', '9.7', '6.9', '10.3'],
'A0A040D': ['3652', '3195', '3156', '2762', '2862', '2675', '2499', '2338', '2087', '1869'],
'A0A040E': ['14.3', '1.2', '14.3', '-3.5', '7.0', '7.1', '6.9', '12.0', '11.6', '14.9'],
'A0A040F': ['2904', '2469', '2599', '2032', '2513', '2226', '2086', '1915', '1723', '1536'],
'A0A040G': ['17.6', '-5.0', '27.9', '-19.1', '12.9', '6.7', '8.9', '11.2', '12.2', '9.9'],
'A0A040H': ['2460', '2120', '2115', '1843', '1902', '1685', '1451', '1307', '1165', '1045'],
'A0A040I': ['16.0', '0.2', '14.8', '-3.1', '12.9', '16.1', '11.0', '12.3', '11.5', '14.5'],
'A0A040J': ['697', '595', '569', '462', '524', '477', '447', '406', '389', '358'],
'A0A040K': ['17.1', '4.6', '23.2', '-11.8', '9.7', '6.8', '10.0', '4.4', '8.7', '10.3']
}
label = {
'A0A0401': '居民人均消費支出',
'A0A0402': '居民人均消費支出_比上年增長',
'A0A0403': '居民人均服務性消費支出',
'A0A0404': '居民人均服務性消費支出_比上年增長',
'A0A0405': '居民人均食品煙酒支出',
'A0A0406': '居民人均食品煙酒支出_比上年增長',
'A0A0407': '居民人均衣著支出',
'A0A0408': '居民人均衣著支出_比上年增長',
'A0A0409': '居民人均居住支出',
'A0A040A': '居民人均居住支出_比上年增長',
'A0A040B': '居民人均生活用品及服務支出',
'A0A040C': '居民人均生活用品及服務支出_比上年增長',
'A0A040D': '居民人均交通通信支出',
'A0A040E': '居民人均交通通信支出_比上年增長',
'A0A040F': '居民人均教育文化娛樂支出',
'A0A040G': '居民人均教育文化娛樂支出_比上年增長',
'A0A040H': '居民人均醫療保健支出',
'A0A040I': '居民人均醫療保健支出_比上年增長',
'A0A040J': '居民人均其他用品及服務支出',
'A0A040K': '居民人均其他用品及服務支出_比上年增長'
}
keys = list(data.keys()) # 獲取所有鍵並轉換為列表
need_keys = []
for i in range(1, len(keys), 2):
need_keys.append(keys[i])
# 數據
years = data['year']
# 繪製折線圖
for key in range(0, len(need_keys), 2):
plt.plot(years, [int(x) for x in data[need_keys[key]]], label=label[need_keys[key]], marker='o')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.xlabel('年份')
plt.ylabel('消費支出(元)')
plt.title('全國居民人均支出情況')
plt.legend()
plt.grid(True)
plt.show()
get_now_plt()
為了保持代碼的流暢性,我先複製了數據並定義了兩個字典項,分別是label和data。字典label用於存儲每種數據類型的中文標簽,而字典data包含了各年份的不同消費支出數據,比如居民人均消費支出、居民人均服務性消費支出等。接著,我使用matplotlib.pyplot庫來繪製折線圖。在繪製過程中,我遍歷了need_keys列表,為每種數據類型繪製相應的折線圖,並添加了標簽和數據點。
當處理數據時,請確保註意到,如果需要顯示中文字元,您可能需要使用以下語句來設置字體以避免亂碼:plt.rcParams['font.sans-serif'] = ['SimHei']
。此外,請註意我存儲的數據是按倒序排列的。
未來預測
當我們擁有近10年的數據時,實際上可以利用這些數據進行預測。在這方面有許多方法可供選擇,今天我們將探討ARIMA模型。ARIMA代表自回歸(Autoregressive, AR)、差分(Integrated, I)和移動平均(Moving Average, MA)這三種技術的結合,是一種用於時間序列預測的統計模型。通過ARIMA模型,我們可以捕捉時間序列數據中的趨勢、季節性變化和周期性變化。
在本章中,我們僅僅是提供了一些基礎信息,希望能夠引發你的興趣,具體的內容將在後續的章節中詳細展開。因此,接下來可以直接查看代碼部分:
import requests
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
import re
def get_feature_plt():
# 數據
data = {
'year': [2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023],
'A0A0403': [5842, 6460, 7157, 7803, 8781, 9886, 9037, 10645, 10590, 12114],
'A0A0405': [4494, 4814, 5151, 5374, 5631, 6084, 6397, 7178, 7481, 7983],
'A0A0407': [1099, 1164, 1203, 1238, 1289, 1338, 1238, 1419, 1365, 1479],
'A0A0409': [3201, 3419, 3746, 4107, 4647, 5055, 5215, 5641, 5882, 6095],
'A0A040B': [890, 951, 1044, 1121, 1223, 1281, 1260, 1423, 1432, 1526],
'A0A040D': [1869, 2087, 2338, 2499, 2675, 2862, 2762, 3156, 3195, 3652],
'A0A040F': [1536, 1723, 1915, 2086, 2226, 2513, 2032, 2599, 2469, 2904],
'A0A040H': [1045, 1165, 1307, 1451, 1685, 1902, 1843, 2115, 2120, 2460],
'A0A040J': [358, 389, 406, 447, 477, 524, 462, 569, 595, 697],
}
label = {
'A0A0401': '居民人均消費支出',
'A0A0402': '居民人均消費支出_比上年增長',
'A0A0403': '居民人均服務性消費支出',
'A0A0404': '居民人均服務性消費支出_比上年增長',
'A0A0405': '居民人均食品煙酒支出',
'A0A0406': '居民人均食品煙酒支出_比上年增長',
'A0A0407': '居民人均衣著支出',
'A0A0408': '居民人均衣著支出_比上年增長',
'A0A0409': '居民人均居住支出',
'A0A040A': '居民人均居住支出_比上年增長',
'A0A040B': '居民人均生活用品及服務支出',
'A0A040C': '居民人均生活用品及服務支出_比上年增長',
'A0A040D': '居民人均交通通信支出',
'A0A040E': '居民人均交通通信支出_比上年增長',
'A0A040F': '居民人均教育文化娛樂支出',
'A0A040G': '居民人均教育文化娛樂支出_比上年增長',
'A0A040H': '居民人均醫療保健支出',
'A0A040I': '居民人均醫療保健支出_比上年增長',
'A0A040J': '居民人均其他用品及服務支出',
'A0A040K': '居民人均其他用品及服務支出_比上年增長'
}
df = pd.DataFrame(data)
df.set_index('year', inplace=True)
need_keys = list(data.keys()) # 獲取所有鍵並轉換為列表
for i in range(1, len(need_keys), 3):
# 擬合ARIMA模型
model = ARIMA(df[need_keys[i]], order=(1, 1, 1)) # 根據數據特點選擇合適的order
model_fit = model.fit()
# 進行未來預測
future_years = [2024,2025] # 假設預測未來兩年
forecast = model_fit.forecast(steps=len(future_years))
# 可視化預測結果
plt.plot(df.index, df[need_keys[i]], label=label[need_keys[i]])
plt.plot(future_years, forecast, label='預測'+label[need_keys[i]], linestyle='--', marker='o')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.xlabel('年份')
plt.ylabel('消費支出(元)')
plt.title('未來兩年預測消費支出')
plt.legend()
plt.grid(True)
plt.show()
在進行優化處理時,首先將暫存的數據轉換為Pandas的DataFrame格式,並將年份設為索引。接著,從數據中提取所需的鍵名,對每三個鍵進行ARIMA模型的擬合和預測,隨後通過可視化展示了消費支出隨時間變化的趨勢圖,並呈現了未來兩年的預測數據。
總結
完美收官,本文是爬蟲實戰的最後一章了,所以儘管本文著重呈現爬蟲實戰,但其中有一大部分內容專註於數據分析。爬蟲只是整個過程的起點,其主要目的之一就是為後續數據分析等工作做好準備。通過對爬取的數據進行精確的清洗和分析,可以揭示其中隱藏的規律和趨勢,為決策提供有力支持。因此,爬蟲實戰並不僅僅是技術的展示,更是對數據價值的挖掘和充分利用。
還有一點需要特別強調的是,絕對不能利用這種方式從中謀取個人利益,比如搭建爬蟲網站等手段,這些行為是違法的。我想再次強調,在進行爬蟲操作時一定要遵守相關法律法規,儘量以學習為主,切勿觸犯法律。