用python爬取历史天气数据的方法示例


Posted in Python onDecember 30, 2019

某天气网站(www.数字.com)存有2011年至今的天气数据,有天看到一本爬虫教材提到了爬取这些数据的方法,学习之,并加以改进。

用python爬取历史天气数据的方法示例

准备爬的历史天气

爬之前先分析url。左上有年份、月份的下拉选择框,按F12,进去看看能否找到真正的url:

用python爬取历史天气数据的方法示例

很容易就找到了,左边是储存月度数据的js文件,右边是文件源代码,貌似json格式。

双击左边js文件,地址栏内出现了url:http://tianqi.数字.com/t/wea_history/js/54511_20161.js

url中的“54511”是城市代码,“20161”是年份和月份代码。下一步就是找到城市代码列表,按城市+年份+月份构造url列表,就能开始遍历爬取了。

城市代码也很诚实,很快就找到了:

用python爬取历史天气数据的方法示例

下一步得把城市名称和代码提取出来,构造一个“城市名称:城市代码”的字典,或者由元组(城市名称,城市代码)组成的列表,供爬取时遍历。考虑到正则提取时,构造元组更便捷,就不做成字典了。

def getCity():
  html = reqs.get('https://tianqi.2345.com/js/citySelectData.js').content
  text = html.decode('gbk')
  city = re.findall('([1-5]\d{4})\-[A-Z]\s(.*?)\-\d{5}',text)  #只提取了地级市及以上城市的名称和代码,5以上的是县级市  
  city = list(set(city))                    #去掉重复城市数据
  print('城市列表获取成功')
  return city

接下来是构造url列表,感谢教材主编的提醒,这里避免了一个大坑。原来2017年之前的url结构和后面的不一样,在这里照搬了主编的构造方法:

def getUrls(cityCode):
  urls = []
  for year in range(2011,2020):
    if year <= 2016:
      for month in range(1, 13):
        urls.append('https://tianqi.数字.com/t/wea_history/js/%s_%s%s.js' % (cityCode,year, month))
    else:
      for month in range(1,13):
        if month<10:
          urls.append('https://tianqi.数字.com/t/wea_history/js/%s0%s/%s_%s0%s.js' %(year,month,cityCode,year,month))          
        else:
          urls.append('https://tianqi.数字.com/t/wea_history/js/%s%s/%s_%s%s.js' %(year,month,cityCode,year,month))
  return urls

接下来定义一个爬取页面的函数getHtml(),这个是常规操作,用requests模块就行了:

def getHtml(url):
  header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
       'Referer': '******'}
  request = reqs.get(url,headers = header)
  text = request.content.decode('gbk') #经试解析,这里得用gbk模式
  time.sleep(random.randint(1,3))    #随机暂停,减轻服务器压力
  return text

然后就是重点部分了,数据解析与提取。

试了试json解析,发现效果不好,因为页面文本里面含杂质。

还是用正则表达式吧,能够提取有效数据,尽可能少浪费机器时间。

2016年开始的数据和之前年份不一样,多了PM2.5污染物情况,因此构造正则表达式时,还不能用偷懒模式。

str1 = "{ymd:'(.*?)',bWendu:'(.*?)℃',yWendu:'(.*?)℃',tianqi:'(.*?)',fengxiang:'(.*?)',fengli:'(.*?)',aqi:'(.*?)',aqiInfo:'(.*?)',aqiLevel:'(.*?)'.*?}"
str2 = "{ymd:'(.*?)',bWendu:'(.*?)℃',yWendu:'(.*?)℃',tianqi:'(.*?)',fengxiang:'(.*?)',fengli:'(.*?)'.*?}"
 
#这个就是偷懒模式,取出来的内容直接存入元组中

如果严格以2016年为界,用一下偷懒模式还行,但本人在这里遇坑了,原来个别城市的污染物信息是时有时无的,搞不清在某年某月的某天就出现了,因此还得构造一个通用版的,把数据都提出来,再把无用的字符去掉。

def getDf(url):  
  html = getHtml(url)
  pa = re.compile(r'{(ymd.+?)}')           #用'{ymd'打头,把不是每日天气的其它数据忽略掉
  text = re.findall(pa,html)
  list0 = []
  for item in text:
    s = item.split(',')              #分割成每日数据
    d = [i.split(':') for i in s]         #提取冒号前后的数据名称和数据值
    t = {k:v.strip("'").strip('℃') for k,v in d} #用数据名称和数据值构造字典   
    list0.append(t)
  df = pd.DataFrame(list0)              #加入pandas列表中,便于保存
  return df

数据的保存,这里选择了sqlite3轻便型数据库,可以保存成db文件:

def work(city,url):
  con =sql.connect('d:\\天气.db') 
  try:
    df = getDf(url)
    df.insert(0,'城市名称',city)                #新增一列城市名称
    df.to_sql('total', con, if_exists='append', index=False) 
    print(url,'下载完成')    
  except Exception as e:
    print("出现错误:\n",e)
  finally:
    con.commit()
    con.close()

在这里还有一个小坑,第一次连接数据库文件时,如果文件不存在,会自动添加,后续在写入数据时,如果数据中新增了字段,写入时会报错。可以先把数据库文件字段都设置好,但这样太累,所以本人又搞了个偷懒的方式,即先传入一个2019年某月的单个url搞一下,自动添加好字段,后面再写入时就没问题了。本人觉得这个应该还有更佳的解决办法,目前还在挖掘中。

数据保存后的状态如下:

 用python爬取历史天气数据的方法示例

本来考虑过用多线程爬虫,想想又觉得既然人家没有设置反爬措施,咱们也不能太不厚道了,就单线程吧。

最终爬了334个城市,100多万条数据。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
详解Python3.1版本带来的核心变化
Apr 07 Python
Python中max函数用法实例分析
Jul 17 Python
Python 文件管理实例详解
Nov 10 Python
Python爬虫之模拟知乎登录的方法教程
May 25 Python
matplotlib实现热成像图colorbar和极坐标图的方法
Dec 13 Python
Appium+python自动化怎么查看程序所占端口号和IP
Jun 14 Python
python中的线程threading.Thread()使用详解
Dec 17 Python
tensorflow实现在函数中用tf.Print输出中间值
Jan 21 Python
Python 代码调试技巧示例代码
Aug 11 Python
python爬虫工具例举说明
Nov 30 Python
五分钟学会怎么用Pygame做一个简单的贪吃蛇
Jan 06 Python
七个Python必备的GUI库
Apr 27 Python
pytorch 自定义卷积核进行卷积操作方式
Dec 30 #Python
PyTorch中反卷积的用法详解
Dec 30 #Python
python使用正则表达式(Regular Expression)方法超详细
Dec 30 #Python
Pytorch实现各种2d卷积示例
Dec 30 #Python
Python面向对象之多态原理与用法案例分析
Dec 30 #Python
Pytoch之torchvision.transforms图像变换实例
Dec 30 #Python
python面向对象之类属性和类方法案例分析
Dec 30 #Python
You might like
自己做矿石收音机
2021/03/02 无线电
php实现的zip文件内容比较类
2014/09/24 PHP
PHP获取网页所有连接的方法(附demo源码下载)
2016/03/30 PHP
PHP7 新特性详细介绍
2016/09/06 PHP
php查看一个变量的占用内存的实例代码
2020/03/29 PHP
javascript读取RSS数据
2007/01/20 Javascript
jquery ajax请求实例深入解析
2012/11/26 Javascript
JavaScript实现x秒后自动跳转到一个页面
2013/01/03 Javascript
两种常用的javascript数组去重方法思路及代码
2013/03/26 Javascript
JavaScript中几种排序算法的简单实现
2015/07/29 Javascript
详解JavaScript的回调函数
2015/11/20 Javascript
基于BootStrap实现局部刷新分页实例代码
2016/08/08 Javascript
Vue的MVVM实现方法
2017/08/16 Javascript
使用DataTable插件实现异步加载数据
2017/11/19 Javascript
JS函数节流和函数防抖问题分析
2017/12/18 Javascript
webpack多入口文件页面打包配置详解
2018/01/09 Javascript
如何使用 vue + d3 画一棵树
2018/12/03 Javascript
Vue-cli3.x + axios 跨域方案踩坑指北
2019/07/04 Javascript
[01:28:31]《加油DOTA》真人秀 第五期
2014/09/01 DOTA
pycharm 使用心得(三)Hello world!
2014/06/05 Python
玩转python爬虫之爬取糗事百科段子
2016/02/17 Python
Python UnboundLocalError和NameError错误根源案例解析
2018/10/31 Python
python pands实现execl转csv 并修改csv指定列的方法
2018/12/12 Python
Django框架实现分页显示内容的方法详解
2019/05/10 Python
Python API 自动化实战详解(纯代码)
2019/06/11 Python
Eclipse面试题
2014/03/22 面试题
传统软件工程与面向对象的软件工程有什么区别
2012/05/31 面试题
物业公司的岗位任命书
2014/06/06 职场文书
协商一致解除劳动合同协议书
2014/09/14 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
2016简历自荐信优秀范文
2016/01/29 职场文书
十二月早安励志心语大全
2019/12/03 职场文书
Python 线程池模块之多线程操作代码
2021/05/20 Python
Python使用Kubernetes API访问集群
2021/05/30 Python
Nginx进程调度问题详解
2021/09/25 Servers
Spring中bean集合注入的方法详解
2022/07/07 Java/Android