Not to Hot for Mojo
I saw a post out in the Blogosphere today about getting weather info from NOAA (The United States National Weather Service). Oh! the horrors of using XML::LibXML or XML::DOM or those other hairy XML modules to get at the data.
The blogger didn't seem to keen on my quick and dirty Mojolicious solution:
$ perl -Mojo -E 'say g("http://w1.weather.gov/xml/current_obs/KBUR.xml")->dom("temp_f")->first->text'
76.0
I guess I was a bit too brief and off the point, so here's a nicer example:
NOAA provides an XML response that goes something like:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="latest_ob.xsl" type="text/xsl"?>
<current_observation version="1.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.weather.gov/view/current_observation.xsd">
<credit>NOAA's National Weather Service</credit>
<credit_URL>http://weather.gov/</credit_URL>
<image>
<url>http://weather.gov/images/xml_logo.gif</url>
<title>NOAA's National Weather Service</title>
<link>http://weather.gov</link>
</image>
<suggested_pickup>15 minutes after the hour</suggested_pickup>
<suggested_pickup_period>60</suggested_pickup_period>
<location>Burbank/Glendale, CA</location>
<station_id>KBUR</station_id>
<latitude>34.2</latitude>
<longitude>-118.36</longitude>
<observation_time>Last Updated on Aug 11 2012, 1:53 am PDT</observation_time>
<observation_time_rfc822>Sat, 11 Aug 2012 01:53:00 -0700</observation_time_rfc822>
<weather>Fair</weather>
<temperature_string>76.0 F (24.4 C)</temperature_string>
<temp_f>76.0</temp_f>
<temp_c>24.4</temp_c>
<relative_humidity>56</relative_humidity>
<wind_string>East at 3.5 MPH (3 KT)</wind_string>
<wind_dir>East</wind_dir>
<wind_degrees>90</wind_degrees>
<wind_mph>3.5</wind_mph>
<wind_kt>3</wind_kt>
<pressure_string>1006.3 mb</pressure_string>
<pressure_mb>1006.3</pressure_mb>
<pressure_in>29.76</pressure_in>
<dewpoint_string>59.0 F (15.0 C)</dewpoint_string>
<dewpoint_f>59.0</dewpoint_f>
<dewpoint_c>15.0</dewpoint_c>
<visibility_mi>10.00</visibility_mi>
<icon_url_base>http://w1.weather.gov/images/fcicons/</icon_url_base>
<two_day_history_url>http://www.weather.gov/data/obhistory/KBUR.html</two_day_history_url>
<icon_url_name>nskc.jpg</icon_url_name>
<ob_url>http://www.nws.noaa.gov/data/METAR/KBUR.1.txt</ob_url>
<disclaimer_url>http://weather.gov/disclaimer.html</disclaimer_url>
<copyright_url>http://weather.gov/disclaimer.html</copyright_url>
<privacy_policy_url>http://weather.gov/notice.html</privacy_policy_url>
</current_observation>
And Mojolicious has become my go-to tool for simple HTTP/HTML/XML/DOM/CSS voodoo. Here's a basic script to fetch the current temperature (°F):
#!/usr/bin/env perl
use strict;
use warnings;
use utf8::all; # for °
use feature 'say';
use Mojolicious;
my $ua = Mojo::UserAgent->new;
my $dom = $ua->get('http://w1.weather.gov/xml/current_obs/KBUR.xml')->res->dom;
my $temp_f = $dom->find('current_observation temp_f:only-of-type')->first;
# or depending on how loose you want to play it.
# $temp_f = $dom->find('temp_f')->[0];
say "It's currently ", $temp_f->text, "°F in Burbank.";
__END__
It's currently 76.0°F in Burbank.
That's just awesome, but wait, there's more. Mojolicious has the ojo
module that imports a few handy functions like g()
which does the whole Mojo::UserAgent->new->get()->res
bit in one go. ojo
makes it easy to do one-liners. How Perlish is that? And there's even a command line tool called mojo
that makes things even easier:
$ mojo get http://w1.weather.gov/xml/current_obs/KBUR.xml temp_f 0 text
76.0
So do yourself a favor and check out Mojolicious, especially the Mojocasts. You'll probably stop using all those nasty XML modules unless you really have no other choice.
perl -MLWP::Simple=get -MXML::TreeBuilder -E'my $xml = XML::TreeBuilder->new; $xml->parse(get "http://w1.weather.gov/xml/current_obs/KBUR.xml"); say "Temperature is " . $_->as_text . "°C" for $xml->look_down(_tag => "temp_c")'
Indeed. And for XML::LibXML...
perl -MXML::LibXML -E'say "Temperature is ", $_->textContent, "°C" for XML::LibXML->load_xml(location => "http://w1.weather.gov/xml/current_obs/KBUR.xml")->getElementsByTagName("temp_c")'