呆鸟的Python数据分析

Python开发案例:爬取四川省统计局数据Matplotlib绘

2020-01-28  本文已影响0人  佳瑞Jarrett

开发环境

前言

四川省统计局提供了过去若干月份的统计数据。统计局提供的数据内容丰富,数据完整,包括了给规模以上工业增加值,规模以上工业企业经济效益等等信息。但是如下图所示各个月份的统计数据在不同的表格中,无法形象地表现各类数据在不同时间点的变化情况。


四川省统计局数据下载页面(2020年1月28日)

准备工作

因此为了快速将过去若干个月的数据进行整理并分析获取相关信息的变化情况,因此接下来构思了数据统计的方法。
1.使用python的Request库获取网页内容。
2.将网站提供的文件.xlxs文件下载保存。
3.对下载的数据进行整理解析。
4.使用matplotlib对对齐的数据进行绘图。


查看网页源代码 网页源代码,箭头位置即下载的文件
|<ul class="f14"> |
|  | <li><span>2019-12-31</span> |
|  | <a href="[./201912/P020191231644906597282.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201912/P020191231644906597282.xlsx)" target="_bank" title="11月统计月报">11月统计月报</a></li> |
|  |  |
|  | <li><span>2019-12-05</span> |
|  | <a href="[./201912/P020191205637063227715.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201912/P020191205637063227715.xlsx)" target="_bank" title="10月统计月报">10月统计月报</a></li> |
|  |  |
|  | <li><span>2019-11-12</span> |
|  | <a href="[./201911/P020191112537765946834.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191112537765946834.xlsx)" target="_bank" title="9月统计月报">9月统计月报</a></li> |
|  |  |
|  | <li><span>2019-10-10</span> |
|  | <a href="[./201911/P020191114628665804316.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191114628665804316.xlsx)" target="_bank" title="8月统计月报">8月统计月报</a></li> |
|  |  |
|  | </ul> |
|  |  |
|  | <div class="blankH12"></div> |
|  | <ul class="f14"> |
|  |  |
|  | <li><span>2019-09-10</span> |
|  | <a href="[./201911/P020191118344715996243.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191118344715996243.xlsx)" target="_bank" title="7月统计月报">7月统计月报</a></li> |
|  |  |
|  | <li><span>2019-07-10</span> |
|  | <a href="[./201911/P020191118350091450271.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191118350091450271.xlsx)" target="_bank" title="6月统计月报">6月统计月报</a></li> |
|  |  |
|  | <li><span>2019-06-10</span> |
|  | <a href="[./201911/P020191118385640636026.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191118385640636026.xlsx)" target="_bank" title="5月统计月报">5月统计月报</a></li> |
|  |  |
|  | <li><span>2019-05-10</span> |
|  | <a href="[./201911/P020191118393966687607.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191118393966687607.xlsx)" target="_bank" title="4月统计月报">4月统计月报</a></li> |
|  |  |
|  | </ul> |
|  |  |
|  | <div class="blankH12"></div> |
|  | <ul class="f14"> |
|  |  |
|  | <li><span>2019-04-10</span> |
|  | <a href="[./201911/P020191118398490226826.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191118398490226826.xlsx)" target="_bank" title="3月统计月报">3月统计月报</a></li> |
|  |  |
|  | <li><span>2019-03-12</span> |
|  | <a href="[./201911/P020191118402541238839.xlsx](http://tjj.sc.gov.cn/sjfb/sjxz/201911/P020191118402541238839.xlsx)" target="_bank" title="2月统计月报">2月统计月报</a></li> |
|  |  |
|  | </ul> |
|  |  |

获取数据

获取网页内容

#四川省统计局网址
url = 'http://tjj.sc.gov.cn/sjfb/sjxz/'
#获取网页内容
r = requests.get(url)

使用python爬虫的本质是程序模拟浏览网页,并获取到网页上显示的内容。
requests.get()可以获取网页内容。

file_lst = [] #保存文件链接
file_name = [] #保存文件名
bs = BeautifulSoup(r.content, "html.parser") #解析网页,比如加上.content的内容。
hyperlink = bs.find_all('a')
for h in hyperlink:
    hh = h.get('href')
    if hh and '.xlsx' in hh:
        file_lst.append(hh)
        file_name.append(h.string)

由于网页中提供下载的文件命名不是按照有意义的命名方式进行命名,因此需要将文件名和文件链接进行分别存储在不同的列表中。
file_lst 用于保存文件链接。
file_name 用于保存文件名。

# encoding: utf-8
"""
@version: 1.0
@author: Jarrett_UESTC
@file: tongji
@time: 2020/1/19 22:35
"""

import requests
from bs4 import BeautifulSoup
import os
from urllib.request import urlretrieve  #方法直接将远程数据下载到本地

url = 'http://tjj.sc.gov.cn/sjfb/sjxz/'

r = requests.get(url)

file_lst = []
file_name = []
bs = BeautifulSoup(r.content, "html.parser") #解析网页,比如加上.content的内容。
hyperlink = bs.find_all('a')
for h in hyperlink:
    hh = h.get('href')
    if hh and '.xlsx' in hh:
        file_lst.append(hh)
        file_name.append(h.string)

def download(url, savepath, filename):
    """
    download file from internet
    :param url: path to download from
    :param savepath: path to save files
    :return: None
    """
    def reporthook(a, b, c):
        """
        显示下载进度
        :param a: 已经下载的数据块
        :param b: 数据块的大小
        :param c: 远程文件大小
        :return: None
        """
        t =  (a * b * 100.0 / c)
        if t >= 100:
            t = 100
        print("\rdownloading: %5.1f%%" % t, end="")
    #filename = os.path.basename(url)

    # 判断文件是否存在,如果不存在则下载
    if not os.path.isfile(os.path.join(savepath, filename)):
        print('Downloading data from %s' % url)
        urlretrieve(url, os.path.join(savepath, filename), reporthook=reporthook)
        print('\nDownload finished!')
    else:
        print('File already exsits!')
    # 获取文件大小
    filesize = os.path.getsize(os.path.join(savepath, filename))
    # 文件大小默认以Bytes计, 转换为Mb
    print('File size = %.2f Mb' % (filesize/1024/1024))

#print(file_lst)
#print(file_name)
for i in range(len(file_lst)):
    download(url+file_lst[i][2:], savepath='./file/', filename = file_name[i]+'.xlsx')

爬取到的网站数据,一共10个Excel文件

解析数据

# encoding: utf-8
"""
@version: 1.0
@author: Jarrett_UESTC
@file: xlrd
@time: 2020/1/23 15:56
"""

import xlrd
import os
import pandas as pd
import re
import matplotlib.pyplot as plt


def get_filelist(dir):
    Filelist = []
    File_name_lst = []
    for home, dirs, files in os.walk(path):
        for filename in files:
            # 文件名列表,包含完整路径
            Filelist.append(os.path.join(home, filename))
            # # 文件名列表,只包含文件名
            #re.findall(r'\d',str1)
            month = re.findall(r'\d', (filename))
            month = ''.join(month)
            File_name_lst.append(int(month))
        pass

    return Filelist, File_name_lst

path = './file'


file_list, file_name = get_filelist(path)
file_name = sorted(file_name)
#print(file_name)

def read(file, sheet_index=0):
    """

    :param file: 文件路径
    :param sheet_index: 读取的工作表索引
    :return: 二维数组
    """
    workbook = xlrd.open_workbook(file)
    # all_sheets_list = workbook.sheet_names()
    # print("本文件中所有的工作表名称:", all_sheets_list)
    # 按索引读取工作表
    sheet = workbook.sheet_by_index(sheet_index)
    #print(sheet_index)
    #print("工作表名称:", sheet.name)
    #print("行数:", sheet.nrows)
    #print("列数:", sheet.ncols)

    # 按工作表名称读取数据
    # second_sheet = workbook.sheet_by_name("b")
    # print("Second sheet Rows:", second_sheet.nrows)
    # print("Second sheet Cols:", second_sheet.ncols)
    # 获取单元格的数据
    # cell_value = sheet.cell(1, 0).value
    # print("获取第2行第1列的单元格数据:", cell_value)
    data = []
    for i in range(0, sheet.nrows):
        data.append(sheet.row_values(i))

    return data

#print(read(file_list[0]))

file = []
for j in range(len(file_name)):
    for i in range(len(file_list)):
        if (file_list[i]).find(str(file_name[j])) > -1:
            file.append(file_list[i])
            continue
        else:
            pass

#print(file)

all_data = pd.DataFrame()
for i in range(len(file_name)):
    if file_name[i]%3 == 0:
        data = pd.DataFrame(read(file[i], sheet_index = 3))
    else:
        data = pd.DataFrame(read(file[i]))
    data = data.T
    if i == 0:
        all_data = data
    else:
        data.drop(index=0, inplace=True)
        all_data = all_data.append(data)

data_index = all_data.loc[0] #获取第一行数据
all_data.drop(index=0, inplace=True) #删除首行数据
all_data.reset_index(drop=True, inplace=True)
print(data_index)
all_data = all_data.fillna(0)
print(all_data)
data1 = all_data.iloc[[0,2,4,6,8,10,12,14,16,18,]]
data2 = all_data.iloc[[1,3,5,7,9,11,13,15,17,19]]

print(data1)
print(data2)

代码解析:
get_filelist(dir):获取在路径中的文件内容,分别返回在路径中的文件地址和文件名。由于我们的需求是希望按照月份获取不同的文件,因此需要按顺序读取月份。
month = re.findall(r'\d', (filename)) 采用re正则表达式提取文件名中的数值。
由于sorted()函数只能对列表中的数字进行排序,因此需要将文件名列表转换为只有数值的列表。

在对文件名进行排序的基础上对文件路径也进行排序:

file = []
for j in range(len(file_name)):
    for i in range(len(file_list)):
        if (file_list[i]).find(str(file_name[j])) > -1:
            file.append(file_list[i])
            continue
        else:
            pass

read(file, sheet_index=0): 读取excel文件的函数。

Excel表格中纵轴信息

0 规模以上工业增加值
1 工业增加值
2 一、采矿业
3 制造业
4 电力、热力、燃气及水生产和供应业
5 二、国有企业
6 集体企业
7 股份合作企业
8 股份制企业
9 外商及港澳台商投资企业
10 其他经济类型企业
11 # 国有控股企业
12 产销率(%)
13 说明:规模以上工业统计范围为主营业务收入2000万元以上的工业企业。

2-11月份规模以上工业增加值的数据

由于获取到的不同月份的数据是横向存储的数据结构,不利于纵列操作。python对DataFrame数据转置的操作语句是data.T。

data = data.T

0 本月±% ... 0
1 累计±% 7.8 -11.4 9.9 5.3 ... 4.5 97.9 0
2 本月±% 8.59917 10.2769 8.57273 7.82264 ... 7.97941 0
3 累计±% 8.09946 9.57373 8.3171 5.59089 ... 8.17587 97.5 0
4 本月±% 8.19936 4.8707 8.60466 7.82713 ... 11.7472 0
5 累计±% 8.0997 8.79899 8.24435 6.138 ... 9.2568 97.3 0
6 本月±% 8.39919 12.8727 8.52713 4.04696 ... 11.1338 0
7 累计±% 8.19936 11.0549 8.23284 5.62991 ... 9.1104 97 0
8 本月±% 7.99986 6.82139 8.6297 3.46406 ... 6.53752 0
9 累计±% 8.19952 10.61 8.26665 5.52924 ... 8.7852 97.2 0
10 本月±% 7.89968 6.37955 8.24926 6.47463 ... 8.38832 0
11 累计±% 8.09979 9.98552 8.21664 5.72195 ... 8.76858 97.4 0
12 本月±% 7.8 5.51805 8.13193 7.36263 ... 7.06552 0 0
13 累计±% 8.1 9.32167 8.26085 6.00397 ... 8.63433 97.5 0 0
14 本月±% 8.6 5.96442 9.13242 6.37795 ... 8.7 0
15 累计±% 8.2 9.19974 8.44314 5.90009 ... 8.8 97.6 0
16 本月±% 7.2 5.30024 7.16615 8.54786 ... 7.6
17 累计±% 8.09982 10.2048 8.16261 5.88815 ... 8.65569 97.7
18 本月±% 7.7 5.98313 8.09603 5.9083 ... 5.2
19 累计±% 8 8.98966 8.19198 5.98343 ... 7.99959 97.9

从总数据中提取的本月百分比

0 本月±% ... 0
2 本月±% 8.59917 10.2769 8.57273 7.82264 ... 7.97941 0
4 本月±% 8.19936 4.8707 8.60466 7.82713 ... 11.7472 0
6 本月±% 8.39919 12.8727 8.52713 4.04696 ... 11.1338 0
8 本月±% 7.99986 6.82139 8.6297 3.46406 ... 6.53752 0
10 本月±% 7.89968 6.37955 8.24926 6.47463 ... 8.38832 0
12 本月±% 7.8 5.51805 8.13193 7.36263 ... 7.06552 0 0
14 本月±% 8.6 5.96442 9.13242 6.37795 ... 8.7 0
16 本月±% 7.2 5.30024 7.16615 8.54786 ... 7.6
18 本月±% 7.7 5.98313 8.09603 5.9083 ... 5.2

从总数据中提取的累计百分比

1 累计±% 7.8 -11.4 9.9 5.3 ... 4.5 97.9 0
3 累计±% 8.09946 9.57373 8.3171 5.59089 ... 8.17587 97.5 0
5 累计±% 8.0997 8.79899 8.24435 6.138 ... 9.2568 97.3 0
7 累计±% 8.19936 11.0549 8.23284 5.62991 ... 9.1104 97 0
9 累计±% 8.19952 10.61 8.26665 5.52924 ... 8.7852 97.2 0
11 累计±% 8.09979 9.98552 8.21664 5.72195 ... 8.76858 97.4 0
13 累计±% 8.1 9.32167 8.26085 6.00397 ... 8.63433 97.5 0 0
15 累计±% 8.2 9.19974 8.44314 5.90009 ... 8.8 97.6 0
17 累计±% 8.09982 10.2048 8.16261 5.88815 ... 8.65569 97.7
19 累计±% 8 8.98966 8.19198 5.98343 ... 7.99959 97.9

接下来将对获取的数据进行绘图。

绘图

对本月百分比采用柱状图绘图,累计百分比采用折线图绘图。
绘图结果如下图所示。

绘图结果

源代码

以下是数据解析和绘图的源代码,仅供参考。

# encoding: utf-8
"""
@version: 1.0
@author: Jarrett_UESTC
@file: xlrd
@time: 2020/1/23 15:56
"""

import xlrd
import os
import pandas as pd
import re
import matplotlib.pyplot as plt


def get_filelist(dir):
    Filelist = []
    File_name_lst = []
    for home, dirs, files in os.walk(path):
        for filename in files:
            # 文件名列表,包含完整路径
            Filelist.append(os.path.join(home, filename))
            # # 文件名列表,只包含文件名
            #re.findall(r'\d',str1)
            month = re.findall(r'\d', (filename))
            month = ''.join(month)
            File_name_lst.append(int(month))
        pass

    return Filelist, File_name_lst

path = './file'


file_list, file_name = get_filelist(path)
file_name = sorted(file_name)
#print(file_name)

def read(file, sheet_index=0):
    """

    :param file: 文件路径
    :param sheet_index: 读取的工作表索引
    :return: 二维数组
    """
    workbook = xlrd.open_workbook(file)
    # all_sheets_list = workbook.sheet_names()
    # print("本文件中所有的工作表名称:", all_sheets_list)
    # 按索引读取工作表
    sheet = workbook.sheet_by_index(sheet_index)
    #print(sheet_index)
    #print("工作表名称:", sheet.name)
    #print("行数:", sheet.nrows)
    #print("列数:", sheet.ncols)

    # 按工作表名称读取数据
    # second_sheet = workbook.sheet_by_name("b")
    # print("Second sheet Rows:", second_sheet.nrows)
    # print("Second sheet Cols:", second_sheet.ncols)
    # 获取单元格的数据
    # cell_value = sheet.cell(1, 0).value
    # print("获取第2行第1列的单元格数据:", cell_value)
    data = []
    for i in range(0, sheet.nrows):
        data.append(sheet.row_values(i))

    return data

#print(read(file_list[0]))

file = []
for j in range(len(file_name)):
    for i in range(len(file_list)):
        if (file_list[i]).find(str(file_name[j])) > -1:
            file.append(file_list[i])
            continue
        else:
            pass

#print(file)

all_data = pd.DataFrame()
for i in range(len(file_name)):
    if file_name[i]%3 == 0:
        data = pd.DataFrame(read(file[i], sheet_index = 3))
    else:
        data = pd.DataFrame(read(file[i]))
    data = data.T
    if i == 0:
        all_data = data
    else:
        data.drop(index=0, inplace=True)
        all_data = all_data.append(data)

data_index = all_data.loc[0] #获取第一行数据
all_data.drop(index=0, inplace=True) #删除首行数据
all_data.reset_index(drop=True, inplace=True)
print(data_index)
all_data = all_data.fillna(0)
print(all_data)
data1 = all_data.iloc[[0,2,4,6,8,10,12,14,16,18,]]
data2 = all_data.iloc[[1,3,5,7,9,11,13,15,17,19]]

print(data1)
print(data2)
x1 = file_name

plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
#有中文出现的情况,需要u'内容'

fig,axes = plt.subplots(2,5,figsize=(18, 8))
fig.tight_layout()#调整整体空白,致密布局
fig.subplots_adjust(top=0.95)
plt.subplots_adjust(wspace =0.3, hspace =0.3)#调整子图间距
for i in range(len(data_index)):
    if i == 0 or i == 11 or i == 12 or i == 13 or i == 14 or i == 15:
        pass
    else:
        y1 = (data1.loc[:, i]).tolist()
        y1[0] = 0
        y2 = (data2.loc[:, i]).tolist()
        if i <= 5:
            ax1 = axes[0,i-1]
        else:
            ax1 = axes[1,i-6]
        #ax3 = axes[0,2]
        ax1.bar(x1,y1)
        ax1_1 = ax1.twinx()
        ax1_1.plot(x1,y2,'r')
        ax1.set_xlabel(u'月份')
        ax1.set_ylabel(u'累计')
        ax1_1.set_ylabel(u'同比')
        ax1.set_title(u'四川省统计局\n'+data_index[i])
plt.show()

展望与结论

  1. 采用Requests方法爬取该网站时几乎没有难度,根据api接口说明及相关案例可以解决大部分问题。
  2. 读取Excel时可以采用速度更快的方法。
    3.绘制的图片结果不够完美,可以更进一步的优化,例如在官方提供的数据中有空格或无效字符应该去除。

后言

上一篇下一篇

猜你喜欢

热点阅读