python爬虫----全国天气预报的获取

这次是通过全国天气预报的网站去抓取实时天气和预测未来24小时的天气,抓取的数据包括:地区的区号、地区名称、实时气温、湿度、风向、风力、预测的最高温和最低温、晚间风向、日间的风向和各自的风力大小,把这些数据存储到mysql的数据库当中,并利用pygal库来对数据进行可视化操作。

抓取的网页

通过分析可以看到一个数据的接口,可以获取全国的地区码,或者点击地图上的区域也能找到 http://forecast.weather.com.cn/napi/h5map/city/10113/jQuery1537792324377?callback=jQuery1537792324377,city后面的数字是要查询的地区码,只要知道地区码就可以获取到该地区的天气预报。

抓取网页的链接

http://www.weather.com.cn/static/html/weather.shtml

目标链接:

http://forecast.weather.com.cn/napi/h5map/city/101/jQuery1537791723170?callback=jQuery1537791723170

从该链接可以获取到所有地区的地区号和地区名称,参数jQuery1537791723170后面的数字1537791723170是当前的时间戳,可以通过本机的当前时间来替代,实现实时性。

http://forecast.weather.com.cn/napi/h5map/city/10113/jQuery1537792324377?callback=jQuery1537792324377

该链接是跟上面获取到的地区码去通过字符串的拼接得到新的url,再去抓取该地区的天气预报。

导包

import pymsql---数据库的操作

import requests---网页抓取

import re----正则表达式的使用

import json----数据格式的转换

import time----获取当前的时间

import pygal----数据可视化操作

网页抓取和解析

编写一个类来实现网页的抓取。

# coding:utf-8
import requests
import re
import json
import timeclass WeatherSpider():"""docstring for WeatherSpider"""def __init__(self):self.headers={'Accept-Encoding':'gzip, deflate','Connection':'keep-alive','Cookie': 'vjuids=-9fcc3e115.165947a9a2a.0.48bc4b39849d4; UM_distinctid=165947a9ae1324-0ae8457e8abdee-47e1039-100200-165947a9ae219e; f_city=%E5%B9%BF%E5%B7%9E%7C101280101%7C; returnUrl=%2Fweb%2Fdashboard%2Fmobile%2Findex.do; vjlast=1535794387.1536491754.11; Hm_lvt_080dabacb001ad3dc8b9b9049b36d43b=1536491927,1536491942,1536492078,1536492148; Hm_lpvt_080dabacb001ad3dc8b9b9049b36d43b=1536492148; Wa_lvt_1=1536491927,1536491942,1536492079,1536492148; Wa_lpvt_1=1536492148','Host':'forecast.weather.com.cn','Upgrade-Insecure-Requests':'1','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}self.url='http://forecast.weather.com.cn/napi/h5map/city/101/jQuery'self.city_url='http://forecast.weather.com.cn/napi/h5map/city/'def get_page(self,url):# 请求头的设置try:response=requests.get(url,headers=self.headers)# 获取内容,byte类型return response.contentexcept Exception as e:print(e)return Nonedef parase_page(self,url):# 返回bytes类型json_content=self.get_page(url)# python3的字符串默认为unicode格式(无编码)# 先进行解码再进行编码再解码# encode就是对字符串进行编码作为字节对象返回if json_content !=None:try:# decode就是对字节进行解码作为字符串对象返回data=json_content.decode('utf-8').encode('utf-8').decode('utf-8')# 正则表达式匹配detact=re.search('jQuery[0-9]{13}',data).group()except Exception as e:print("出错:"+e)print(json_content)passelse:# 去除jQuery1536491942006跟括号data=data.replace(detact,'')[1:-1]# 转换为字典格式results=json.loads(data)['result']return results# 获取系统当前的时间def get_current_time(self):# 得到当前时间的时间戳,float类型cur_time=time.time()return str(int(cur_time*1000))# 获取每一个省份、自治区、特别行政区、直辖市的areaiddef get_areaid(self,results):for area,value in results.items():yield{'area_name':area,'area_id':value['area']['areaid']}# print("区域名:"+area)# print(value['area']['areaid'])# 根据数据库中每一个省份、自治区、特别行政区、直辖市的areaid再去查询他们每个城市的气温def get_weather(self,city_url):city_weathers=self.parase_page(city_url)'''forecast24h是预测未来24小时的天气:maxtem为最高气温,mintep为最低气温,dayweather、nightweather均为天气代码,dayws为白天风力大小,daywd为白天风向,nightws为晚间风力大小,nightwd为晚间风向obs里面是实时天气:tem为气温,rh为相对湿度,ws为风力大小,wd为风向'''# print(city_weathers)if city_weathers != None:return city_weatherselse:return None# 解析未来24小时的城市天气和实时天气def parse_city_weather(self,city_weathers,column,area_name):'''city_weathers:字典格式的天气数据,column:传入的是未来24小时的天气还是实时天气area_name:是地区属于的省份或者直辖市或者其他的'''if 'forecast24h' in column:if city_weathers !=None:for key,forecast_data in city_weathers.items():if forecast_data !=None:yield{'city_name':key,'city_id':forecast_data.get('area').get('areaid'),'maxtem':forecast_data.get('forecast24h').get('maxtem'),'mintem':forecast_data.get('forecast24h').get('mintem'),'dayws':forecast_data.get('forecast24h').get('dayws'),'daywd':forecast_data.get('forecast24h').get('daywd'),'nightws':forecast_data.get('forecast24h').get('nightws'),'nightwd':forecast_data.get('forecast24h').get('nightwd'),'factime':forecast_data.get('forecast24h').get('fctime'),'area_name':area_name}elif 'obs' in column:if city_weathers !=None:for key,forecast_data in city_weathers.items():if forecast_data !=None:yield{'city_name':key,'city_id':forecast_data.get('area').get('areaid'),'tem':forecast_data.get('obs').get('tem'),'rh':forecast_data.get('obs').get('rh'),'ws':forecast_data.get('obs').get('ws'),'wd':forecast_data.get('obs').get('wd'),'factime':forecast_data.get('obs').get('fctime'),'area_name':area_name}

问题一:

其中在使用接口去获取信息,返回的数据全部是乱码。由于编写的get_page(self,url)方法的返回值是byte类型的,所以先进行解码、编码、再进行解码,去解决乱码问题。

问题二:

在进行数据解析的时候,因为香港、台湾和澳门之前不存在一些字段,所以无法获取到信息,而选择在解析的时候去除它们,它们的地区码分别为:'101320101' and 101330101' and '101340101'。

数据存储

数据库的操作全部封装到一个类里面去。

地区表的结构:id varchar(10),areaId varchar(10),areaName varchar(20)

不同地区的实时天气的表结构:id VARCHAR(20),cityName VARCHAR(20), tem VARCHAR(10), rh VARCHAR(10),wd VARCHAR(10),ws VARCHAR(10),factime VARCHAR(20),areaName VARCHAR(20)。

# coding:utf-8
import pymysqlclass DbMysql():# 初始化参数def __init__(self):self.host='127.0.0.1'self.user='root'self.password='root'self.db='mysql'self.port=3306self.charset='utf8'self._conn=self.get_connect()if self._conn:self._cur=self._conn.cursor()# 创建连接def get_connect(self):conn=Falsetry:conn=pymysql.connect(host=self.host,user=self.user,password=self.password,db=self.db,port=self.port,charset=self.charset)except Exception as e:print('连接数据库出错,%s' % e)return Noneelse:return conn# 创建db_area数据库里面的实时天气表,根据areaId来创建表名def creata_db_area_table(self,area_id):try:# 选择数据库self._cur.execute('USE db_area')# 创建表,用obs+地区号码来作为表名sql_create_table='CREATE TABLE obs'+area_id+''' (id VARCHAR(20),cityName VARCHAR(20),tem VARCHAR(10),rh VARCHAR(10),wd VARCHAR(10),ws VARCHAR(10),factime VARCHAR(20),areaName VARCHAR(20))'''self._cur.execute(sql_create_table)except Exception as e:print('创建表出错,%s' % e)# 出现错误-->%d format: a number is required, not str# 存储地区名称跟areaiddef save_area(self,areas):# 选择数据库self._cur.execute('USE db_area')i=0try:for area in areas:# 插入数据i+=1print("正在插入")# 由于 在插入的时候,python默认插入的字符串类型,字段都是字符串类型self._cur.execute('insert into areas values(%s,%s,%s)',(str(i),area['area_id'],area['area_name']))print("插入结束")except Exception as e:print('存储地区出错,%s' % e)passfinally:self._conn.commit()# 存储实时天气def save_obs_weather(self,weather,city_id):# 选择数据库self._cur.execute('USE db_area')try:self._cur.execute('insert into obs'+city_id+' values(%s,%s,%s,%s,%s,%s,%s,%s)',(weather['city_id'],weather['city_name'],weather['tem'],weather['rh']+"%",weather['wd'],weather['ws'],weather['factime'],weather['area_name']))print("插入一条")except Exception as e:print("存储实时天气出错,%s" % e)passfinally:self._conn.commit()# 存储未来24小时的天气def save_forecast24h_weather(self,weather):# 选择数据库self._cur.execute('USE db_area')try:self._cur.execute('insert into city_forecast_weather values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)',(weather['city_id'],weather['city_name'],weather['maxtem'],weather['mintem'],weather['daywd'],weather['dayws'],weather['nightwd'],weather['nightws'],weather['factime'],weather['area_name']))except Exception as e:print('存储未来24小时天气出错,%s' % e)passfinally:self._conn.commit()# 查询得到全部地区的areaiddef  research_for_sql(self):try:self._cur.execute('USE db_area')self._cur.execute('select *  from areas')# 查询全部,返回元组results=self._cur.fetchall()return [result[1] for result in results]except Exception as e:print('查询全部出错,%s' % e)return None# 查询一条数据def read_areas_sql(self,Id):try:# 选择数据库self._cur.execute('USE db_area')self._cur.execute('select * from areas where areaId='+str(Id))# 返回一条数据,数据类型是元组result=self._cur.fetchone()msgs,area_name=result[1][0:5],result[2]return msgs,area_nameexcept Exception as e:print('查询一条信息出错,%s' % e)return None  # 读取实时/预测天气def read_Weather(self,db_pass):self._cur.execute('USE db_area')try:# 查询天气self._cur.execute(db_pass)# 获取所有的数据results=self._cur.fetchall()return [result for result in results]except Exception as e:print('读取实时/预测天气,%s' % e)return None# 析构函数def __del__(self):self._cur.close()self._conn.close()

问题一:

# 出现错误-->%d format: a number is required, not str,尝试存储id字段为整形的数据,却报错,修改该字段类型为varchar。

数据可视化

可视化的操作都编写到一个类里面去,从数据库里面获取到实时气温和湿度用pygal库画出各个地区的实时天气,预测天气图只做了北京的最高温和最低温,其他的没有做。

import pygalclass WeatherPygal():"""docstring for WeatherPygal"""def __init__(self):self.sql="select * from city_forecast_weather where areaName="# 制作实时天气图表def show_weather(self,db_pass,sql):cityNames,tems,rhs=[],[],[]areaName=''for result in sql.read_Weather(db_pass):cityNames.append(result[1])tems.append(result[2])rhs.append(result[3])areaName=result[7]line=pygal.Line(x_label_rotation=20) line.title=areaName+'实时天气'line.x_labels=cityNamesline.add('气温°',[int(tem) for tem in tems if tem !=''])line.add('湿度%',[int(float(rh[:-1])) for rh in rhs if rh !=''])line.render_to_file(areaName+'实时天气.svg')# 预测24小时的最高和最低气温def show_forecast_weather(self,term_area,sql):cityNames,maxTems,minTems=[],[],[]areaName=''conditio=self.sql+"'"+term_area+"'"if sql.read_Weather(condition):for result in sql.read_Weather(condition):cityNames.append(result[1])maxTems.append(result[2])minTems.append(result[3])areaName=result[9]line=pygal.Line(x_label_rotation=20) line.title=areaName+'预测天气'line.x_labels=cityNamesline.add('最高气温°',[int(maxTem) for maxTem in maxTems])line.add('最低气温°',[int(minTem) for minTem in minTems])line.render_to_file(areaName+'预测天气.svg')

实现效果

项目没有用到多线程,所以相对来说慢了点,大概5分钟之内都可以完成了,有需要的伙伴可以自己优化,下一步的自己也要提高程序的效率,对程序进行优化。

链接: https://pan.baidu.com/s/1T-SHGi5haf2HoHRDiBuGgQ 密码:wzgj


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部