Use weather for wallpapers

出自Full Circle 中文项目主页

跳转到: 导航, 搜索

   网络的内容并不都是静态的,它们通过一种有意义的方式被链接起来,有些看起来甚至很酷。后Web2.0运动将混搭(Mashup)作为基本概念,将网络数据以一种新的、令人激动的形式组合起来,其基本思想就来源于此。因此欢迎来到数据朋克(Data punk)的世界。
   开始探索之旅前,我们先做一些简单的事情,例如随着天气的变化改变我们的桌面背景。目前已经有许多的桌面应用程序和部件提供这种功能,因此可以想象这些数据应该可以从互联网上免费获得。
   果然,以"weather API data"作为关键词进行搜索可以得到许多结果,我们选择的标准是:API必须容易理解,需要涵盖世界上绝大多数地区,并且需要提供可用的文档。最后一个准则排除了大多数链接,它们的文档做的不够好——是的,对你而言,花费时间和努力来理解数据或理解API是可以理解的,但是如果已经有人提供数据和使用手册的话,你还有必要花费时间和精力在那些事情上面吗?
   经过一番分析,看起来雅虎(Yahoo)的天气服务最符合我们的需求。它非常直观,并且有足够的文档,可以大幅度减少我们的工作量。深层次的学习可以从http://developer.yahoo.com/weather开始,那里提供了大量的细节和例子,希望读者可以雅虎学到很多东西。


Python.mash1.jpg

    在进行之前,需要确定位置信息,这可以通过阅读雅虎天气服务文档解决。
    雅虎天气服务的原理是在网址后面添加位置标识符,通过请求该网址就可以返回RSS格式的该地区的气象资料。在某些方面这是很有用的,因为这意味着我们不需要写任何代码就可以使用该服务,另外地区的位置代码也很容易找到,只需要到天气页面,将城市名称输入进行,看看返回页面的地址就可以找到我们需要的城市代码。
    例如在http://weather.yahoo.com/中输入"Bath,GB",返回的页面地址是"http://weather.yahoo.com/forecast/UKXX0637.html",因此我们的位置代码是UKXX0637。可能因为某种原因,这指的是Avon Park(或许这就是气象站的名字)。就这样我们获得了位置信息。操作指南还指出我们可以添加一个值来确定是返回摄氏温度(在URL结尾添加u=c)还是华氏温度。现在我们测试这个URL能否正常工作。
    一个优秀的浏览器,例如Firefox,会做很多工作来渲染RSS源,而我们需要做的就是直接输入网址:http://weather.yahooapis.com/forecastrss?p=UKXX0637&u=c

Python.mash2.jpg

    我们可以在Firefox中检查该URL产生的RSS源,看看它是否是我们需要的结果。

目录


解析器

    Okay,我们现在想要的信息可以以RSS源的方式获得,下一步要考虑如何写一个Python脚本来获得并对其进行解码。如果你在Firefox中打开该网址,并且选择View->Page Source,你可以看到很多数据以及头文件等内容。
    我们可以使用原始资料构造一个解析器,并提取我们需要的信息——但通常情况下不需要这么多,因为这件事已经有人做了。有一个叫做Feedparser的Python模块可以帮助我们获取RSS流,你最好不要通过包管理器来安装这个包,因为这会让事情变得糟糕。

为什么使用Python?

    你当然可以用很多其他语言来编写脚本和应用程序来获得网络数据,例如我们为什么使用Python而不是C#呢?有几个理由,Python是一种简单直观的语言,写代码比较容易,也比较容易理解和阅读你写的代码。
    在使用和操作文本方面Python非常优秀(对于我们的应用正是如此),而且它是跨平台的,有各种各样的网络服务和协议可供使用。通过使用Python和库,你可以在很短的时间内创建一个应用程序或脚本。
    安装Feedparser之后,还需要一些时间来编码。首先从命令行启动Python,这样我们可以看到所处理的内容。我们首先以交互式的方式运行Python。
>>> import feedparser
>>> url = "http://weather.yahooapis.com/forecastrss?p=UKXX0637&u=c"
>>> data = feedparser.parse(url)
>>> data
    最后一行给出一列字符,代表查询源的结果。幸运的是虽然它看起来是一列用括号括起来的随机字符,但实际上这是一个合理构架的对象,你可以通过如下的代码进行验证。
>>>for x in data :
... print x ...
feed
status
version
encoding
bozo
headers
etag
href
namespaces
entries
    这个简单循环给出了数据中的每个对象,Python最伟大的一点就是很容易操纵对象,甚至可以给出自身的信息,例如我们这里可能想精确的知道对象的类型。
>>>type(data)
<class 'feedparser.FeedParserDict'>
    结果给出了一些信息。如果以前用Python,你可能接触过字典对象,它用来存储数据的关键词-值对,本例中我们已经列出了所有的关键词。如果我们浏览Feedparser的文档,我们可以知道这些项所代表的意义。其中entries关键词非常重要,它包括了我们所需要的信息。
>>> data.entries[0]
{'updated': u'Wed, 1 Apr 2009 12:50 am BST', 'yweather_condition': u, 'updated_parsed': ...
>>> for x in data.entries[0]
... print x
...
updated
yweather_condition
updated_parsed
links
title
summary_detail
geo_lat
summary
guidislink
title_detail
link
geo_long
yweather_forecast
id
    至此我们有了关键词-值对。这些关键词-值对由XML结构本身定义,因此并没有哪个模块定义了对象的蓝图,虽然这些项目已经在Yahoo的网站上给出了文档。经过分析,摘要可能是对我们最有用的,因为它包括了当前的气象状况,如气温等。
    有一个小问题是我们所需要的数据并不在一个关键词内,这种特殊的源数据是以HTML形式格式化的,其目标是在网页上显示,但我们需要的仅仅是文本数据。


正则表达式

    正则表达式在很多应用中是必不可缺的(regular expressions或regexes),特别是在混搭(mash-up)应用中。正则表达式是一种特殊的表达式,用来进行高速的搜索和替换,虽然它看起来很神秘,但是并不难以理解。本案例中,我们需要做的是删除那些不需要的HTML标记,虽然我们也可以提取所需要的数据,我们希望这是有益的,特别是以后我们需要这个脚本来处理不同源的时候。
    我们可以导入Python的re模块(它是标准库之一,因此不需要下载),并且将其应用于文本之上。限于篇幅,我们并不打算解释正则表达式的工作原理,但是下文将给出正则表达式的更多信息。
    正则表达式的应用范围很广,既可以用于简单的情况,也可以用于很复杂的情形。简而言之,正则表达式是一种表达字符中特定组合的形式,还可以表达特殊字符,例如可以用于匹配特殊字符。另外还包括列表、组甚至操作符,所有的东西都可以用于匹配,你可以通过正则表达式来进行搜索和匹配。
    当你开始创建匹配呢,最好寻找一种工具来帮你检查匹配模式是否正确-在正则表达式中一个错误的字符都会引起灾难,在线的正则表达式创建器http://www.gskinner.com/RegExr/desktop非常优秀,可以通过粘贴样本文本并检测正则表达式,你可能需要访问Python的正则表达式文档http://docs.python.org/library/re.html。

Python.mash3.jpg

    正则表达式对任何人来说都不是有趣的,不过你可以从http://www.gskinner.com/RegExr/desktop获得帮助。
    对我们的表达式而言,需要匹配'<'和'>'之间的内容作为HTML标记,这是很容易的,表达式需要一个<,紧跟着一些字符.+?,并以>结尾,+?与+*一样,但是一种非贪婪匹配,意味着仅匹配最短合法字符,包括<>之间的内容,而不包括其他内容,这才是我们想要的。
>>>summary = data.entries[0].summary
>>>import re
>>>pattern = '<.+?>'
>>>temp = re.sub(pattern,,summary)
>>>temp
u'\nCurrent Conditions:\nHaze, 13 C\nForecast:\nThu - Rain. High: 14 Low: 9\nFri - Light Rain. High: 12 Low: 7\n\n Full Forecast at Yahoo! Weather\n(provided by The Weather Channel)'
    现在已经没有HTML标记了,但是还有换行符。我们可以使用标准的string模块将其分割为一个列表,但是我们需要重新加载re模块。我们需要搜索换行符,这可以通过在字符串前面添加一个r将其变为raw string。
>>> temp = re.split(r'\n',temp)
>>> temp
[u
, u'Current Conditions:', u'Haze, 13 C', u'Forecast:', u'Thu - Rain. High: 14 Low: 9', u'Fri - Light Rain. High: 12 Low: 7', u, u'Full Forecast at Yahoo! Weather', u'(provided by The Weather Channel)'] >>>temp[2] u'Haze, 13 C'
    可以看到我们假设结果的第三个元素是我们想要的字符串,为了从这里提取温度,还需要进一步利用正则表达式提取数字。
>>>temp = re.findall(u'[0-9]+',temp[2])[0]
>>>temp
u'13'
>>>
>>> temp = int(temp)
>>> temp
13
    最后一步是使用Python的内置方法将字符串转换为数字,在现实世界中我们可能将这些步骤结合起来提高效率,但是其开销相当小,将其分解为多个步骤可以使其看起来更加明了。现在要做的就是实现桌面背景与天气之间的联系。


天气的改变

    我们如何改变桌面背景呢?实际上我们可以通过Python调用外部命令来实现,对于Gnome而言,可以通过调用gconftool-2来更改桌面背景,它通过修改桌面背景图的文件名了修改桌面背景,这就是我们需要的。但是,我们可以通过几个变量,将这个过程变成一个函数。
>>> def change_wallpaper(filename):
... cmd = .join(["gconftool-2 -s /desktop/gnome/background/picture_filename -t string \"",filename,"\""])
... os.system(cmd)
...
>>> change_wallpaper('plop.jpg')
    第一行定义了一个函数,调用了os.system函数。当我们调用这个函数时,系统变量被修改并加载了新的图像。我们这里使用的文件是一个虚拟文件,在你的主目录存放图像是一个好主意,例如在主目录下创建一个名为weather的文件夹,并将图像名称适当的进行命名。在Gnome中,你还可以使用svg图像来设置桌面,这意味着你可以创建一些可扩展的图像应用于桌面。
    现在我们在考虑你或许应该准备5个图像-冷、凉、温和、温暖、热。你需要根据地理位置相应的调整温度范围,例如0℃以下为冷、0-8℃是凉、9-15℃为温和、16-25为温暖、25℃以上为热。
    在某些语言总你可以使用case/switch来解决该问题,Python中没有这种结构,因此你只能使用if/elif/else来解决这个问题:
>>> if (temp <0) :
... change_wallpaper('/home/evilnick/weather/freezing.svg')
... elif (temp<9) :
... change_wallpaper('/home/evilnick/weather/snow.svg')
... elif (temp<16) :
... change_wallpaper('/home/evilnick/weather/mild.svg')
... elif (temp<26):
... change_wallpaper('/home/evilnick/weather/warm.svg')
... else:
... change_wallpaper('/home/evilnick/weather/hot.svg')
...
    如果将上述脚本写在一起,看起来应该如下,你所需要的是提供图片。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import feedparser,re,os
def change_wallpaper(filename):
cmd =
.join(["gconftool-2 -s /desktop/gnome/background/picture_filename -t string \"",filename,"\""])
os.system(cmd)
url = "http://weather.yahooapis.com/forecastrss?p=UKXX0637&u=c"
data = feedparser.parse(url)
# extract the summary from the data
summary = data.entries[0].summary
temp = re.split(r'\n',re.sub('<.+?>',,summary))
temp = int(re.findall('[0-9]+',temp[2])[0])
if (temp <0) :
change_wallpaper('/home/evilnick/weather/freezing.svg')
elif (temp<9) :
change_wallpaper('/home/evilnick/weather/snow.svg')
elif (temp<16) :
change_wallpaper('/home/evilnick/weather/mild.svg')
elif (temp<26):
change_wallpaper('/home/evilnick/weather/warm.svg')
else:
change_wallpaper('/home/evilnick/weather/hot.svg')
    当然这只是个小脚本,并不是一个具有想象力的应用程序,但这是一个基础。我们已经实现了从网络上获得数据并将其自动纳入我们的桌面应用中。我们已经看到了RSS源是如何工作的以及如何在Python中对其进行操作,其中涉及了可怕的正则表达式以及系统调用。这就是我们所做的探索工作。
    你可以很容易的扩展这个脚本,也可以实现用户位置的选择或者将其变为一个applet。目前在不联网的情况下它会失败,这是不理想的,但是我们可以在后续教程中学到很多的技巧。

    为什么不用一个基于SVG的漂亮的雪花壁纸呢,它也可以属于你。


网络数据格式

    我们确定了网络应用的存在,它们可以提供一些y有用的数据。但是你不希望这些应用如此简单,对吗?有几种不同的协议或者方法,它们可以提供这些数据,在某些情况(例如Flickr)他们提供很多信息。在更复杂的情况下,他们有时甚至是跨站点的。在未来的几个月内,我们会逐步涉及更高级的协议,请耐心等待。

个人工具