最近学习了如何在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]
结果:
查看并提取数据
点开链接,就能看到数据的具体样式了。因此先下载数据,并对其进行针对性处理。
天气数据的存储方式
首先看看数据到底是什么:
import requests
print(r1 = requests.get(todo_urls[0]))
print(r1.text)
结果:
得到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])
到结尾时,可以发现多下的字符串在这里:
只要提取一开始的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])
结果如下:
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()
结果如下:
转换日期列
pd.to_datetime
是pandas的一个函数,能将字符串、列表、series变成日期形式。其对单个日期字符串处理会得到Timestamp
;对日期字符串列表处理会得到DatetimeIndex
。
df.set_index(pd.to_datetime(df["ymd"]), inplace=True)
df.head()
这里的 inplace = True
含义是不创建新的对象,直接对原始对象进行修改。
对日期进行查询
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()
本文参考自这篇文章,感谢原作者的教程。
2024年9月23日 08:54
不错不错,我喜欢看 https://www.jiwenlaw.com/
2021年11月22日 15:10
请问为什么爬出来的数据是乱码呢?
2021年08月13日 08:42
害,看不懂了