MATLAB 爬取天气预报数据

今天主题的格局未免有点小,因为爬虫这件事说起来简单,但当真正实践的时候费时又费力。关注我的朋友中不乏有MATLAB爬虫高手,小弟在此献丑了!
在这里插入图片描述
思考爬取的数据能干什么有意义的事以及对数据的分析、处理、推出结论,我认为这才是比较重要的。提到爬虫不得不想到正则表达式(Regular Expression),爬虫的基本思路是本地服务器发送HTTP请求,获取目标地址内容,然后正则匹配提取需要的文本或数据。有时候在请求HTTP时会被反爬虫,得不到目标地址内容,这时就需要一些特殊的技术来应对了,比如大家熟知的配置代理服务器。

关于正则表达式的语法规则可以在网上直接搜到,于各种语言工具基本都是通用的。MATLAB支持正则表达式,并且功能很强大,通过 regexp, regexpi, regexprep 函数来匹配或者替换内容,具体的用法可通过 help | doc regexp, regexpi, regexprep 帮助使用。请求HTTP需要用到 webread , weboptions 配置网络参数。 weboptions 的属性下表的描述非常清晰:
在这里插入图片描述
这里最常用到的属性 CharacterEncoding 指定爬取到内容的字符编码,通常设定为 ‘UTF-8’, 对于中文这样的特殊字符就不会乱码;UserAgent 指定用户代理,比如 ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36’Timeout 响应时间,默认是5s,可以将这个值设的高一些,如10, 20s; ContentType 内容类型,可选值有:‘text’,‘image’, ‘audio’, ‘binary’, ‘table’, ‘json’, ‘xmldom’, ‘raw’, ‘auto’

MATLAB获取天气数据有两种方法:第一种就是爬虫的方法,解析HTML页面的内容;第二种是通过天气预报网站提供的API,直接获取结构化数据,省去解析HTML页面内容这一块。下面我们会分别介绍这两种方法。

  • 利用中国天气网站提供的API

中国天气网站的地址是 http://www.weather.com.cn/ ,如下图所示
在这里插入图片描述
获取不同城市的天气预报API,请求地址是 http://www.weather.com.cn/data/sk/城市代码.html ,城市代码可以直接在网上搜索得到。比如通渭的代码是 101160202,西安的是 101110101。返回参数如下表所示

NameDescription
weatherinfoweatherinfo 消息根节点
city城市中文名
cityid城市代码
temp温度
WD风向
WS风力
SD湿度
AP压强
WSE风力
time发布时间
isRadar是否有雷达图
Radar雷达图编号

该信息其实就是一个格式化的json数据,可以通过如下MATLAB代码直接解析:

clear
clc
% 101110101/101160202/
url = 'http://www.weather.com.cn/data/sk/101110101.html';
options = weboptions('ContentType','text', 'Timeout', 10);
options.CharacterEncoding = 'UTF-8';
options. ContentType = 'json';
options.RequestMethod = 'get';
htmlContent = webread(url, options);
allInfo = htmlContent.weatherinfo;

在这里插入图片描述
OMG! 西安温度23.3 oC,从发布时间看是18:00,总感觉这个API提供的数据有问题
在这里插入图片描述
当然,举这个例子仅仅是启发,可通过设置 weboptions 的属性可以获取结构化的数据,这里再推荐一个天气网站API:彩云天气,使用这个网站API需要付费!贫穷限制了我的想象和尝试,但我就不行没钱啥也干不了?在我的一顿狂搜下,发现了新大陆:https://www.tianqi.com,这个天气网站还是很不错的,更人性化的是提供了历史天气数据。

  • 爬取天气网站数据

综合网页分析发现,天气网站的请求地址是:“https://www.tianqi.com/城市名/”,如下图所示
在这里插入图片描述
将手机APP上的天气数据做一对比:
在这里插入图片描述
OMG! 这数据还可以…
在这里插入图片描述
接下来就是如何获取HTML内容和爬取关键数据的问题了。在此我们封装一个 Weather 基类,用于获取HTML内容和保存爬取结果,将具体的内容解析延迟到 CurrentWeather 子类中,源代码如下:
Weather.m

classdef Weather < handle
    %WEATHER   The Interface of Weather Report
    %   Writen by QPTechnology.
    %   Date Mar 11, 2019.
    %   Copyright 2018-2020 QPing Technology Inc.
    %
    properties(Access = protected)
        Url
    end  % end properties
    
    methods(Access = protected)
        % Constructor
        function obj = Weather(url)
            obj.Url = url;
        end  % end constructor
        
        % getHTMLContent
        function htmlContent = getHTMLContent(obj)
            options = weboptions('ContentType','text', 'Timeout', 10);
            options.CharacterEncoding = 'UTF-8';
            htmlContent = webread(obj.Url, options);
        end  % end getHTMLContent
       
    end  % end methods
    
    methods(Static)
        % saveResult
        function saveResult(filename, data)
            if ~exist('scrap_result', 'dir')
                mkdir('scrap_result');
            end  % end if
            xlswrite(['scrap_result/', filename, '.xls'], data);
        end  % end saveResult
    end  % end static methods
    
    methods(Abstract)
        runCapture(obj);
    end  % end abstract methods
    
end  % end classdef

CurrentWeather.m

classdef CurrentWeather < Weather
    %CURRENTWEATHER   Capture the current weather
    %   obj = CurrentWeather(cityName, url) construct a object of
    %   CurrentWeather. cityName is a string city name, example 'beijing'
    %   url is one of the address of www.tiqi.com/.
    %   Writen by QPing Technology.
    %   Date Mar 11, 2019.
    %   Copyright 2018-2020 QPing Technology Inc.
    %
    properties
        CityName
    end  % end properties
    
    methods
        % Constructor
        function obj = CurrentWeather(cityName, url)
            obj = obj@Weather(url);
            obj.CityName = cityName;
        end  % end constructor
        
        % runCapture
        function allInfo = runCapture(obj)
            htmlContent = obj.getHTMLContent();
            pat = '(?<=<dd class="name"><h2>)\S*(?=</h2><i>)';
            cityName = regexp(htmlContent, pat, 'match');
            pat = '(?<=<dd class="week">).+?(?=</dd>)';
            currentTime = regexp(htmlContent, pat, 'match');
            pat = '(?<=<p class="now"><b>)\-?\d+(?=</b><i>)';
            nowTempe = regexp(htmlContent, pat, 'match');
            nowTempe = regexprep(nowTempe, '\-?(\d+)', '$1℃');
            nowWeatherState = regexp(htmlContent, '(?<=<span><b>).*?(?=</b>)', 'match');
            pat = '(?<=</b>)\-?\d+\s~\s\-?\d+℃(?=</span>)';
            rangeTempe = regexp(htmlContent, pat, 'match');
            pat = '(?<=<dd class="shidu"><b>).*?(?=</b><b>)';
            nowHumidity = regexp(htmlContent, pat, 'match');
            windDirection = regexp(htmlContent, '(?<=%</b><b>).*?(?=</b><b>)', 'match');
            ultravioletRay = regexp(htmlContent, '(?<=</b><b>).*?(?=</b></dd>)', 'match');
            ultravioletRay = regexprep(ultravioletRay, '.*</b><b>', '');
            airQuality = regexp(htmlContent, '(?<=;">)\D*(?=</h5><h6>)', 'match');
            
            allInfo = [append(append(cityName, {': '}), nowWeatherState); ...
                append(append(nowTempe, {'  range:'}), rangeTempe); ...
                nowHumidity; ...
                windDirection; ...
                ultravioletRay; ...
                airQuality; ...
                currentTime];
            clear cityName nowWeatherState nowTempe rangeTempe nowHumidity ...
                windDirection ultravioletRay airQuality currentTime
            % save result
            filename = [obj.CityName, class(obj)];
            obj.saveResult(filename, allInfo);
        end  % end runCapture
        
    end  % end methods
    
end  % end classdef

可以用如下脚本爬取指定的城市天气状况:

captureCurrentWeather.m

% This script is the main of capture a city weather report.

clear
clc

%% Get the current day weather of a city.
cityName = split(input('Please enter cities name with spaces: ', 's'));
numCity = length(cityName);
allInfo = [];
for ii = 1 : numCity
    url = ['https://www.tianqi.com/', cityName{ii}, '/'];
    currentObj = CurrentWeather(cityName{ii}, url);
    result = currentObj.runCapture();
    allInfo = [allInfo; {' '}; result(1:end-1)];
end

allInfoNew = [allInfo; {' '}; result(end)];
clear allInfo

最后就是一顿猛爬操作…
在这里插入图片描述
说明:上面 captureCurrentWeather.m 脚本中省略了发邮件的代码,爬取的结果以发邮件的形式被接受到我们的手机上,关于MATLAB发邮件,可以参考 公众号:清贫王子 之前推送的主题 文章链接: Matlab如何发送电子邮件. 顺便吐槽一下自己之前的编码风格,真的太low!
在这里插入图片描述
查看发送到手机上的信息如下:
在这里插入图片描述
至此,实时爬取城市天气预报的介绍就结束了。Wait… 这有神马用处了?其实实时爬取城市天气预报还是有点用的,那就是通过设定计算机任务,每天固定时间执行一次 captureCurrentWeather.m 脚本,将设定城市当天的天气预报数据实时爬取下来,然后发送到我们的手机上,这样就免去了来回切换手机APP的麻烦,又或者将以上程序部署到服务器上,这样能真正的每天按时推送到我们的手机上了,这算不算 ”企业级“装逼神器?


既然天气网站提供了各城市历史天气数据,我们有必要再猛爬一下(注意别爬过火)。关于爬取历史天气数据的源代码在这里就不贴了,感兴趣的朋友可以在后台回复 历史天气, 注意是 历史天气,而不是历史的天空, 更不是太阳的后裔,扯远了。。。

这里我们爬取三个城市(通渭,天津,西安)在2019年的历史数据,三座城,三个故事,三种情怀。下面我们分别看看三座城市在过去一年(2019)的最高温、平均温、最低温比较情况:

最高温
在这里插入图片描述
平均温
在这里插入图片描述
最低温
在这里插入图片描述
三座城市就拿最高温来说,西安,天津的最高温超过或者接近40 oC,而通渭的最高温不到30 oC,所以每年的夏天,家乡通渭被称之为避暑圣地那是有一定的道理的,平均温度也能反映出这样的结论。相反,通渭的冬天异常寒冷,让人色色发抖,相比天津和西安要冷的多,所以冬天的话就建议待在大城市吧,毕竟最低温高啊, 同时温差大也是一方面。

2019年通渭最高、最低温
在这里插入图片描述
当然,更多的数据我们可以爬取下来保存成excel等用于后续的数据分析。
在这里插入图片描述


今天的内容就到此结束了,如果觉得对您有帮助,帮忙点在看或者转发下,让更多的初学者受益。最后祝大家周末愉快,战胜疫情,武汉加油,中国加油,世界加油!

欢迎对MATLAB GUI,APP以及爬虫感兴趣的朋友关注卫星公众号 清贫王子, 更多的GUI和APP干货将在这里分享, 谢谢大家!
在这里插入图片描述

  • 6
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值