soarli

百度CV-Day1实践课课堂笔记
课程目标:使用爬虫爬取丁香园上的公开疫情数据,并使数据使用图表形式实现可视化。一、数据准备上网的全过程:普通用户打...
扫描右侧二维码阅读全文
04
2020/04

百度CV-Day1实践课课堂笔记

课程目标:使用爬虫爬取丁香园上的公开疫情数据,并使数据使用图表形式实现可视化。

一、数据准备

上网的全过程:

  • 普通用户

打开浏览器 --> 往目标站点发送请求 --> 接收响应数据 --> 渲染到页面上。

  • 爬虫程序

模拟浏览器 --> 往目标站点发送请求 --> 接收响应数据 --> 提取有用的数据 --> 保存到本地/数据库。

爬虫的过程:

  1. 发送请求(requests模块)
  2. 获取响应数据(服务器返回)
  3. 解析并提取数据(re正则)
  4. 保存数据

模块准备:

request模块:

requests是python实现的简单易用的HTTP库,官网地址:http://cn.python-requests.org/zh_CN/latest/

re模块:

re模块是python用于匹配字符串的模块,该模块中提供的很多功能是基于正则表达式实现的,

爬虫代码:

import json
import re
import requests
import datetime

today = datetime.date.today().strftime('%Y%m%d')   #20200315

def crawl_dxy_data():
    """
    爬取丁香园实时统计数据,保存到data目录下,以当前日期作为文件名,存JSON文件
    """
    response = requests.get('https://ncov.dxy.cn/ncovh5/view/pneumonia') #request.get()用于请求目标网站
    print(response.status_code)                                          # 打印状态码


    try:
        url_text = response.content.decode()                             #更推荐使用response.content.deocde()的方式获取响应的html页面
        #print(url_text)
        url_content = re.search(r'window.getAreaStat = (.*?)}]}catch',   #re.search():扫描字符串以查找正则表达式模式产生匹配项的第一个位置 ,然后返回相应的match对象。
                                url_text, re.S)                          #在字符串a中,包含换行符\n,在这种情况下:如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始;
                                                                         #而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。
        texts = url_content.group()                                      #获取匹配正则表达式的整体结果
        content = texts.replace('window.getAreaStat = ', '').replace('}catch', '') #去除多余的字符
        json_data = json.loads(content)                                         
        with open('data/' + today + '.json', 'w', encoding='UTF-8') as f:
            json.dump(json_data, f, ensure_ascii=False)
    except:
        print('<Response [%s]>' % response.status_code)


def crawl_statistics_data():
    """
    获取各个省份历史统计数据,保存到data目录下,存JSON文件
    """
    with open('data/'+ today + '.json', 'r', encoding='UTF-8') as file:
        json_array = json.loads(file.read())

    statistics_data = {}
    for province in json_array:
        response = requests.get(province['statisticsData'])
        try:
            statistics_data[province['provinceShortName']] = json.loads(response.content.decode())['data']
        except:
            print('<Response [%s]> for url: [%s]' % (response.status_code, province['statisticsData']))

    with open("data/statistics_data.json", "w", encoding='UTF-8') as f:
        json.dump(statistics_data, f, ensure_ascii=False)


if __name__ == '__main__':
    crawl_dxy_data()
    crawl_statistics_data()

爬虫最早被应用于搜索引擎,搜索引擎放出大量爬虫,去各个网站爬数据。给自己的搜索引擎提供数据支撑,方便用户搜索数据。但是也存在大量的恶意爬虫,它们会对目标网站造成危害,因此有了“反爬虫”的概念。近年来,爬虫与反爬虫在相互对抗中形成了平衡。

整体思路:向服务器请求数据,并通过正则表达式提取数据,随后将获取到的数据序列化之后保存为json,作为python的一个对象。

二、疫情地图(数据可视化)

'''
安装第三方库pyecharts ,如果下载时出现断线和速度过慢的问题导致下载失败,可以尝试使用清华镜像
'''
#!pip install pyecharts
!pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyecharts

Echarts 是一个由百度开源的数据可视化工具,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可。而 Python 是一门富有表达力的语言,很适合用于数据处理。当数据分析遇上数据可视化时,pyecharts 诞生了。pyecharts api可以参考:https://pyecharts.org/#/zh-cn/chart_api

使用 options 配置项,在 pyecharts 中,一切皆 Options。

主要分为全局配置组件和系列配置组件。

(1)系列配置项 set_series_opts(),可配置图元样式、文字样式、标签样式、点线样式等;

(2)全局配置项 set_global_opts(),可配置标题、动画、坐标轴、图例等;

先来认识下全局配置组件吧

全国疫情地图:

import json
import datetime
from pyecharts.charts import Map
from pyecharts import options as opts

# 读原始数据文件
today = datetime.date.today().strftime('%Y%m%d')   #20200315
datafile = 'data/'+ today + '.json'
with open(datafile, 'r', encoding='UTF-8') as file:
    json_array = json.loads(file.read())

# 分析全国实时确诊数据:'confirmedCount'字段
china_data = []
for province in json_array:
    china_data.append((province['provinceShortName'], province['confirmedCount']))
china_data = sorted(china_data, key=lambda x: x[1], reverse=True)                 #reverse=True,表示降序,反之升序

print(china_data)
# 全国疫情地图
# 自定义的每一段的范围,以及每一段的特别的样式。
pieces = [
    {'min': 10000, 'color': '#540d0d'},
    {'max': 9999, 'min': 1000, 'color': '#9c1414'},
    {'max': 999, 'min': 500, 'color': '#d92727'},
    {'max': 499, 'min': 100, 'color': '#ed3232'},
    {'max': 99, 'min': 10, 'color': '#f27777'},
    {'max': 9, 'min': 1, 'color': '#f7adad'},
    {'max': 0, 'color': '#f7e4e4'},
]
labels = [data[0] for data in china_data]
counts = [data[1] for data in china_data]

m = Map()
m.add("累计确诊", [list(z) for z in zip(labels, counts)], 'china')

#系列配置项,可配置图元样式、文字样式、标签样式、点线样式等
m.set_series_opts(label_opts=opts.LabelOpts(font_size=12),
                  is_show=False)
#全局配置项,可配置标题、动画、坐标轴、图例等
m.set_global_opts(title_opts=opts.TitleOpts(title='全国实时确诊数据',
                                            subtitle='数据来源:丁香园'),
                  legend_opts=opts.LegendOpts(is_show=False),
                  visualmap_opts=opts.VisualMapOpts(pieces=pieces,
                                                    is_piecewise=True,   #是否为分段型
                                                    is_show=True))       #是否显示视觉映射配置
#render()会生成本地 HTML 文件,默认会在当前目录生成 render.html 文件,也可以传入路径参数,如 m.render("mycharts.html")
m.render(path='/home/aistudio/data/全国实时确诊数据.html')

湖北省疫情地图:

import json
import datetime
from pyecharts.charts import Map
from pyecharts import options as opts

# 读原始数据文件
today = datetime.date.today().strftime('%Y%m%d')   #20200315
datafile = 'data/'+ today + '.json'
with open(datafile, 'r', encoding='UTF-8') as file:
    json_array = json.loads(file.read())

# 分析湖北省实时确诊数据
# 读入规范化的城市名称,用于规范化丁香园数据中的城市简称
with open('/home/aistudio/data/data24815/pycharts_city.txt', 'r', encoding='UTF-8') as f:
    defined_cities = [line.strip() for line in f.readlines()]


def format_city_name(name, defined_cities):
    for defined_city in defined_cities:
        if len((set(defined_city) & set(name))) == len(name):
            name = defined_city
            if name.endswith('市') or name.endswith('区') or name.endswith('县') or name.endswith('自治州'):
                return name
            return name + '市'
    return None


province_name = '湖北'
for province in json_array:
    if province['provinceName'] == province_name or province['provinceShortName'] == province_name:
        json_array_province = province['cities']
        hubei_data = [(format_city_name(city['cityName'], defined_cities), city['confirmedCount']) for city in
                      json_array_province]
        hubei_data = sorted(hubei_data, key=lambda x: x[1], reverse=True)

        print(hubei_data)

labels = [data[0] for data in hubei_data]
counts = [data[1] for data in hubei_data]
pieces = [
    {'min': 10000, 'color': '#540d0d'},
    {'max': 9999, 'min': 1000, 'color': '#9c1414'},
    {'max': 999, 'min': 500, 'color': '#d92727'},
    {'max': 499, 'min': 100, 'color': '#ed3232'},
    {'max': 99, 'min': 10, 'color': '#f27777'},
    {'max': 9, 'min': 1, 'color': '#f7adad'},
    {'max': 0, 'color': '#f7e4e4'},
]

m = Map()
m.add("累计确诊", [list(z) for z in zip(labels, counts)], '湖北')
m.set_series_opts(label_opts=opts.LabelOpts(font_size=12),
                  is_show=False)
m.set_global_opts(title_opts=opts.TitleOpts(title='湖北省实时确诊数据',
                                            subtitle='数据来源:丁香园'),
                  legend_opts=opts.LegendOpts(is_show=False),
                  visualmap_opts=opts.VisualMapOpts(pieces=pieces,
                                                    is_piecewise=True,
                                                    is_show=True))
m.render(path='/home/aistudio/data/湖北省实时确诊数据.html')

疫情增长趋势图:

import numpy as np
import json
from pyecharts.charts import Line
from pyecharts import options as opts

# 读原始数据文件
datafile = 'data/statistics_data.json'
with open(datafile, 'r', encoding='UTF-8') as file:
    json_dict = json.loads(file.read())

# 分析各省份2月1日至今的新增确诊数据:'confirmedIncr'
statistics__data = {}
for province in json_dict:
    statistics__data[province] = []
    for da in json_dict[province]:
        if da['dateId'] >= 20200201:
            statistics__data[province].append(da['confirmedIncr'])

# 获取日期列表
dateId = [str(da['dateId'])[4:6] + '-' + str(da['dateId'])[6:8] for da in json_dict['湖北'] if
          da['dateId'] >= 20200201]

# 全国新增趋势
all_statis = np.array([0] * len(dateId))
for province in statistics__data:
    all_statis = all_statis + np.array(statistics__data[province])

all_statis = all_statis.tolist()
# 湖北新增趋势
hubei_statis = statistics__data['湖北']
# 湖北以外的新增趋势
other_statis = [all_statis[i] - hubei_statis[i] for i in range(len(dateId))]

line = Line()
line.add_xaxis(dateId)
line.add_yaxis("全国新增确诊病例",   #图例
                all_statis,       #数据
                is_smooth=True,   #是否平滑曲线
               linestyle_opts=opts.LineStyleOpts(width=4, color='#B44038'),#线样式配置项
               itemstyle_opts=opts.ItemStyleOpts(color='#B44038',          #图元样式配置项
                                                 border_color="#B44038",   #颜色
                                                 border_width=10))         #图元的大小
line.add_yaxis("湖北新增确诊病例", hubei_statis, is_smooth=True,
               linestyle_opts=opts.LineStyleOpts(width=2, color='#4E87ED'),
               label_opts=opts.LabelOpts(position='bottom'),              #标签在折线的底部
               itemstyle_opts=opts.ItemStyleOpts(color='#4E87ED',
                                                 border_color="#4E87ED",
                                                 border_width=3))
line.add_yaxis("其他省份新增病例", other_statis, is_smooth=True,
               linestyle_opts=opts.LineStyleOpts(width=2, color='#F1A846'),
               label_opts=opts.LabelOpts(position='bottom'),              #标签在折线的底部
               itemstyle_opts=opts.ItemStyleOpts(color='#F1A846',
                                                 border_color="#F1A846",
                                                 border_width=3))
line.set_global_opts(title_opts=opts.TitleOpts(title="新增确诊病例", subtitle='数据来源:丁香园'),
                     yaxis_opts=opts.AxisOpts(max_=16000, min_=1, type_="log",    #坐标轴配置项
                                              splitline_opts=opts.SplitLineOpts(is_show=True),#分割线配置项
                                              axisline_opts=opts.AxisLineOpts(is_show=True)))#坐标轴刻度线配置项
line.render(path='/home/aistudio/data/新增确诊趋势图.html')

作业:

作业1:飞桨本地安装

提交飞桨本地安装成功的截图,如下图所示,有安装问题可以随时在群里提问,可参考飞桨官网:https://www.paddlepaddle.org.cn/documentation/docs/zh/install/index_cn.html

作业2:新冠疫情可视化

请根据课上所学内容,爬取3月31日当天丁香园公开的统计数据,根据累计确诊数,使用pyecharts绘制疫情分布图,如下图所示,提交截图。

Pycharts api可参考:https://pyecharts.org/#/zh-cn/

可视化,是一种利用计算机图形学和图像处理技术,将数据转换成图像在屏幕上显示出来,再进行交互处理的理论、方法和技术。

本次实践基于丁香园公开的统计数据,实现新冠疫情可视化,包括疫情地图、疫情增长趋势图、疫情分布图等。

全国疫情地图如下:

疫情增长趋势图如下:

提交的作业:

一不小心得了100分,哈哈!

作业1:

安装成功的截图(AI studio):

安装成功的截图(win 10):

作业2:(4月1号的数据)

程序1:

import json
import re
import requests
import datetime

today = datetime.date.today().strftime('%Y%m%d')   #20200315

def crawl_dxy_data():
    """
    爬取丁香园实时统计数据,保存到data目录下,以当前日期作为文件名,存JSON文件
    """
    response = requests.get('https://ncov.dxy.cn/ncovh5/view/pneumonia') #request.get()用于请求目标网站
    print(response.status_code)                                          # 打印状态码


    try:
        url_text = response.content.decode()                             #更推荐使用response.content.deocde()的方式获取响应的html页面
        #print(url_text)
        url_content = re.search(r'window.getAreaStat = (.*?)}]}catch',   #re.search():扫描字符串以查找正则表达式模式产生匹配项的第一个位置 ,然后返回相应的match对象。
                                url_text, re.S)                          #在字符串a中,包含换行符\n,在这种情况下:如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始;
                                                                         #而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。
        texts = url_content.group()                                      #获取匹配正则表达式的整体结果
        content = texts.replace('window.getAreaStat = ', '').replace('}catch', '') #去除多余的字符
        json_data = json.loads(content)                                         
        with open('data/' + today + '.json', 'w', encoding='UTF-8') as f:
            json.dump(json_data, f, ensure_ascii=False)
    except:
        print('<Response [%s]>' % response.status_code)


def crawl_statistics_data():
    """
    获取各个省份历史统计数据,保存到data目录下,存JSON文件
    """
    with open('data/'+ today + '.json', 'r', encoding='UTF-8') as file:
        json_array = json.loads(file.read())

    statistics_data = {}
    for province in json_array:
        response = requests.get(province['statisticsData'])
        try:
            statistics_data[province['provinceShortName']] = json.loads(response.content.decode())['data']
        except:
            print('<Response [%s]> for url: [%s]' % (response.status_code, province['statisticsData']))

    with open("data/statistics_data.json", "w", encoding='UTF-8') as f:
        json.dump(statistics_data, f, ensure_ascii=False)


if __name__ == '__main__':
    crawl_dxy_data()
    crawl_statistics_data()

程序2:

import json
import datetime
from pyecharts.charts import Pie
from pyecharts import options as opts
# 读原始数据文件
today = datetime.date.today().strftime('%Y%m%d') #20200315
datafile = 'data/'+ today + '.json'
with open(datafile, 'r', encoding='UTF-8') as file:
    json_array = json.loads(file.read())
# 分析全国实时确诊数据:'confirmedCount'字段
china_data = []
for province in json_array: 
    china_data.append((province['provinceShortName'], province['confirmedCount']))
china_data = sorted(china_data, key=lambda x: x[1], reverse=True) #reverse=True,表示降序,反之升序
# 全国疫情地图
# 自定义的每一段的范围,以及每一段的特别的样式。 
labels = [data[0] for data in china_data]
counts = [data[1] for data in china_data]
m = Pie(init_opts=opts.InitOpts(width="800px",height="800px")) 
m.add("累计确诊", [list(z) for z in zip(labels, counts)],center=["50%","70%"],radius=[0,140])
m.center=["0%","0%"]
m.set_global_opts( title_opts=opts.TitleOpts(title='全国实时确诊数据', subtitle='数据来源:丁香园'), 
legend_opts=opts.LegendOpts(pos_right="80",pos_bottom="0%", orient="vertical"), )
m.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
#render()会生成本地 HTML 文件,默认会在当前目录生成 render.html 文件,也可以传入路径参数,如 m.render("mycharts.html")
m.render(path='全国实时确诊数据.html')

生成的HTML代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Awesome-pyecharts</title>
            <script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>

</head>
<body>
    <div id="082384f56ef5477da16c9de5f7e74591" class="chart-container" style="width:800px; height:800px;"></div>
    <script>
        var chart_082384f56ef5477da16c9de5f7e74591 = echarts.init(
            document.getElementById('082384f56ef5477da16c9de5f7e74591'), 'white', {renderer: 'canvas'});
        var option_082384f56ef5477da16c9de5f7e74591 = {
    "animation": true,
    "animationThreshold": 2000,
    "animationDuration": 1000,
    "animationEasing": "cubicOut",
    "animationDelay": 0,
    "animationDurationUpdate": 300,
    "animationEasingUpdate": "cubicOut",
    "animationDelayUpdate": 0,
    "color": [
        "#c23531",
        "#2f4554",
        "#61a0a8",
        "#d48265",
        "#749f83",
        "#ca8622",
        "#bda29a",
        "#6e7074",
        "#546570",
        "#c4ccd3",
        "#f05b72",
        "#ef5b9c",
        "#f47920",
        "#905a3d",
        "#fab27b",
        "#2a5caa",
        "#444693",
        "#726930",
        "#b2d235",
        "#6d8346",
        "#ac6767",
        "#1d953f",
        "#6950a1",
        "#918597"
    ],
    "series": [
        {
            "type": "pie",
            "name": "\u7d2f\u8ba1\u786e\u8bca",
            "clockwise": true,
            "data": [
                {
                    "name": "\u6e56\u5317",
                    "value": 67802
                },
                {
                    "name": "\u5e7f\u4e1c",
                    "value": 1501
                },
                {
                    "name": "\u6cb3\u5357",
                    "value": 1276
                },
                {
                    "name": "\u6d59\u6c5f",
                    "value": 1257
                },
                {
                    "name": "\u6e56\u5357",
                    "value": 1018
                },
                {
                    "name": "\u5b89\u5fbd",
                    "value": 990
                },
                {
                    "name": "\u6c5f\u897f",
                    "value": 937
                },
                {
                    "name": "\u5c71\u4e1c",
                    "value": 774
                },
                {
                    "name": "\u9999\u6e2f",
                    "value": 765
                },
                {
                    "name": "\u6c5f\u82cf",
                    "value": 646
                },
                {
                    "name": "\u5317\u4eac",
                    "value": 580
                },
                {
                    "name": "\u91cd\u5e86",
                    "value": 579
                },
                {
                    "name": "\u56db\u5ddd",
                    "value": 552
                },
                {
                    "name": "\u4e0a\u6d77",
                    "value": 516
                },
                {
                    "name": "\u9ed1\u9f99\u6c5f",
                    "value": 484
                },
                {
                    "name": "\u798f\u5efa",
                    "value": 345
                },
                {
                    "name": "\u6cb3\u5317",
                    "value": 323
                },
                {
                    "name": "\u53f0\u6e7e",
                    "value": 322
                },
                {
                    "name": "\u9655\u897f",
                    "value": 255
                },
                {
                    "name": "\u5e7f\u897f",
                    "value": 254
                },
                {
                    "name": "\u4e91\u5357",
                    "value": 182
                },
                {
                    "name": "\u5929\u6d25",
                    "value": 174
                },
                {
                    "name": "\u6d77\u5357",
                    "value": 168
                },
                {
                    "name": "\u8d35\u5dde",
                    "value": 147
                },
                {
                    "name": "\u8fbd\u5b81",
                    "value": 140
                },
                {
                    "name": "\u7518\u8083",
                    "value": 138
                },
                {
                    "name": "\u5c71\u897f",
                    "value": 137
                },
                {
                    "name": "\u5185\u8499\u53e4",
                    "value": 111
                },
                {
                    "name": "\u5409\u6797",
                    "value": 98
                },
                {
                    "name": "\u65b0\u7586",
                    "value": 76
                },
                {
                    "name": "\u5b81\u590f",
                    "value": 75
                },
                {
                    "name": "\u6fb3\u95e8",
                    "value": 41
                },
                {
                    "name": "\u9752\u6d77",
                    "value": 18
                },
                {
                    "name": "\u897f\u85cf",
                    "value": 1
                }
            ],
            "radius": [
                0,
                140
            ],
            "center": [
                "50%",
                "70%"
            ],
            "label": {
                "show": true,
                "position": "top",
                "margin": 8,
                "formatter": "{b}: {c}"
            },
            "rippleEffect": {
                "show": true,
                "brushType": "stroke",
                "scale": 2.5,
                "period": 4
            }
        }
    ],
    "legend": [
        {
            "data": [
                "\u6e56\u5317",
                "\u5e7f\u4e1c",
                "\u6cb3\u5357",
                "\u6d59\u6c5f",
                "\u6e56\u5357",
                "\u5b89\u5fbd",
                "\u6c5f\u897f",
                "\u5c71\u4e1c",
                "\u9999\u6e2f",
                "\u6c5f\u82cf",
                "\u5317\u4eac",
                "\u91cd\u5e86",
                "\u56db\u5ddd",
                "\u4e0a\u6d77",
                "\u9ed1\u9f99\u6c5f",
                "\u798f\u5efa",
                "\u6cb3\u5317",
                "\u53f0\u6e7e",
                "\u9655\u897f",
                "\u5e7f\u897f",
                "\u4e91\u5357",
                "\u5929\u6d25",
                "\u6d77\u5357",
                "\u8d35\u5dde",
                "\u8fbd\u5b81",
                "\u7518\u8083",
                "\u5c71\u897f",
                "\u5185\u8499\u53e4",
                "\u5409\u6797",
                "\u65b0\u7586",
                "\u5b81\u590f",
                "\u6fb3\u95e8",
                "\u9752\u6d77",
                "\u897f\u85cf"
            ],
            "selected": {},
            "show": true,
            "right": "80",
            "bottom": "0%",
            "orient": "vertical",
            "padding": 5,
            "itemGap": 10,
            "itemWidth": 25,
            "itemHeight": 14
        }
    ],
    "tooltip": {
        "show": true,
        "trigger": "item",
        "triggerOn": "mousemove|click",
        "axisPointer": {
            "type": "line"
        },
        "textStyle": {
            "fontSize": 14
        },
        "borderWidth": 0
    },
    "title": [
        {
            "text": "\u5168\u56fd\u5b9e\u65f6\u786e\u8bca\u6570\u636e",
            "subtext": "\u6570\u636e\u6765\u6e90\uff1a\u4e01\u9999\u56ed",
            "padding": 5,
            "itemGap": 10
        }
    ]
};
        chart_082384f56ef5477da16c9de5f7e74591.setOption(option_082384f56ef5477da16c9de5f7e74591);
    </script>
</body>
</html>
最后修改:2020 年 04 月 04 日 08 : 27 PM

发表评论