Python 获得NOAA全球开放气象数据

  参考文章: https://zhuanlan.zhihu.com/p/362808034

  NOAA全球气象数据下载地址:https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/

  气象数据字段属性说明:https://www.ncei.noaa.gov/data/global-summary-of-the-day/doc/readme.txt

  本文所有代码下载地址:https://gitee.com/liuyueming/noaa.git

  在学习Python的过程中接触到一个项目要求分析气象数据在github上找到一个爬取数据并生成图表的源码,于是修改了一下源码写了一个小爬虫程序:https://www.cnblogs.com/minseo/p/15723258.html

  只能简单通过python爬取一个城市的气象信息,感觉功能简单了一些,于是百度找到了一个可以通过下载noaa所有气象数据的文章并参考改文章写了一个可以爬取并存储大量气象数据的爬虫

  原作者把noaa站点的全球数据都下载下来了,执行下载即持久化的时间太长,我就简化了一点,下载数据以后只把中国的数据进行持久化存储

  本项目流程如下

 

 

  一,noaa数据字段分析

  首先我们手动下载压缩文件tar.gz

 

   解压缩以后是csv文件,文件名是气象站点id共11位,命名格式为气象站点编号+099999,例如江西吉安的气象站点编号是57799则文件名为57799099999

 

   在来看文件字段内容

 

   一共有28个字段分别代表的意思如下,翻译可能不准确,可以查看官方的字段说明:https://www.ncei.noaa.gov/data/global-summary-of-the-day/doc/readme.txt

 `station` char(11) NOT NULL COMMENT '站点ID',
  `date` date NOT NULL COMMENT '日期',
  `latitude` float DEFAULT NULL COMMENT '纬度',
  `longitude` float DEFAULT NULL COMMENT '经度',
  `elevation` float DEFAULT NULL COMMENT '海拔',
  `name` varchar(50) DEFAULT NULL COMMENT '城市英文名',
  `temp` float DEFAULT NULL COMMENT '平均温度',
  `temp_attributes` int(11) DEFAULT NULL COMMENT '计算平均温度的观测次数',
  `dewp` float DEFAULT NULL COMMENT '平均露点',
  `dewp_attributes` int(11) DEFAULT NULL COMMENT '计算平均露点的观测次数',
  `slp` float DEFAULT NULL COMMENT '海平面压力',
  `slp_attributes` int(11) DEFAULT NULL COMMENT '计算海平面压力的观测次数',
  `stp` float DEFAULT NULL COMMENT '当天平均站压',
  `stp_attributes` int(11) DEFAULT NULL COMMENT '平均站压的观测次数',
  `visib` float DEFAULT NULL COMMENT '能见度',
  `visib_attributes` int(11) DEFAULT NULL COMMENT '能见度观测次数',
  `wdsp` float DEFAULT NULL COMMENT '平均风速',
  `wdsp_attributes` int(11) DEFAULT NULL COMMENT '计算平均风速的观测次数',
  `mxspd` float DEFAULT NULL COMMENT '最大持续风速',
  `gust` float DEFAULT NULL COMMENT '最大阵风',
  `max` float DEFAULT NULL COMMENT '最高气温',
  `max_attributes` varchar(10) DEFAULT NULL COMMENT '空白表示最高明确温度而非小时数据',
  `min` float DEFAULT NULL COMMENT '最低气温',
  `min_attributes` varchar(10) DEFAULT NULL COMMENT '空白表示最低明确温度而非小时数据',
  `prcp` float DEFAULT '0' COMMENT '降雨量',
  `prcp_attributes` char(1) DEFAULT NULL COMMENT '取值ABCDEFGH分别代表A 1-6小时 B 2-6小时 C 3-6小时 D 4-6小时 E 1-12小时 F 2-12小时 G 1-24小时即全天 HI不完整降雨记录 一般值为G',
  `sndp` float DEFAULT NULL COMMENT '降雪量',
  `frshtt` char(6) DEFAULT '000000' COMMENT '发生气象事件的概率默认0代表未发生1代表发生,六位分别代表Fog雾Rain雨Snow雪Hail冰雹Thunder雷电Tornado龙卷风',

  我们可以通过这些字段去创建数据表data,用于一一对应存储这些数据

  二,创建MySQL数据库表

  根据noaa的数据可以创建MySQL数据库表,建表语句如下

DROP TABLE IF EXISTS `data`;
CREATE TABLE `data` (
  `station` char(11) NOT NULL comment '站点ID',
  `date` date NOT NULL comment '日期',
  `latitude` float DEFAULT NULL comment '纬度',
  `longitude` float DEFAULT NULL comment '经度',
  `elevation` float DEFAULT NULL comment '海拔',
  `name` varchar(50) DEFAULT NULL comment '城市英文名',
  `temp` float DEFAULT NULL comment '平均温度',
  `temp_attributes` int DEFAULT NULL comment '计算平均温度的观测次数',
  `dewp` float DEFAULT NULL comment '平均露点',
  `dewp_attributes` int DEFAULT NULL comment '计算平均露点的观测次数',
  `slp` float DEFAULT NULL comment '海平面压力',
  `slp_attributes` int DEFAULT NULL comment '计算海平面压力的观测次数',
  `stp` float DEFAULT NULL comment '当天平均站压',
  `stp_attributes` int DEFAULT NULL comment '平均站压的观测次数',
  `visib` float DEFAULT NULL comment '能见度',
  `visib_attributes` int DEFAULT NULL comment '能见度观测次数',
  `wdsp` float DEFAULT NULL comment '平均风速',
  `wdsp_attributes` int DEFAULT NULL comment '计算平均风速的观测次数',
  `mxspd` float DEFAULT NULL comment '最大持续风速',
  `gust` float DEFAULT NULL comment '最大阵风',
  `max` float DEFAULT NULL comment '最高气温',
  `max_attributes` varchar(10) DEFAULT NULL comment '空白表示最高明确温度而非小时数据',
  `min` float DEFAULT NULL comment '最低气温',
  `min_attributes` varchar(10) DEFAULT NULL comment '空白表示最低明确温度而非小时数据',
  `prcp` float DEFAULT 0 comment '降雨量',
  `prcp_attributes` char(1) DEFAULT NULL comment '取值ABCDEFGH分别代表A 1-6小时 B 2-6小时 C 3-6小时 D 4-6小时 E 1-12小时 F 2-12小时 G 1-24小时即全天 HI不完整降雨记录 一般值为G',
  `sndp` float DEFAULT NULL comment '降雪量',
  `frshtt` char(6) DEFAULT '000000' comment '发生气象事件的概率默认0代表未发生1代表发生,六位分别代表Fog雾Rain雨Snow雪Hail冰雹Thunder雷电Tornado龙卷风',
  PRIMARY KEY (station, date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

  表data的字段和noaa下载下来的字段是一一对应的,然后设置联合主键 PRIMARY KEY (station, date)即站点id和日期是不能重复的,保证数据库不会插入重复数据。

  我们注意到数据里面只有经纬度信息,并没有中文的城市信息,如果我们想要搜索一个中国城市的信息首先我们还得知道对应的经纬度显然不符合我们的使用习惯,我们先创建一个表info用于存储对应的城市中文信息

  我们可以去百度申请api通过经纬度逆向查询到地址信息

  城市信息info表建表语句如下

DROP TABLE IF EXISTS `info`;
CREATE TABLE `info` (
  `station_id` char(11) NOT NULL comment '站点ID',
  `name` varchar(50) NULL comment '城市英文名',
  `latitude` float DEFAULT NULL comment '纬度',
  `longitude` float DEFAULT NULL comment '经度',
  `country` varchar(20) DEFAULT NULL comment '国家',
  `province` varchar(20) DEFAULT NULL comment '省',
  `city` varchar(20) DEFAULT NULL comment '城市',
  `district` varchar(20) DEFAULT NULL comment '县',
  PRIMARY KEY (station_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

  其中站点id字段station_id和data表的字段station是对应的,设置了主键为station_id即一个气象站点的记录是唯一的

  三,下载压缩文件

  打开需要下载页面分析一下html源码来换取我们要下载的文件信息

  查看下载页面的源码很容易知道我们需要根据url以及对应的文件名信息拼接出下载的完整url

 

 

  首先我们通过标签table使用xpath查找

import requests
import os
from lxml import etree
print("downloading with requests")
tar_gz_url = 'https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/'
# 定义请求头部信息
headers = {'User-Agent': 'M'}
# 发送请求
resp= requests.get(tar_gz_url,headers=headers)
tar_gz_resp_text = resp.text
# 根据返回的text创建etree对象才能使用xpath分析
tar_gz_etree_element = etree.HTML(tar_gz_resp_text)
tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table')
print(tar_gz_url_xpath)

  唯一匹配到一个table标签

 

   缩小范围继续查找标签tr

tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table/tr')
print(tar_gz_url_xpath)

  查到了很多个

 

   继续缩小范围查找

tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table/tr/td')
print(tar_gz_url_xpath)

  查到了更加多的td标签,我们要查找的字符是包含在a标签里面的,继续查找

tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table/tr/td/a')
print(tar_gz_url_xpath)

  同样输出很多,我们怎么确定是我们要找的a标签呢,把上面查询出来的多个对象遍历使用xpath继续分析取href属性即可

tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table/tr/td/a')
for i in tar_gz_url_xpath:
    print(i.xpath('@href'))

  输出如下

['/data/global-summary-of-the-day/']
['1929.tar.gz']
['1930.tar.gz']
['1931.tar.gz']
['1932.tar.gz']
['1933.tar.gz']
['1934.tar.gz']
['1935.tar.gz']
['1936.tar.gz']
['1937.tar.gz']
['1938.tar.gz']
['1939.tar.gz']
['1940.tar.gz']
['1941.tar.gz']
['1942.tar.gz']
['1943.tar.gz']
['1944.tar.gz']
['1945.tar.gz']
['1946.tar.gz']
['1947.tar.gz']
['1948.tar.gz']
['1949.tar.gz']
['1950.tar.gz']
['1951.tar.gz']
['1952.tar.gz']
['1953.tar.gz']
['1954.tar.gz']
['1955.tar.gz']
['1956.tar.gz']
['1957.tar.gz']
['1958.tar.gz']
['1959.tar.gz']
['1960.tar.gz']
['1961.tar.gz']
['1962.tar.gz']
['1963.tar.gz']
['1964.tar.gz']
['1965.tar.gz']
['1966.tar.gz']
['1967.tar.gz']
['1968.tar.gz']
['1969.tar.gz']
['1970.tar.gz']
['1971.tar.gz']
['1972.tar.gz']
['1973.tar.gz']
['1974.tar.gz']
['1975.tar.gz']
['1976.tar.gz']
['1977.tar.gz']
['1978.tar.gz']
['1979.tar.gz']
['1980.tar.gz']
['1981.tar.gz']
['1982.tar.gz']
['1983.tar.gz']
['1984.tar.gz']
['1985.tar.gz']
['1986.tar.gz']
['1987.tar.gz']
['1988.tar.gz']
['1989.tar.gz']
['1990.tar.gz']
['1991.tar.gz']
['1992.tar.gz']
['1993.tar.gz']
['1994.tar.gz']
['1995.tar.gz']
['1996.tar.gz']
['1997.tar.gz']
['1998.tar.gz']
['1999.tar.gz']
['2000.tar.gz']
['2001.tar.gz']
['2002.tar.gz']
['2003.tar.gz']
['2004.tar.gz']
['2005.tar.gz']
['2006.tar.gz']
['2007.tar.gz']
['2008.tar.gz']
['2009.tar.gz']
['2010.tar.gz']
['2011.tar.gz']
['2012.tar.gz']
['2013.tar.gz']
['2014.tar.gz']
['2015.tar.gz']
['2016.tar.gz']
['2017.tar.gz']
['2018.tar.gz']
['2019.tar.gz']
['2020.tar.gz']
['2021.tar.gz']

  很明显除了第一个不是我们需要拼接的其他的都是我们需要拼接下载的,我们从下标1开始遍历即可

  下面是完整的下载压缩文件的代码

import requests
import os
from lxml import etree
print("downloading with requests")
tar_gz_url = 'https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/'
# 定义请求头部信息
headers = {'User-Agent': 'M'}
# 发送请求
resp= requests.get(tar_gz_url,headers=headers)
tar_gz_resp_text = resp.text
# 根据返回的text创建etree对象才能使用xpath分析
tar_gz_etree_element = etree.HTML(tar_gz_resp_text)
tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table/tr/td/a')
# for i in tar_gz_url_xpath:
#     print(i.xpath('@href'))

for tar_gz in tar_gz_url_xpath[1:]:
    # 打印要下载的文件名
    # print(tar_gz.xpath('@href')[0])
    requests_url = tar_gz_url + tar_gz.xpath('@href')[0]
    file_name = tar_gz.xpath('@href')[0]
    # 打印拼接以后的下载链接
    # print(requests_url)
    # 通过下载链接创建对象r
    r = requests.get(requests_url)
    # 如果当前文件夹没有tar_gz目录则创建该目录
    if 'tar_gz' not in [x for x in os.listdir('.') if os.path.isdir(x)]:
        try:
            os.mkdir('tar_gz')
        except:
            print('创建文件夹失败')
    # 如果在目录tar_gz下已经有文件了则不重复下载,否则下载
    if file_name in [x for x in os.listdir('tar_gz')]:
        print('%s文件已下载'%(file_name))
    else:
        # 通过拼接的下载url下载文件,下载文件存储在目录tar_gz下
        with open(f'tar_gz/{file_name}', "wb") as code:
            code.write(r.content)
            print('下载文件%s成功'%(file_name))

  运行即可在当前目录创建文件夹tar_gz并且下载所有气象数据压缩文件,因为是国外网站可能下载时间会比较长,慢慢等待即可

 

 

  四,解压缩

  使用模块tarfile解压tar.gz文件,这个比较简单

import tarfile
t = tarfile.open(fname)
t.extractall(path = dirs)

  导入模块,打开文件,然后解压到指定文件夹

  定义一个函数用于解压缩

def untar(fname, dirs):
    t = tarfile.open(fname)
    t.extractall(path = dirs)

  解压缩完整代码如下

import tarfile
import os
def untar(fname, dirs):
    t = tarfile.open(fname)
    t.extractall(path = dirs)

# print(os.listdir('tar_gz'))
# t=tarfile.open('tar_gz/1929.tar.gz')
# t.extractall(path='tar_gz/1929')

def _decomp_tar_gz():
    # 遍历压缩包文件夹,获取的是所有压缩包文件名的list
    for tar_gz_file in os.listdir('tar_gz'):
        # print(tar_gz_file)
        # 把压缩的文件名使用.分割,取第一个元素作为解压缩文件的文件夹,例如文件2021.tar.gz则全部解压缩到文件夹tar_gz/2021下
        tar_gz_dir = tar_gz_file.split('.')[0]
        if os.path.isfile(f'tar_gz/{tar_gz_file}'):
            untar(f'tar_gz/{tar_gz_file}',f'tar_gz/{tar_gz_dir}')

_decomp_tar_gz()

  五,通过解压后的csv文件往MySQL数据库插入数据

  全部解压完毕我们获得了以年份命名的所有csv文件,下面我们遍历文件夹,读取csv文件插入到数据库

import csv
import pymysql
import os
from config import *
db = pymysql.connect(host=HOST, user=USER, passwd=PASSWORD, db=DB, charset=CHARSET)
cursor = db.cursor()

# 插入数据库函数,传递参数为csv文件路径
def insert_data_from_csv(csv_file):
    f = csv.reader(open(csv_file,'r'))
    for i in f:
        # csv第一行是字段名,排除掉
        if i[0] == "STATION":
            continue
        sql = 'insert into data values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
        # print(i)
        # 插入到数据库
        try:
            cursor.execute(sql,list(i[n] for n in range(28)))
            db.commit()
        except:
            print('插入数据错误')
      
# 遍历接压缩后的文件夹,获取csv文件,如果符合条件则把文件名作为参数传递给函数insert_data_from_csv插入数据
def insert_data():
    # 遍历文件夹tar_gz
    for tar_gz_dir in os.listdir('tar_gz'):
        # 如果是文件夹则继续遍历即只遍历下面的文件夹,排除压缩文件
        if os.path.isdir(f'tar_gz/{tar_gz_dir}'):
            # 排除压缩文件继续遍历文件夹,下面的遍历文件列表为csv文件
            for tar_gz_file in os.listdir(f'tar_gz/{tar_gz_dir}'):
                # 切割文件,如果csv文件是以5开头则代表是中国的气象站,则调用插入函数插入,否则不处理
                if f'tar_gz/{tar_gz_dir}/{tar_gz_file}'.split('/')[2][0] == '5':
                    print('是中国的气象站网数据库插入数据%s'%(f'tar_gz/{tar_gz_dir}/{tar_gz_file}'))
                    insert_data_from_csv(f'tar_gz/{tar_gz_dir}/{tar_gz_file}')
                else:
                    print('外国气象站数据不处理')

  解析:定义一个函数用于插入数据,函数传递的参数为csv文件名称,然后去csv文件内所有数据插入MySQL表,这里是完全一一对应的

    函数insert_data遍历文件夹tar_gz下面的文件夹,不对刚刚下载的压缩文件进行处理,因为数据太多我们只处理了以5开头的中国气象站的数据

    运行即可往数据库插入数据,即使只处理中国气象站的数据运行时间也比较长,运行完毕以后查看数据库就能看到所有数据了

 count(*) from data;
+----------+
| count(*) |
+----------+
|  9075398 |
+----------+
1 row in set (3.50 sec)

  居然有900多万条数据

  注意:不要使用通配符*一次性搜索所有数据,数据库会卡死

  六,插入气象站中文信息

  经过以上步骤我们已经获取了noaa所有的中国的气象数据,我们可以直接搜索数据查询数据,例如,我知道江西吉安的气象站编码是57799099999,那么我可以使用以下方法搜索气象站所有数据

select * from data where station='57799099999';

 

  截取部分查询结果

57799099999 | 2021-12-17 |  27.1167 |   114.967 |        78 | JI AN, CH | 50.9 |               8 |   35.5 |               8 | 1030.3 |              8 |  21.4 |              8 |  12.3 |                8 |   3.6 |               8 |   7.8 |  21.2 |   59.7 |                |   40.5 | *              |     0 | G               | 999.9 | 000000 |
| 57799099999 | 2021-12-18 |  27.1167 |   114.967 |        78 | JI AN, CH | 45.9 |               8 |   30.8 |               8 | 1031.1 |              8 |  22.1 |              8 |  15.5 |                8 |   2.9 |               8 |     6 |  13.6 |   57.9 |                |   38.3 |                |     0 | G               | 999.9 | 000000 |
| 57799099999 | 2021-12-19 |  27.1167 |   114.967 |        78 | JI AN, CH | 47.5 |               8 |   33.8 |               8 | 1026.2 |              8 |  17.2 |              8 |  14.7 |                8 |   1.5 |               8 |   2.3 | 999.9 |   59.2 |                |   37.4 |                |     0 | G               | 999.9 | 000000 |
| 57799099999 | 2021-12-20 |  27.1167 |   114.967 |        78 | JI AN, CH | 48.8 |               8 |   43.7 |               8 | 1022.5 |              8 |  13.6 |              8 |   9.2 |                8 |   1.4 |               8 |   2.7 | 999.9 |   59.2 |                |   38.8 |                |  0.31 | G               | 999.9 | 010000 |
| 57799099999 | 2021-12-21 |  27.1167 |   114.967 |        78 | JI AN, CH | 50.7 |               8 |   49.5 |               7 | 1020.7 |              8 |  11.8 |              8 |   6.4 |                8 |   1.3 |               8 |   2.3 | 999.9 |   56.5 |                |   42.8 |                |  0.02 | G               | 999.9 | 100000 |
| 57799099999 | 2021-12-22 |  27.1167 |   114.967 |        78 | JI AN, CH | 51.2 |               8 |   48.3 |               8 | 1021.1 |              8 |  12.2 |              8 |   7.1 |                8 |   1.1 |               8 |   3.7 | 999.9 |   59.7 |                |   45.7 |                |     0 | G               | 999.9 | 000000 |
+-------------+------------+----------+-----------+-----------+-----------+------+-----------------+--------+-----------------+--------+----------------+-------+----------------+-------+------------------+-------+-----------------+-------+-------+--------+----------------+--------+----------------+-------+-----------------+-------+--------+
20883 rows in set (0.20 sec)

  有2万多条数据

  通过时间排序可以知道吉安气象站最早记录的数据是1956年的

mysql> select * from data where station='57799099999' order by date limit 1;
+-------------+------------+----------+-----------+-----------+-----------+------+-----------------+------+-----------------+--------+----------------+-------+----------------+-------+------------------+------+-----------------+-------+-------+------+----------------+------+----------------+------+-----------------+-------+--------+
| station     | date       | latitude | longitude | elevation | name      | temp | temp_attributes | dewp | dewp_attributes | slp    | slp_attributes | stp   | stp_attributes | visib | visib_attributes | wdsp | wdsp_attributes | mxspd | gust  | max  | max_attributes | min  | min_attributes | prcp | prcp_attributes | sndp  | frshtt |
+-------------+------------+----------+-----------+-----------+-----------+------+-----------------+------+-----------------+--------+----------------+-------+----------------+-------+------------------+------+-----------------+-------+-------+------+----------------+------+----------------+------+-----------------+-------+--------+
| 57799099999 | 1956-08-20 |  27.1167 |   114.967 |        78 | JI AN, CH | 84.4 |               7 | 70.4 |               7 | 1007.2 |              7 | 999.9 |              0 |  12.1 |                7 |  6.9 |               7 |    14 | 999.9 |   97 |                |   66 | *              | 0.17 | C               | 999.9 | 010000 |
+-------------+------------+----------+-----------+-----------+-----------+------+-----------------+------+-----------------+--------+----------------+-------+----------------+-------+------------------+------+-----------------+-------+-------+------+----------------+------+----------------+------+-----------------+-------+--------+
1 row in set (0.00 sec)

  如果我们想要查找一个气象站的气象信息,我们要不知道气象站的站点编号,要不就需要知道站点的其他信息,例如经纬度信息,很明显这不符合我们的搜索习惯

  下面我们通过百度地图api通过数据的经纬度信息逆向查询出站点的地址信息

  首先去百度地图api申请一个ak用于通过经纬度逆地址查询出国家,省份,城市,县名信息 https://lbsyun.baidu.com/index.php

  代码如下 toolbox.py

# -*- coding: utf-8 -*-
# @Time        : 2021/12/29 15:58
# @Author      : Liuym
# @Email       : 274670459@qq.com
# @File        : toolbox.py
# @Project     : noaa
# @Description : 辅助函数库
import os
import requests
class BaiduMap:
    """
    处理与百度地图API相关的所有操作
    """
    def __init__(self, ak: str):
        """
        初始化类
        :param ak: 从百度地图API控制台处获取到的AK
        """
        self.ak = ak
        self.reverse_geocoding_url = 'http://api.map.baidu.com/reverse_geocoding/v3/'
        self.s = requests.Session()

    def get_location(self, lat: float, lng: float) -> dict:
        """
        根据提供的经纬度坐标获取地理位置信息
        :param lat: 纬度
        :param lng: 经度
        :return:
        """
        params = {
            'ak':        self.ak,
            'output':    'json',
            'coordtype': 'wgs84ll',
            'location':  f'{lat},{lng}',
        }
        resp = self.s.get(url=self.reverse_geocoding_url, params=params).json()
        address = resp['result']['addressComponent']

        # 仅返回其中的省市县信息
        return {
           item: address[item] for item in ['country', 'province', 'city', 'district']
        }

  使用以下方法调用

baidumap = BaiduMap(r'You Baidu AK')
print(baidumap.get_location(27.1166666,114.9666666))

  初始化需要传递百度申请的AK信息

 

   输出如下

 

   传递两个参数,经纬度就可以获取到对应的国家,省份,城市,县或区级信息

  insert_station_info.py

from toolbox import BaiduMap
from config import * 
baidu_api = BaiduMap(BD_AK)
import pymysql


def insert_station_info():
    db = pymysql.connect(host=HOST, user=USER, passwd=PASSWORD, db=DB, charset=CHARSET)
    cursor = db.cursor()
    # 查找数据库,如果station重复则保留一个结果
    station_number = cursor.execute('select * from data group by station having count(station)>1')
    # 所有查询结果
    station_result = cursor.fetchall()
    # 遍历所有结果,然后把数据插入info表,其中字段station_id name latitude longitude从原始表data取值不修改
    # 字段country province city district分别代表国家,省份,城市,县或区级信息是通过传递经纬度调用百度api取到的
    for i in station_result:  
        #print(baidu_api.get_location(i[2],i[3]))
        sql = 'insert into info values(%s, %s, %s, %s, %s, %s, %s, %s)'
        values = [i[0],i[5],i[2],i[3],baidu_api.get_location(i[2],i[3])['country'],baidu_api.get_location(i[2],i[3])['province'],baidu_api.get_location(i[2],i[3])['city'],baidu_api.get_location(i[2],i[3])['district']]
        print(values)
        cursor.execute(sql,values)
        db.commit()

  解析:首先我们从表data取获取气象站点的编号,因为有很多条,我们去点重复的气象站点编号,只取一个即可

  下面搜索语句搜索出去重复的站点信息

 select * from data group by station having count(station)>1;

  搜索结果如下

| 59995099999 | 1988-11-02 |  9.53333 |   112.883 |         6 | YONGSHUJIAO, CH                           |  82.5 |               4 | 9999.9 |               0 | 1009.7 |              4 | 999.9 |              0 |  12.6 |                4 |    17 |               4 |  19.4 | 999.9 |   83.7 | *              |   76.6 |                |  0.51 | G               | 999.9 | 000000 |
| 59997099999 | 1973-01-06 |  10.3833 |   114.367 |         5 | NANSHA DAO, CH                            |  85.1 |               4 |   81.5 |               4 | 1012.2 |              4 | 999.9 |              0 |  10.1 |                4 |  19.9 |               4 |  25.3 | 999.9 |   89.6 | *              |   80.6 | *              |     0 | I               | 999.9 | 000000 |
+-------------+------------+----------+-----------+-----------+-------------------------------------------+-------+-----------------+--------+-----------------+--------+----------------+-------+----------------+-------+------------------+-------+-----------------+-------+-------+--------+----------------+--------+----------------+-------+-----------------+-------+--------+
1017 rows in set (11.06 sec)

  总计找打1017个气象站点信息

  遍历这些站点信息,把不需要处理字段的数据station_id=station name=name latitude=latitude longitude=longitude直接插入info表即可

  需要处理的字段则是调用百度api获取的country,province,city,district信息,运行完毕以后就把气象站点的中文信息插入到info表了

  查询几条数据看一下

mysql> select * from info limit 5;
+-------------+-----------------+----------+-----------+---------+--------------------+--------------------+-----------------+
| station_id  | name            | latitude | longitude | country | province           | city               | district        |
+-------------+-----------------+----------+-----------+---------+--------------------+--------------------+-----------------+
| 50136099999 | MOHE, CH        |  52.9667 |   122.533 | 中国    | 黑龙江省           | 大兴安岭地区       | 漠河市          |
| 50246099999 | TA HE, CH       |   52.333 |     124.8 | 中国    | 黑龙江省           | 大兴安岭地区       | 塔河县          |
| 50349099999 | JIU HAI LAI, CH |   51.133 |    124.05 | 中国    | 黑龙江省           | 大兴安岭地区       | 松岭区          |
| 50353099999 | HUMA, CH        |  51.7333 |   126.633 | 中国    | 黑龙江省           | 大兴安岭地区       | 呼玛县          |
| 50425099999 | SAN HE, CH      |     50.5 |   120.067 | 中国    | 内蒙古自治区       | 呼伦贝尔市         | 额尔古纳市      |
+-------------+-----------------+----------+-----------+---------+--------------------+--------------------+-----------------+
5 rows in set (0.00 sec)

  对应的站点有了地址信息了

  注意:部分气象站的原始数据没有经纬度信息,默认的经纬度信息为0 0通过百度api是查询不到中文信息的,数据库表只存储了站点编号,英文名等信息

mysql> select * from info where latitude='';
+-------------+------------------------+----------+-----------+---------+----------+------+----------+
| station_id  | name                   | latitude | longitude | country | province | city | district |
+-------------+------------------------+----------+-----------+---------+----------+------+----------+
| 50845099999 | NAME LOCATION UNKN, CH |        0 |         0 |         |          |      |          |
| 50945099999 | TA AN, CH              |        0 |         0 |         |          |      |          |
| 52303099999 | YA MAN SU, CH          |        0 |         0 |         |          |      |          |
| 52367099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52377099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52543099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52671099999 | SALT LAKE, CH          |        0 |         0 |         |          |      |          |
| 52723099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52736099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52738099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52766099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52782099999 | NAME LOCATION UNKN, CH |        0 |         0 |         |          |      |          |
| 52826099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52846099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 52854099999 | JIANG XI GOU, CH       |        0 |         0 |         |          |      |          |
| 52864099999 | HUANG YUAN, CH         |        0 |         0 |         |          |      |          |
| 52974099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53073099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53376099999 | BA DAO GOU, CH         |        0 |         0 |         |          |      |          |
| 53477099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53488099999 | CHU LE P U, CH         |        0 |         0 |         |          |      |          |
| 53563099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53587099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53624099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53763099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53788099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53823099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53945099999 | HUANG LONG, CH         |        0 |         0 |         |          |      |          |
| 53948099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 53985099999 | SHEN CHUANG, CH        |        0 |         0 |         |          |      |          |
| 54016099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54017099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54276099999 | JING YU, CH            |        0 |         0 |         |          |      |          |
| 54336099999 | TAI AN SOUTHEAST, CH   |        0 |         0 |         |          |      |          |
| 54378099999 | CHING ZHI, CH          |        0 |         0 |         |          |      |          |
| 54474099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54529099999 | CHAI SHANG, CH         |        0 |         0 |         |          |      |          |
| 54543099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54614099999 | HE JIAN, CH            |        0 |         0 |         |          |      |          |
| 54625099999 | QI KOU, CH             |        0 |         0 |         |          |      |          |
| 54626099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54635099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54680099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54723099999 | ZHAN CHENG, CH         |        0 |         0 |         |          |      |          |
| 54726099999 | BIN XIAN, CH           |        0 |         0 |         |          |      |          |
| 54743099999 | NAME UNKNOWN, CH       |        0 |         0 |         |          |      |          |
| 54790099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54853099999 | NANWU, CH              |        0 |         0 |         |          |      |          |
| 54923099999 | MENG YIN, CH           |        0 |         0 |         |          |      |          |
| 54946099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 54948099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 55821099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56019099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56023099999 | NAME LOCATION UNKN, CH |        0 |         0 |         |          |      |          |
| 56026099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56036099999 | CANG DUO, CH           |        0 |         0 |         |          |      |          |
| 56042099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56043099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56057099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56069099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56070099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56089099999 | BAI GU SI, CH          |        0 |         0 |         |          |      |          |
| 56090099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56147099999 | CHUN LUO SI, CH        |        0 |         0 |         |          |      |          |
| 56156099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56192099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56237099999 | BANG DA, CH            |        0 |         0 |         |          |      |          |
| 56375099999 | HAN YUAN JIE, CH       |        0 |         0 |         |          |      |          |
| 56384099999 | E BIAN, CH             |        0 |         0 |         |          |      |          |
| 56472099999 | GAN LUO, CH            |        0 |         0 |         |          |      |          |
| 56474099999 | MIAN NING, CH          |        0 |         0 |         |          |      |          |
| 56561099999 | DA CUN, CH             |        0 |         0 |         |          |      |          |
| 56562099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 56581099999 | TIAN DI BA, CH         |        0 |         0 |         |          |      |          |
| 56674099999 | OGUS CHINESE, CH       |        0 |         0 |         |          |      |          |
| 56916099999 | NAME LOCATION UNKN, CH |        0 |         0 |         |          |      |          |
| 56979099999 | JIE JIE, CH            |        0 |         0 |         |          |      |          |
| 56982099999 | KAI YUAN, CH           |        0 |         0 |         |          |      |          |
| 56987099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 57063099999 | MIAN CHI, CH           |        0 |         0 |         |          |      |          |
| 57068099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 57093099999 | NAO KAO, CH            |        0 |         0 |         |          |      |          |
| 57128099999 | YANG XIAN, CH          |        0 |         0 |         |          |      |          |
| 57137099999 | SHI QUAN, CH           |        0 |         0 |         |          |      |          |
| 57347099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 57406099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 57428099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 57477099999 | SHA SHI, CH            |        0 |         0 |         |          |      |          |
| 57525099999 | XIANG GOU, CH          |        0 |         0 |         |          |      |          |
| 57597099999 | MA AO, CH              |        0 |         0 |         |          |      |          |
| 57623099999 | CHEN NAN, CH           |        0 |         0 |         |          |      |          |
| 57632099999 | CHANG PU QI, CH        |        0 |         0 |         |          |      |          |
| 57645099999 | HUA YUAN, CH           |        0 |         0 |         |          |      |          |
| 57666099999 | MA JI TANG, CH         |        0 |         0 |         |          |      |          |
| 57672099999 | YUAN JIANG, CH         |        0 |         0 |         |          |      |          |
| 57706099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 57732099999 | DE WANG, CH            |        0 |         0 |         |          |      |          |
| 57813099999 | YANG CHANG, CH         |        0 |         0 |         |          |      |          |
| 57958099999 | NAME UNKNOWN, CH       |        0 |         0 |         |          |      |          |
| 57966099999 | NING YUAN, CH          |        0 |         0 |         |          |      |          |
| 57973099999 | GUI YANG, CH           |        0 |         0 |         |          |      |          |
| 57983099999 | NAME UNKNOWN, CH       |        0 |         0 |         |          |      |          |
| 58083099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 58202099999 | TSAOCHUANG, CH         |        0 |         0 |         |          |      |          |
| 58211099999 | YING SHANG, CH         |        0 |         0 |         |          |      |          |
| 58231099999 | JIA SHAN, CH           |        0 |         0 |         |          |      |          |
| 58252099999 | AN FENG, CH            |        0 |         0 |         |          |      |          |
| 58266099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 58324099999 | SHAN HE, CH            |        0 |         0 |         |          |      |          |
| 58327099999 | KONG CHENG, CH         |        0 |         0 |         |          |      |          |
| 58368099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 58438099999 | JING DE, CH            |        0 |         0 |         |          |      |          |
| 58447099999 | HE QIAO, CH            |        0 |         0 |         |          |      |          |
| 58456099999 | HUANG WAN, CH          |        0 |         0 |         |          |      |          |
| 58458099999 | SHAO XING, CH          |        0 |         0 |         |          |      |          |
| 58462099999 | MIN HANG, CH           |        0 |         0 |         |          |      |          |
| 58467099999 | YU YAO, CH             |        0 |         0 |         |          |      |          |
| 58478099999 | ZHI ZHI ISLAND, CH     |        0 |         0 |         |          |      |          |
| 58517099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 58654099999 | JIN YUN, CH            |        0 |         0 |         |          |      |          |
| 58713099999 | HUANG SHI DU, CH       |        0 |         0 |         |          |      |          |
| 58734099999 | JIAN YANG, CH          |        0 |         0 |         |          |      |          |
| 58736099999 | XIONG MOUNTAIN, CH     |        0 |         0 |         |          |      |          |
| 58747099999 | LI MEN, CH             |        0 |         0 |         |          |      |          |
| 58749099999 | FU AN, CH              |        0 |         0 |         |          |      |          |
| 58753099999 | RUI AN, CH             |        0 |         0 |         |          |      |          |
| 58854099999 | XI YANG ISLAND, CH     |        0 |         0 |         |          |      |          |
| 58864099999 | TUNG YIN ISLAND, CH    |        0 |         0 |         |          |      |          |
| 58932099999 | NAME UNKNOWN ONC, CH   |        0 |         0 |         |          |      |          |
| 58934099999 | YONG CHUN, CH          |        0 |         0 |         |          |      |          |
| 58945099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 58984099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 59008099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 59013099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 59062099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 59076099999 | NAME UNKNOWN ONC, CH   |        0 |         0 |         |          |      |          |
| 59077099999 | XIA SHUI XU, CH        |        0 |         0 |         |          |      |          |
| 59092099999 | CHENG LONG, CH         |        0 |         0 |         |          |      |          |
| 59097099999 | XINFENG, CH            |        0 |         0 |         |          |      |          |
| 59217099999 | RONG LAO XIANG, CH     |        0 |         0 |         |          |      |          |
| 59234099999 | QIAO LI, CH            |        0 |         0 |         |          |      |          |
| 59277099999 | LU BU, CH              |        0 |         0 |         |          |      |          |
| 59288099999 | GUANG ZHOU EAST, CH    |        0 |         0 |         |          |      |          |
| 59326099999 | XIONG DI ISLAND, CH    |        0 |         0 |         |          |      |          |
| 59432099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 59622099999 | DONG JIAO, CH          |        0 |         0 |         |          |      |          |
| 59633099999 | BAI MU, CH             |        0 |         0 |         |          |      |          |
| 59683099999 | BEI JIAN ISLAND, CH    |        0 |         0 |         |          |      |          |
| 59745099999 | XI CUN, CH             |        0 |         0 |         |          |      |          |
| 59748099999 | LIN GAO CAPE, CH       |        0 |         0 |         |          |      |          |
| 59752099999 | BOGUS CHINESE, CH      |        0 |         0 |         |          |      |          |
| 59755099999 | CHIN HO CHIN NANG, CH  |        0 |         0 |         |          |      |          |
| 59848099999 | BAI SHA, CH            |        0 |         0 |         |          |      |          |
| 59938099999 | HUANG LIU, CH          |        0 |         0 |         |          |      |          |
| 59945099999 | BAO TING, CH           |        0 |         0 |         |          |      |          |
| 59958099999 |                        |        0 |         0 |         |          |      |          |
+-------------+------------------------+----------+-----------+---------+----------+------+----------+
156 rows in set (0.01 sec)

  七,通过中文查询气象信息

  通过info表的气象站点的中文信息,我们就可以愉快地连表查询了

  连表查询参考:https://www.cnblogs.com/minseo/p/14333399.html

  查询吉安市2021年的所有气象信息

 select * from data inner join info on data.station=info.station_id where info.city='吉安市' and data.date>='2021-01-01';

  连表查询语句解析

  连表查询data和info表,条件是data.station等于info.station_id即站点的编号相同

  where继续定义查询条件查询的城市是吉安市,时间大于2021-01-01

  注意:吉安地区在noaa有记录的有4个气象站,只不过大部分记录在气象站编号为57799099999的气象站

  去重复查询吉安市的几个气象站信息

mysql> select info.* from data inner join info on data.station=info.station_id where info.city='吉安市' group by station having count(station)>1\G
*************************** 1. row ***************************
station_id: 57799099999
      name: JI AN, CH
  latitude: 27.1167
 longitude: 114.967
   country: 中国
  province: 江西省
      city: 吉安市
  district: 吉州区
*************************** 2. row ***************************
station_id: 57891099999
      name: HECHUAN, CH
  latitude: 26.95
 longitude: 114.233
   country: 中国
  province: 江西省
      city: 吉安市
  district: 永新县
*************************** 3. row ***************************
station_id: 57896099999
      name: HUANGDIAN, CH
  latitude: 26.25
 longitude: 114.333
   country: 中国
  province: 江西省
      city: 吉安市
  district: 遂川县
*************************** 4. row ***************************
station_id: 57896199999
      name: SUI CHUAN, CH
  latitude: 26.333
 longitude: 114.5
   country: 中国
  province: 江西省
      city: 吉安市
  district: 遂川县
4 rows in set (5.05 sec)

  根据气象站点id可以查询到这几个气象站点的最后一条气象信息是时间

mysql> select info.district,data.date from data inner join info on data.station=info.station_id where data.station='57891099999' order by data.date desc limit 1;
+-----------+------------+
| district  | date       |
+-----------+------------+
| 永新县    | 1963-01-29 |
+-----------+------------+
1 row in set (0.00 sec)

mysql> select info.district,data.date from data inner join info on data.station=info.station_id where data.station='57896099999' order by data.date desc limit 1;
+-----------+------------+
| district  | date       |
+-----------+------------+
| 遂川县    | 1961-03-14 |
+-----------+------------+
1 row in set (0.00 sec)

mysql> select info.district,data.date from data inner join info on data.station=info.station_id where data.station='57896199999' order by data.date desc limit 1;
+-----------+------------+
| district  | date       |
+-----------+------------+
| 遂川县    | 1946-02-28 |
+-----------+------------+
1 row in set (0.00 sec)


mysql> select info.district,data.date from data inner join info on data.station=info.station_id where data.station='57799099999' order by data.date desc limit 1;
+-----------+------------+
| district  | date       |
+-----------+------------+
| 吉州区    | 2021-12-22 |
+-----------+------------+
1 row in set (0.00 sec)

  即除了气象站点57799099999其他几个站点早就不更新气象信息了

  查询吉安地区有史以来的最高气温和日期前10

select data.date,data.max from data inner join info on data.station=info.station_id where info.city='吉安市' and data.max<200 order by data.max desc limit 10\G

  查询语句解析

select data.date,data.max  # 只显示日期和最高气温的温度
from data inner join info # 连表data和info
on data.station=info.station_id # 条件是站点编号相同
where info.city='吉安市' and data.max<200 # 条件是吉安市和最大气温小于200华氏度
order by data.max desc # 使用字段max倒序排序
limit 10\G # 取10个

  查询结果,吉安地区在1973-07-23温度最高达到华氏温度111.2换算成摄氏度(华氏温度-32)÷1.8 相当于44℃高温

 

   注意:最高气温字段如果没有记录则值取值为9999.9这里使用条件data.max<>9999.9查询不生效还是把9999.9查询出来了,所以使用了一个不可能出现的高温作为条件data.max<200

  同理查询最低温度

mysql> select data.date,data.min from data inner join info on data.station=info.station_id where info.city='吉安市'  order by data.min  limit 10\G

  查询结果如下,最低温度出现在1960-01-26换算成摄氏度相当于-7℃

 

   有个数据库就可以根据需要查询想要的数据了,如果能做一个html页面通过页面输入就更加好了!

  八,整合成一个类

  把以上代码以类的形式合并到一起noaa.py

# -*- coding: utf-8 -*-
# @Time        : 2021/12/29 15:58
# @Author      : Liuym
# @Email       : 274670459@qq.com
# @File        : noaa.py
# @Project     : noaa
# @Description : 主程序

import requests
import os
from lxml import etree
import tarfile
import csv
import pymysql
from config import *
from toolbox import BaiduMap
db = pymysql.connect(host=HOST, user=USER, passwd=PASSWORD, db=DB, charset=CHARSET)
cursor = db.cursor()

class Noaa(object):
    def __init__(self):
        self.baidu_api = BaiduMap(BD_AK)
    
    # 下载压缩文件方法
    def _download_tar_gz(self,url):
        # tar_gz_url = 'https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/'
        tar_gz_url = url
        headers = {'User-Agent': 'M'}
        resp= requests.get(tar_gz_url,headers=headers)
        tar_gz_resp_text = resp.text
        tar_gz_etree_element = etree.HTML(tar_gz_resp_text)
        tar_gz_url_xpath = tar_gz_etree_element.xpath('//*/table/tr/td/a')
        # print(tar_gz_url_xpath)
        # print(tar_gz_url[1].xpath('@href'))
        for tar_gz in tar_gz_url_xpath[1:]:
            # 打印要下载的文件名
            # print(tar_gz.xpath('@href')[0])
            requests_url = tar_gz_url + tar_gz.xpath('@href')[0]
            file_name = tar_gz.xpath('@href')[0]
            # 打印拼接以后的下载链接
            # print(requests_url)
            r = requests.get(requests_url)
            # 如果当前文件夹没有tar_gz目录则创建该目录
            if 'tar_gz' not in [x for x in os.listdir('.') if os.path.isdir(x)]:
                try:
                    os.mkdir('tar_gz')
                except:
                    print('创建文件夹失败')
            # 如果在目录tar_gz下已经有文件了则不重复下载,否则下载
            if file_name in [x for x in os.listdir('tar_gz')]:
                print('%s文件已下载'%(file_name))
            else:
            # 通过拼接的下载url下载文件
                with open(f'tar_gz/{file_name}', "wb") as code:
                    code.write(r.content)
                    print('下载文件%s成功'%(file_name))

    # 接压缩方法,传递参数为文件名以及解压的文件夹
    def untar(self,fname, dirs):
        t = tarfile.open(fname)
        t.extractall(path = dirs)
        

    # 解压下载的气象信息压缩文件,例如1929.tar.gz解压至文件夹tar_gz/1929下
    def _decomp_tar_gz(self):
        for tar_gz_file in os.listdir('tar_gz'):
            # print(tar_gz_file)
            tar_gz_dir = tar_gz_file.split('.')[0]
            if os.path.isfile(f'tar_gz/{tar_gz_file}'):
                self.untar(f'tar_gz/{tar_gz_file}',f'tar_gz/{tar_gz_dir}')
                print('文件%s解压成功'%(f'tar_gz/{tar_gz_file}'))

    # 插入数据库方法,传递csv文件路径然后解析csv文件往MySQL数据库插入数据
    def insert_data_from_csv(self,csv_file):
        f = csv.reader(open(csv_file,'r'))
        for i in f:
            if i[0] == "STATION":
                continue
            sql = 'insert into data values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
        
            try:
                cursor.execute(sql,list(i[n] for n in range(28)))
                db.commit()
            except:
                print('插入数据错误')

    # 从解压后的文件夹查找csv文件然后调用插入数据库方法插入数据
    def insert_data(self):
        # 遍历文件夹
        for tar_gz_dir in os.listdir('tar_gz'):
            # 如果是文件夹则为解压缩以后的文件夹,然后再次遍历文件夹,排除压缩文件tar.gz
            if os.path.isdir(f'tar_gz/{tar_gz_dir}'):
                for tar_gz_file in os.listdir(f'tar_gz/{tar_gz_dir}'):
                    # 如果csv文件是5开头的则代表是中国的气象站则插入,否则忽略
                    if f'tar_gz/{tar_gz_dir}/{tar_gz_file}'.split('/')[2][0] == '5':
                        print('是中国的气象站网数据库插入数据%s'%(f'tar_gz/{tar_gz_dir}/{tar_gz_file}'))
                        self.insert_data_from_csv(f'tar_gz/{tar_gz_dir}/{tar_gz_file}')
                    else:
                        print('外国气象站数据不处理')

    # 插入气象站站点信息,首先从data表取出站点的id,去掉重复的station数据只保留1条
    # 然后循环取结果的第3,4条数据经纬度代入百度api取获取国家,省份,城市,县城信息,然后插入info表
    def insert_station_info(self):
        station_number = cursor.execute('select * from data group by station having count(station)>1')
        station_result = cursor.fetchall()
        for i in station_result:  
            #print(baidu_api.get_location(i[2],i[3]))
            sql = 'insert into info values(%s, %s, %s, %s, %s, %s, %s, %s)'
            values = [i[0],i[5],i[2],i[3],self.baidu_api.get_location(i[2],i[3])['country'],self.baidu_api.get_location(i[2],i[3])['province'],self.baidu_api.get_location(i[2],i[3])['city'],self.baidu_api.get_location(i[2],i[3])['district']]
            try:
                cursor.execute(sql,values)
                db.commit()
            except pymysql.err.IntegrityError:
                print('主键重复,该数据已存在')
            except:
                print('插入数据错误')
# 初始化
app = Noaa()
# 在当前文件夹下创建文件夹tar_gz下载气象信息压缩文件
app._download_tar_gz('https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/')
# 把下载的压缩文件解压成csv文件
app._decomp_tar_gz()
# 通过解压的csv文件把数据插入MySQL数据库,只插入气象站开头为5的中国气象站
app.insert_data()
# 通过百度api插入站点中文信息
app.insert_station_info()

  首先初始化,然后根据url下载压缩文件,解压,插入数据库,插入站点中文信息

  注意:第一次运行时间很长,因为数据量太多,下载解压,插入数据时间均比较长,第一次运行成功以后,往后就可以只增加新增的数据了

  

 

posted @ 2021-12-29 17:53  minseo  阅读(3573)  评论(4编辑  收藏  举报