zhangzijian2290

1.选题背景

自新冠肺炎疫情爆发以来,这场疫情几乎影响了每个人的生活,为了对疫情做数据分析,需要采集疫情的数据。

此次期末作业设计我选择了利用爬虫网路来获取疫情相关数据的方法,通过对丁香园网站爬取相关疫情数据,在本地对数据处理后将疫情数据用视图等形式进行展现。

本次爬取网站为丁香园(https://ncov.dxy.cn/ncovh5/view/pneumonia),丁香园是国内实时疫情推送的网站非常实用,感谢丁香园对疫情数据的推送。同时,参考B站UP主(---芋圆---)提供的视频教学,此为本人作业不进行商用,若有侵权请通知本人,作业上交后定会删除,请勿转载和二次使用,谢谢。

 

2.主题思路

1.发送请求获取数据

2.从疫情首页中提取最各国疫情字符串

3.从最近各国疫情字符串共媒体去json格式字符串

4.把json格式字符串,转换为Python类型

5.把python类型的数据,以json格式存入文件之中

6.利用pandas、matplotlib、numpy将数据读出绘制数据对应的图

3.丁香园网页页面

 

 

 

 

 

 

 从上部分模块我们可以获得具体省会的城市对应的疫情感染数据,同时网页根据内嵌式css+id对应文本框嵌套窗口来显示数据

 

我们根据对应id显示的需要内容来进行获取,字符串识别对应ID来达到我们想要的效果

 

 4.Python爬虫代码设计

 

 

 

1.从丁香园网站采集最近一日疫情各国状况

 

#导入模块
import requests
from bs4 import BeautifulSoup
#发送请求
url="https://ncov.dxy.cn/ncovh5/view/pneumonia"
#伪装请求头
headers ={\'User-Agent\':\'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\'}
response=requests.get(url,timeout=30,headers=headers)
home_page = response.content.decode()
#print(home_page)
#使用beautifulsoup提取疫情数据
soup=BeautifulSoup(home_page,\'lxml\')
script=soup.find(id="getListByCountryTypeService2true")
text= script.text
print(text)

 

 

 

接下来将获取的字段用json.dump将它导出到文件

#从数据中获取json格式字符串
json_str = re.findall(r\'\[.+\]\',text)[0]

#把json格式字符串转换为python类型
last_day_corona_virus = json.loads(json_str)
#print(last_day_corona_virus)
with open(\'last_day_corona_virus\',\'w\')as fp:
    json.dump(last_day_corona_virus,fp,ensure_ascii=False)

 

 

 这里为了能方便打开确定导出成功我更改了代码(ensure_ascii=True,不更改了话是json文件无法在网页编辑里面直接打开)

由上我们就获得了各国的疫情数据  现在我们就要利用pandas来调用

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation as animation
from matplotlib.colors import rgb2hex
from matplotlib.patches import Polygon
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader

# 加载世界现有确诊病例数据
last_day_corona_virus = pd.read_json(\'last_day_corona_virus.json\')
# 计算疫情日期
current_date = pd.to_datetime(last_day_corona_virus[\'createTime\'][0], unit=\'ms\')
current_date_str = f\'{current_date.year}年{current_date.month}月{current_date.day-1}日\'
# print(current_date_str)

# 统计最近一日各国现有确诊病例数量
datas = last_day_corona_virus.pivot_table(values=\'currentConfirmedCount\', index=\'provinceName\')[\'currentConfirmedCount\']
# 按现有确诊确诊数量, 进行倒叙排
datas = datas.sort_values(ascending=False)

# 国家地理文件
shpfilename = shpreader.natural_earth(resolution=\'110m\', category=\'cultural\', name=\'admin_0_countries\')

# 地理信息文件阅读器
reader = shpreader.Reader(shpfilename)
# 获取文件中国家地理信息
countries_map = reader.records()

由于获取的json文件转换,接下来用采数据字典对应ascii码将代码转换,不使用往常上课的导入到表格再将表格转换成图片对应的方式

 

# 定义字典方便根据名字找地理信息
# 疫情国家与地图国家映射关系
names_dict = {\'斐济\':\'斐濟\', \'苏里南\':\'蘇利南\',\'加纳\':\'迦納\',\'几内亚比绍\':\'幾內亞比索\',\'中非共和国\':\'中非共和國\',\'赞比亚共和国\':\'赞比亚\',
              \'中国\':\'中华人民共和国\',\'亚美尼亚\':\'亞美尼亞\',\'刚果(金)\':\'刚果民主共和国\', \'刚果(布)\':\'刚果共和国\',\'北马其顿\':\'馬其頓共和國\',
              \'罗马尼亚\':\'羅馬尼亞\',\'博茨瓦纳\':\'波札那\',\'马里\':\'马里共和国\',\'圣马丁岛\':\'荷属圣马丁\',\'塞浦路斯\':\'賽普勒斯\',\'多米尼加\':\'多明尼加\', 
              \'委内瑞拉\':\'委內瑞拉\', \'尼日利亚\':\'奈及利亞\',\'韩国\':\'大韩民国\',\'布隆迪共和国\':\'蒲隆地\',\'黑山\':\'蒙特內哥羅\',\'蒙古\':\'蒙古国\',
              \'荷兰\':\'荷蘭\',\'苏丹\':\'苏丹共和国\',\'突尼斯\':\'突尼西亞\',\'白俄罗斯\':\'白罗斯\',\'玻利维亚\':\'玻利維亞\',\'爱尔兰\':\'爱尔兰共和国\', 
              \'津巴布韦\':\'辛巴威\', \'波黑\':\'波斯尼亚和黑塞哥维那\', \'新喀里多尼亚\':\'新喀里多尼亞\',\'斯里兰卡\':\'斯里蘭卡\', \'也门共和国\':\'也门\',
              \'阿联酋\':\'阿拉伯联合酋长国\',\'拉脱维亚\':\'拉脫維亞\'
}

# 获取画布与坐标系
fig, ax = plt.subplots(figsize=(10, 6), subplot_kw={\'projection\': ccrs.PlateCarree()})


#  把所有的国家设定绘制颜色为灰色的
geom_dict = {}

# 最大值
vmax = 10000
cmap = plt.cm.YlOrRd

# 边框色
edgecolor = \'black\'

# 初始疫情地图颜色, 准备国家名字与其地理位置的对应关系
for country in countries_map:
    geom_dict[country.attributes[\'NAME_ZH\']] = country.geometry

# 清空当前坐标系
ax.clear()
for country_name in datas.index:
    # 获取现有确诊数量
    value = datas[country_name]
    # 把疫情的国家名称 转换为 地图的国家名称
    tmp_name = names_dict.get(country_name) or country_name
    # 根据国家名称, 获取国家地理位置信息
    geom = geom_dict.get(tmp_name)
    # 遍历地理数据, 更新国家颜色
    if geom is not None:
        # 根据疫情数据, 生成国家颜色
        facecolor = rgb2hex(cmap(value/vmax))
        # 绘制国家颜色到地图上
        ax.add_geometries(geom, ccrs.PlateCarree(), facecolor=facecolor, edgecolor=edgecolor)

# 设置世界地图标题
ax.set_title(f\'{current_date_str} 全世界疫情地图\')

# 添加刻度条
cax = fig.add_axes([0.92, 0.2, 0.02, 0.6])
# 颜色条
norm = mpl.colors.Normalize(vmin=0, vmax=vmax)
# 调色板
cb = mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm,
                               spacing=\'proportional\')
cb.set_label(\'现有确诊人数\')


plt.show()

事实证明更具表对应字段转换的时候会快捷方便许多但是新学习的方法用字典来对应也是一种方式

 

 

 

 类似的我们还做了针对国内的

以下为国内的数据提取(利用def我们分类采集了不同时段的疫情数据)

import requests
from bs4 import BeautifulSoup
import re
import json
from tqdm import tqdm

class CoronaVirusSpider(object):

    def __init__(self):
        self.home_url = \'https://ncov.dxy.cn/ncovh5/view/pneumonia\'

    def get_content_from_url(self, url):
        """
        根据URL获取响应内容
        :param url: 请求的URL
        :return: URL的响应的内容字符串
        """
        response = requests.get(url)
        return response.content.decode()

    def parse_home_page(self, home_page, tag_id):
        """
        从疫情首页中提取数据
        """
        # 从疫情首页, 提取疫情数据
        soup = BeautifulSoup(home_page, \'lxml\')
        script = soup.find(id=tag_id)
        text = script.text
        # print(text)
        # 从疫情数据中, 获取json格式的字符串
        json_str = re.findall(r\'\[.+\]\', text)[0]
        # print(json_str)
        # 把json格式的字符串转换为Python类型
        data = json.loads(json_str)
        return data

    def load(self, path):
        """
        根据路径加载数据
        :param path:
        """
        with open(path) as fp:
            data = json.load(fp)
        return data

    def save(self, data, path):
        # print(last_day_corona_virus)
        # 以json格式保存, 最近一日各国疫情数据
        with open(path, \'w\') as fp:
            json.dump(data, fp, ensure_ascii=False)

    def crawl_last_day_corona_virus(self):
        # 获取首页内容
        home_page = self.get_content_from_url(self.home_url)
        # 解析数据
        last_day_corona_virus = self.parse_home_page(home_page, tag_id=\'getListByCountryTypeService2true\')
        # 保存数据
        self.save(last_day_corona_virus, \'last_day_corona_virus.json\')

    def crawl_corona_virus(self):
        """
        采集各国疫情数据
        :return:
        """
        # 1. 加载各国疫情数据
        last_day_corona_virus = self.load(\'last_day_corona_virus.json\')

        # 2. 定义列表, 用于存储各国疫情数据
        corona_virus = self.parse_corona_virus(last_day_corona_virus, \'采集各国疫情信息\')

        # 3. 把列表以json格式保存为文件
        self.save(corona_virus, \'corona_virus.json\')

    def crawl_last_day_corona_virus_of_china(self):
        """
        采集最近一日各省疫情数据
        """
        # 1.发送请求, 获取疫情首页
        home_page = self.get_content_from_url(self.home_url)
        # 2. 解析疫情首页, 获取最近一日各省疫情数据
        last_day_corona_virus_of_china = self.parse_home_page(home_page, tag_id=\'getAreaStat\')
        # 3. 保存疫情数据
        self.save(last_day_corona_virus_of_china, \'last_day_corona_virus_of_china.json\')

    def crawl_corona_virus_of_china(self):
        """
        采集全国各省的疫情数据
        :return:
        """
        # 加载最近一日全国疫情信息
        last_day_corona_virus_of_china = self.load(\'last_day_corona_virus_of_china.json\')

        # 遍历最近一日全国疫情信息, 获取各省疫情URL
        corona_virus = self.parse_corona_virus(last_day_corona_virus_of_china, \'采集各省疫情信息\')
        # 以json格式保存疫情信息
        self.save(corona_virus, \'corona_virus_of_china.json\')

    def parse_corona_virus(self, last_day_corona_virus_of_china, desc):
        # 定义列表, 用于存储各国疫情数据
        corona_virus = []
        # 2. 遍历各国疫情数据, 获取统计的URL
        for country in tqdm(last_day_corona_virus_of_china, desc):
            # 发送请求, 获取各省疫情json字符串
            statistics_data_url = country[\'statisticsData\']
            statistics_data_json_str = self.get_content_from_url(statistics_data_url)
            # print(statistics_data_json_str)
            # 4. 解析各省疫情json字符串, 并添加列表中
            statistics_data = json.loads(statistics_data_json_str)[\'data\']
            # print(statistics_data)
            for one_day in statistics_data:
                one_day[\'provinceName\'] = country[\'provinceName\']
                if country.get(\'countryShortCode\'):
                    one_day[\'countryShortCode\'] = country[\'countryShortCode\']

            # print(statistics_data)
            corona_virus.extend(statistics_data)
            # print(corona_virus)
        return corona_virus

    def run(self):
        # self.crawl_last_day_corona_virus()
        self.crawl_corona_virus()
        # self.crawl_last_day_corona_virus_of_china()
        self.crawl_corona_virus_of_china()

if __name__ == \'__main__\':
    spider = CoronaVirusSpider()
    spider.run()

 

至此我们所有的数据采集任务都全部完成了,接下来重复之前的步骤进行数据可视化

 

 

我们这里做了一些改变不再沿用数据字典对应的方式

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.figure
from matplotlib.patches import Polygon
from mpl_toolkits.basemap import Basemap

# 准备行政区域确诊分布数据
last_day_corona_virus_of_china = pd.read_json(\'last_day_corona_virus_of_china.json\')
current_confirmed_count = last_day_corona_virus_of_china.pivot_table(values=\'currentConfirmedCount\', index=\'provinceName\')
data = current_confirmed_count.to_dict()[\'currentConfirmedCount\']

# 绘制行政区域确诊分布数据
lat_min = 0
lat_max = 60
lon_min = 70
lon_max = 140

handles = [
    matplotlib.patches.Patch(color=\'#ffaa85\', alpha=1, linewidth=0),
    matplotlib.patches.Patch(color=\'#ff7b69\', alpha=1, linewidth=0),
    matplotlib.patches.Patch(color=\'#bf2121\', alpha=1, linewidth=0),
    matplotlib.patches.Patch(color=\'#7f1818\', alpha=1, linewidth=0),
]
labels = [\'1-9人\', \'10-99人\', \'100-999人\', \'>1000人\']

fig = plt.figure(figsize=(8, 10))

axes = fig.add_axes((0.1, 0.12, 0.8, 0.8))  # rect = l,b,w,h
m = Basemap(llcrnrlon=lon_min, urcrnrlon=lon_max, llcrnrlat=lat_min, urcrnrlat=lat_max, resolution=\'l\', ax=axes)
m.readshapefile(\'china-shapefiles-master/china\', \'province\', drawbounds=True)
m.readshapefile(\'china-shapefiles-master/china_nine_dotted_line\', \'p\', drawbounds=True)
 # 洲际线
m.drawcoastlines(color=\'black\') 
# 国界线
m.drawcountries(color=\'black\')  
# 画经度线
m.drawparallels(np.arange(lat_min, lat_max, 10), labels=[1, 0, 0, 0])
# 画纬度线
m.drawmeridians(np.arange(lon_min, lon_max, 10), labels=[0, 0, 0, 1])  

for info, shape in zip(m.province_info, m.province):
    pname = info[\'OWNER\'].strip(\'\x00\')
    fcname = info[\'FCNAME\'].strip(\'\x00\')
    if pname != fcname:  # 不绘制海岛
        continue

    for key in data.keys():
        if key in pname:
            if data[key] == 0:
                color = \'#f0f0f0\'
            elif data[key] < 10:
                color = \'#ffaa85\'
            elif data[key] < 100:
                color = \'#ff7b69\'
            elif data[key] < 1000:
                color = \'#bf2121\'
            else:
                color = \'#7f1818\'
            break

    poly = Polygon(shape, facecolor=color, edgecolor=color)
    axes.add_patch(poly)

axes.legend(handles, labels, bbox_to_anchor=(0.5, -0.11), loc=\'lower center\', ncol=4)
axes.set_title("中国新冠疫情地图")
fig.savefig(\'中国新冠疫情地图.png\')
plt.show()

5.学习总结 

我们在本学期对于学习爬虫部分的课程其实不是很多,课下因为期末作业的原因我上网搜了很多教程来学习和做这个,网上有很多关于爬虫的案例,学习非常方便,但是盲目复制代码去照葫芦画瓢就会出现很多问题,其中最严重的莫过于封IP,将导致我无法在访问网站同服务器下的所有页面。但也学会了很多,比如字典对应方式,利用便利和正则表达式去优化我们的程序,加深了我对里面内容的理解,正则表达式其实也没有那么难理解,不过我也只是能用的程度,json学了很多,在导出json文件出了很多错误,这里需要注意很多东西,比如encode=utf—8和你爬取的网页ascii码不兼容Jytper问题。

分类:

技术点:

相关文章: