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下載壓縮文件,解壓,插入數據庫,插入站點中文信息

  注意:第一次運行時間很長,因為數據量太多,下載解壓,插入數據時間均比較長,第一次運行成功以后,往后就可以只增加新增的數據了

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM