也不知道有多久了,从接近开学一直到现在,杭州天天都在下雨,估计至少有二十天了吧。实在是一件让人非常郁闷的事。之前看到寒仔在 cc98 上的签名档图片是根据时间动态生成的,觉得很好玩,于是也决定弄一个动态的签名档玩玩,正好就做天气预报好了。
首先要选定一个天气来源。之前听说 iGoogle 里面的天气预报小工具预报得非常不准,除了“Current”里的天气情况和目前室外的情况差不多之外,预报的结果似乎完全不可信。 ^_^bb 其实我也完全没有看天气预报的习惯,反正我天天都带着伞的。那么我也干脆弄一个“即时”天气好了,显示当前天气,可以“足不出户,知窗外天气”——关键是要做得漂亮些。但是我又懒得去找那些天气相关的图片素材,最后随便挑选了几个天气来源,就直接选了 Yahoo Weather 。
Yahoo Weather 提供了 RSS feed 形式的详细天气预报,不过由于我想要他的 HTML 页面上那个漂亮的图标,就直接去解析他的 HTML 页面了。和 Google 那种一看就是机器生成,说不定还是故意经过混淆、压缩过的乱糟糟的 HTML 页面不同,Yahoo 的页面结构清晰得多,用正则表达式很轻松地就把温度、天气图标和背景图片给提取出来了。
接下来是要把这几个东西拼成一个图片,我用 Python Imaging Library (PIL) 来做这件事情。首先创建一个空的图片,然后把背景粘贴进去。Yahoo Weather 在白天和晚上的时候使用不同的背景,并在 HTML 中有标识,所以当能在 HTML 页面中找到相应的标识 pat_night
的时候就是用晚上的背景:
img = Image.new('RGBA', (240, 140)) if pat_night.search(page): img.paste(background_nt, (40, 46), background) else: img.paste(background, (40, 46), background) |
PIL 提供的图片粘贴函数直接粘贴的话,会把整个区域都替换掉,即时你粘贴的是一个带 Alpha 通道的透明 PNG 图片也是如此,不过可以在第三个参数那里再传递一下这个图片本身,这是只有那些不透明的像素才会被粘贴(并覆盖)过去。
用类似的办法把天气图标粘贴过去之后,就剩下把温度信息写上去了。在 PIL 中要在一个图片上进行绘制,需要创建一个 ImageDraw
对象:
draw = ImageDraw.Draw(img) draw.text((180, 53), '%-2sC'%degree, font=font) draw.text((203, 53), 'o', font=font_small) |
不知道是字体的原因还是编码处理的原因,我在字符串里直接写度数的符号画出来是乱码,所以我只好用小号字体画了个小写的 ‘o’ 。PIL 中可以很方便地使用 TrueType 字体,就像这样:
font = ImageFont.truetype('font.ttf', 24) |
画完之后,ImageDraw
对象就不再需要了,最终图片保存到文件中即可:
del draw img.save('weather.png', 'PNG') |
接下来只要通过 Web Server 让这个图片可以通过网络被访问到,然后再让签名档图片指向对应的 URL 就可以了。当然,这个脚本要定期地更新天气情况并重新生成图片。完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #!/usr/bin/python import urllib2 import re import time from StringIO import StringIO from PIL import Image, ImageDraw, ImageFont degree = None icon = None icon_url_prev = None background = Image.open('bg.png') background_nt = Image.open('bg_night.png') font = ImageFont.truetype('font.ttf', 24) font_small = ImageFont.truetype('font.ttf', 10) pat_degree = re.compile(r'<h3>(\d+)&(?:#176|deg);</h3>') pat_icon = re.compile('<div class="forecast-icon" style="background:url\\(\'' + '([^\']+)\'\\);') pat_night = re.compile(r'<div id="yw-forecast" class="night">') while True: page = urllib2.urlopen('http://weather.yahoo.com/forecast/CHXX0044_c.html').read() #page = urllib2.urlopen('http://weather.yahoo.com/forecast/USNY0996.html').read() degree = pat_degree.search(page).group(1) icon_url = pat_icon.search(page).group(1) if icon_url_prev != icon_url: icon = Image.open(StringIO(urllib2.urlopen(icon_url).read())) img = Image.new('RGBA', (240, 140)) if pat_night.search(page): img.paste(background_nt, (40, 46), background) else: img.paste(background, (40, 46), background) img.paste(icon, (5, 5), icon) draw = ImageDraw.Draw(img) draw.text((180, 53), '%-2sC'%degree, font=font) draw.text((203, 53), 'o', font=font_small) del draw img.save('weather.png', 'PNG') icon_url_prev = icon_url print '%s deg. [%s]' % (degree, time.ctime(time.time())) time.sleep(10*60) |
BTW: 本文开头那个图片只是一个普通的效果图,并不是实时更新的。 😉
第17行那个 ° 是什么意思呢?
@圆圆
这个留言系统不好啊……555
明明是写的 & # 176,怎么变成小圆圆了呢……
@圆圆
汗…… ^_^bb
因为
176;
确实就是 ° 就是“度数”的符号。这种写法写的东西叫做 HTML Entities ,一些常用的就是有名字的,比如&
lt;
是 < ,就是 less than ,其他的是用字符编码对应的数字来表示的。@pluskid
原来如此~ 诶话说 w3schools 的页面风格好像换了呢:O
要是这样会怎么样呢哈哈?
哈哈变成小圆圆啦!Hooray!
那个太小,像猪鼻子……
这个也像,算了,还是换回圆圆吧。谢谢 kid,晚安啦啦啦
哇,果然啊
我运行后显示,怎么回事?
@°
恩?看起来像是网络错误,你那边能打开 Yahoo 天气那个页面吗?
unicode文字需要明示指定,38L在字符串前加上u就好了。如下:
draw.text((180, 53), u’%-2s℃’%degree, font=font)
原来是zju的师兄,感觉师兄你super 厉害啊!有时间自己也在98上弄一个
想问一下是放到什么Web Server?怎么用脚本自动更新的?
这个是好多好多年前了哈哈