使用python爬取南京历史天气数据(2345天气网)

首页 / 科技区 / 正文

最近学习了如何在2345天气网上爬取历史天气数据,并使用pandas库对数据进行清洗和导出。但是,本文方法对其它网站并不一定适用,主要是记录一种思考的方式。

南京的历史天气数据在这个网站上:http://tianqi.2345.com/wea_history/58238.htm 。其它城市的操作步骤类似,将后面的五位数代号修改一下即可。

获取待获取数据列表

待爬取数据在js里面,例如http://tianqi.2345.com/t/wea_history/js/202001/58238_202001.js。这是后续对数据进行处理的原型,得仔细观察结构,而后抽丝剥缕。

months = []
year = 2020
for i in range(12):
    months.append("%d%02d"%(year, i+1))
todo_urls = [f"http://tianqi.2345.com/t/wea_history/js/{month}/58238_{month}.js" for month in months]

结果:

image-20210201213054542

查看并提取数据

点开链接,就能看到数据的具体样式了。因此先下载数据,并对其进行针对性处理。

天气数据的存储方式

首先看看数据到底是什么:

import requests
print(r1 = requests.get(todo_urls[0]))
print(r1.text)

结果:

image-20210201213750221

得到js格式的json文件

可以看到,用大括号包起来的十分类似json格式。但开头和结尾的东西并不需要。结尾的部分参数也类似json格式,后续再处理,先去处理掉开头的字符串和末尾的

datas = []
for url in todo_urls:
    r = requests.get(url)
    if r.status_code!=200:
        raise Exception()
    # 去除前后的字符串,得到一个js格式的JSON
    data = r.text.lstrip("var weather_str=").rstrip(";")
    datas.append(data)

借助demjson库对数据解码

demjson是一个库,其decode函数用于将已编码的 json 字符串解码为 Python 对象。与json库相比,其优势在于可以解析不规则的json数据(如key未加引号,不是字符形式)

先以一月为例,看一下效果:

import demjson
demjson.decode(datas[0])

image-20210201215555881

到结尾时,可以发现多下的字符串在这里:

image-20210201215616160

只要提取一开始的tqInfo就够了:

tqInfos = demjson.decode(datas[0])["tqInfo"]

解析所有月份的数据

将所有月份按上述步骤解析:

all_datas = []

for data in datas:
    tqInfos = demjson.decode(data)["tqInfo"]
    all_datas.extend([x for x in tqInfos if len(x)>0])

至此,数据就全部处理好了。

导出至csv格式

导入csv库,将数据导出。

import csv
with open('./nanjing_2020.csv', 'w', newline='') as csv_file:
    writer = csv.writer(csv_file)
    columns = [i for i in all_datas[0].keys()]
    # ['ymd', 'bWendu', 'yWendu', 'tianqi', 'fengxiang', 'fengli', 'aqi', 'aqiInfo', 'aqiLevel']
    writer.writerow(columns)
    
    for data in all_datas:
        writer.writerow([data[column] for column in columns])

结果如下:

image-20210201220615695

pandas库简单运用

进行简单的查询、统计与制图。

去掉温度后缀℃

import pandas as pd
filepath = './nanjing_2020.csv'
df = pd.read_csv(filepath)
df.loc[:, "bWendu"] = df["bWendu"].str.split('℃').str[0].astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
df.head()

结果如下:

image-20210201223943716

转换日期列

pd.to_datetime是pandas的一个函数,能将字符串、列表、series变成日期形式。其对单个日期字符串处理会得到Timestamp;对日期字符串列表处理会得到DatetimeIndex

df.set_index(pd.to_datetime(df["ymd"]), inplace=True)
df.head()

这里的 inplace = True含义是不创建新的对象,直接对原始对象进行修改

image-20210201224625881

对日期进行查询

df.loc['2020-12-09'] # 对某一天
df.loc['2020-01-01':'2020-01-10'] # 对某一个区间
df.loc['2020-06'] # 按月份前缀
df.loc['2020'] # 按年份前缀

统计每周最高温并制图

制图需要导入matplotlib库,直接通过df.plot()即可快速绘制,结尾加上plt.show()。(实际上可以不加)

import matplotlib.pyplot as plt
df.groupby(df.index.week)["bWendu"].max().plot()
plt.show()

image-20210201230525654

本文参考自这篇文章,感谢原作者的教程。
评论区
头像
    头像
    zeprycwdly
    2024年9月23日 08:54
    回复

    不错不错,我喜欢看 https://www.jiwenlaw.com/

    头像
    zhouni
    2021年11月22日 15:10
    回复

    请问为什么爬出来的数据是乱码呢?

    头像
    windsky
    2021年08月13日 08:42
    回复

    害,看不懂了

文章目录