用 Python 画一张北京实时疫情地图:从爬虫到绘图到自动上传服务器

发布时间:2022-08-31 11:30

效果

在线地图

闲谈

我校传统是五一放七天,五一前两天开始放,当时疫情初见端倪,但是网上并没有找到准确显示病例位置的地图

后来知道腾讯地图是有的,但是不支持选择时间区间,所以把很早之前的也都显示了出来,整个地图很乱,几乎没有实际价值

所以出去玩前花了2个小时做了个简易版的地图,然后就快快乐乐和人出去了。最近又加上了爬虫,自动上传服务器,高德API并发,觉得可以把这个大杂烩发出来了

需求

  1. 数据爬取:北京卫健委,BeautifulSoup
  2. 坐标获取:高德地图API
  3. 地图绘制:Folium
  4. 自动上传服务器:paramiko

数据爬取

注意

需要说在前面的是,适度爬虫,不要给服务器造成压力,比如做好本地存储避免重复访问,限制爬虫频率等,我这里限制了至少间隔 1s 才能访问一次

元素定位

爬虫都是就题发挥,这里提取所有 a标签,然后筛选出 title 包含“日”、“月”、“新增”、“例”的。值得注意的是,北京卫健委有很简单的反爬虫机制,你需要把 User-Agent 伪装一下

def update(url='http://wjw.beijing.gov.cn/xwzx_20031/wnxw/'):
    '''北京卫健委爬虫'''
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"}
    bs = BeautifulSoup(
        kind_get(url, headers=headers).text, 'lxml')
    for news in bs.find_all('a'):
        try:
            title = news['title']
        except KeyError:
            pass
        else:
            if title.find('月') != -1 and title.find('日') != -1 and title.find('例') != -1:
                print(title)

然后把月和日用正则表达式提取出来,再访问 href ,进一步把所有含有"现住"的 p标签 提取出来

用 Python 画一张北京实时疫情地图:从爬虫到绘图到自动上传服务器_第1张图片

 date = re.search('(?<=北京).*?(?=新增)', title)
 if date:
     m, d, _ = re.split("[月日]", date.group())
     path = './data/{}{}.txt'.format(m.zfill(2), d.zfill(2))
     if not os.path.exists(path):
         bs_news = BeautifulSoup(
             kind_get(parse.urljoin(url, news['href']), headers=headers).text, 'lxml')
         try:
             with open(path, 'w', encoding='utf-8') as file:
                 article = bs_news.find('div', class_='view')
                 for item in article.find_all('p'):
                     if item.text.find('现住') != -1:
                         file.write(item.text+'\n')
                 print(date.group(), '数据文件添加成功')
         except AttributeError:
             print(article)
             print(date.group(), '数据文件添加异常,可能当日病例没有明确地址')
             os.remove(path)
     else:
         print(date.group(), '数据文件已存在')

提取出来的数据大概长这样

确诊病例1、3、4、19:现住朝阳区劲松街道农光里。5月6日诊断为确诊病例,临床分型均为轻型。

确诊病例2:现住朝阳区高碑店西店社区义安门兰花巷。5月6日诊断为确诊病例,临床分型为轻型。

确诊病例5、6、7:现住海淀区甘家口街道甘东社区。5月6日诊断为确诊病例,临床分型均为轻型。

确诊病例8:现住海淀区甘家口街道增光佳苑。5月6日诊断为确诊病例,临床分型为轻型。

确诊病例9:现住海淀区西北旺镇大牛坊社区。5月6日诊断为确诊病例,临床分型为轻型。

数据分析

接下来就是拆分出每个病例的住址了,这里最普遍的格式是 确诊病例x,现住xxxxx。,但是还有很多奇奇怪怪的格式

  • 确诊病例123,现住xxxxx。

    用正则表达式提取“现住”和“。”的中间

  • 确诊病例1、45、47至48,现住xxxx。

    这里病例数不是 1,先用正则匹配出“病例”和“,”中间,然后先按“、"分隔一次,再用至分隔一次,然后一个个加起来

  • 确诊病例3,现住址和1相同。

    与其倒回去找1,不如在病例 1 的时候用正则表达式提取整个文章出现几次 1。这里提取 1 有一点点难度,因为要排除 12 这种,我编写的正则表达式是 f"(?<=[^0-9]){int(ID)}(?=[^0-9])",ID是对应病例编号,这个表达式的意义是找 ID 且左右两边不是数字。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号