定制自己的免费天气预报短信

摘要:这篇博客介绍了一种在 Linux 下使用飞信(libfetion 库)来定时发送天气预报短信的方法。本文的主要贡献是:一、提供了一个 Linux 下发送飞信的命令行程序;二、提供了一个到中国气象网抓取、过滤天气信息并发送短信的脚本。

天气预报短信一直是移动通信公司提供的一种收费服务,Google 免费天气预报服务打破了这个僵局。但是Google 的服务很不稳定,经常收不到短信,而且天气预报内容的定制性差。我家 xixi 一直有看天气预报的习惯,我就告诉她说我能写个程序每天给你发天气预报消息,她不相信,然后我就写了下面的程序。

首先感谢一下 mirth@bbs.nju.edu.cn,本文的主要内容是基于他在小百合 BBS 上发表的如何用飞信定时给自己发免费天气预报一文做的少许改进。

1. 发送飞信的命令行程序[1, 2, 3, 4, 5]

这个程序主要基于邓东东开发的 libfetion 库。这个库不是开源的,但是作者提供了头文件和库文件(在GUI源代码中),所以我们可以使用它的 API 来写一些自己的程序。下面的程序内容很简单,注释也不少,我就只贴源码,不再解释了(注意,编译时需要 curl 的 dev 库)。你可以在这里下载到我的 sendsms 小程序的源代码

sendsms
|-- Makefile
|-- include
|   |-- common.h
|   |-- datastruct.h
|   |-- event.h
|   |-- fxconfig.h
|   `-- libfetion.h
|-- lib
|   |-- libfetion_32.a
|   `-- libfetion_64.a
|-- sendsms
`-- sendsms.cpp

2. 到中国气象网抓取、过滤天气信息并发送短信的 bash 脚本

你可以从这里下载到下面的 bash 脚本,或者到这里下载几乎同样功能的 python 脚本。脚本就不多做解释了,没几行代码,相信稍微研究一下就能看懂。

天气网经常更新,新的脚本我就不再贴到博客里了。如果您发现天气预报脚本不好用了,就请关注脚本下载的地址,我一般会尽快更新的。

$ more weatherman.sh
#!/bin/bash
# This script fetch user specified citys' weather forecast from
# http://weather.com.cn, and send them using a CLI SMS sender "sendsms"
# which you can get from http://share.solrex.cn/dcount/click.php?id=5.
#
# You can look for new or bug fix version
# @ http://share.solrex.cn/scripts/weatherman.sh.
# Copyright (C) Solrex Yang <http://solrex.cn> with GPL license.
#
# Usage: You should add it to crontab by "crontab -e", and then add a line
# such as:
# 00 20 * * * /usr/bin/weatherman.sh >> ~/bin/log/weatherman.log 2>&1
# which will send weather forecast to your fetion friends at every 8pm.

3. 将脚本设置为定时执行

安装好 sendsms 到 /usr/bin 之后,将上面脚本放到 YOURPATH 下,然后在命令行执行:crontab -e,将下面一行添加进去:

50 19 * * * /YOURPATH/weatherman.sh 1> /tmp/weatherman.out 2> /tmp/weatherman.err

就设置为每天下午 7 点 50 发送天气预报短信。

[1] 应大家要求,在程序中加入了读取 http_proxy 代理服务器环境变量的部分,其它类型的代理服务器可以自行添加(毕竟源代码给你了,随便改),增加了重试登录和发送的代码。

[2] 2008 年 11 月 30 日:增加了群发短信功能(多个接收者用','分隔)。

[3] 2009 年 01 月 11 日:增加从标准输入读入信息支持,可使用管道和输入重定向。这篇博客中的代码就不更新了,请到给出的链接去下载新版本。

[4] 2009 年 4 月 17 日:添加了"-l"选项,支持长短信发送,最长可到 1024 字节。解决了一个从标准输入读取短信的 bug。

[5] 2009 年 12 月 08 日:根据中国天气网的改版,更新抓取页面的脚本。

  1. 2008年10月29日12:35 | #1

    Google的天气预报的确不好用,订阅了好久了,到现在就收到过一次-_-!

  2. angelsmud
    2008年10月29日12:52 | #2

    呵呵,好东西,收来泡妹妹。

  3. 这个现在就能试试吗?
    2008年10月29日15:38 | #3

    具体怎么弄呢?想试试今天下午能不能收到天气短信……

  4. 2008年10月29日16:18 | #4

    不错啊
    libfetion库虽然没公开代码,但是有个.a用已经不错了

  5. 2008年10月29日16:37 | #5

    不错,百合上还是有高人啊。谢谢!

    建议在 login 之前设置登录状态为 OFFLINE。另外有人用代理的话还可以加入代理支持。(我没需求,就懒得弄了。)

    fprintf(stderr, "Failed to init.\n");
    return 1;
    }
    + fx_set_login_status(FX_STATUS_OFFLINE);
    ret = fs_login(args.from, args.passwd); /* Login with id and passwd. */
    if (!ret) {
    fprintf(stderr, "Failed to login.\n");

  6. 2008年10月29日16:46 | #6

    百合上的好东西还真不少阿~
    我定的google每天都来阿
    不过时间不好,都是早上九点,应该晚上比较理想

  7. nettest
    2008年10月29日18:31 | #7

    请问如果将该脚本放在网内,如何设置使用代理呀?

  8. Solrex Yang
    2008年10月29日19:35 | #8

    [Comment ID #275331 Will Be Quoted Here]
    已经 update 到支持代理了,不过没有测试(没有测试环境 ^_^)
    谁要是发现代理功能不可用,就来评论一下吧。

  9. 2008年10月29日23:22 | #9

    赞!
    这才是干正事啊
    我都是用libfetion干坏事的
    经常用libfetion把一本小说发给同学,让他手机响一下午 :)

  10. nettest
    2008年10月30日08:52 | #10

    我又重新下载试了一下,在使用代理环境下测试,对于“MY_CITIES”里,我也放入了两个城市,只有第一个城市可以,在发送第二个城市的时候就提示说“Failed to login.”现截取Terminal输出:
    get the ssic is ... (评论过长,讨论结束后部分内容被删除)

  11. Solrex Yang
    2008年10月30日09:05 | #11

    @nettest:
    这个飞信库有时候登录会失败的,这是很正常的现象,通常情况下你可以选择重试(即加一个 while loop,每当返回值不是成功的时候就重新尝试登录或者发送)。
    只是我没有将重试写在代码中,因为我不喜欢这种在程序中强力循环的方式,万一网络有问题,程序就一直在那 loop。为避免这个问题你可以加一个重试次数的上限。

  12. nettest
    2008年10月30日10:48 | #12

    而后我又一再试了一下,好不容易,脚本全部发送成功!是的,确实是如你所说的飞信库的登录问题。
    在此甚为感谢博主的分享,这东西太实用了,谢谢!
    现在就是担心飞信这免费的功能能走多远!

  13. 2008年10月31日09:05 | #13

    最近也想好好利用下飞信 :-)
    貌似从这周一开始调用别人php实现的飞信,给朋友每天自动发点东西

    又在学python所以想把openfetion用python实现下...

  14. nettest
    2008年10月31日09:45 | #14

    再给点建议改进一下:
    能不能将脚本中的循环移进主程序中,这样只登录一次,就可以把所有的信息发送完。不用每发一条短信,又重新登录一次。

  15. Solrex Yang
    2008年11月2日17:14 | #15

    这样会引起程序太复杂,还是模块化一点比较好。您如果需要这个特性,完全可以自己添加。

  16. makefile
    2008年11月3日15:06 | #16

    hi,你好
    我用下面这个函数发送消息给指定手机号码,这个号码非好友。
    fs_send_sms_by_mobile_no(args.to, args.message);
    可是总是不成功。有什么问题吗?

  17. Solrex Yang
    2008年11月3日16:28 | #17

    你确信仔细读了代码吗?我在代码上方已经用 FIXME 写出了这可能是一个 BUG。
    另外我想飞信应该是无法发送给非好友的手机号,不然这就是一个垃圾短信发送机了。

  18. Solrex Yang
    2008年11月3日19:23 | #18

    我发现一个问题,weather.com.cn 更新天气信息的速度非常慢,本来应该 18 点发布天气预报,他们先更新天气情况,再更新日期,而且是一个城市一个城市的更新,所以建议最后在 19 点半以后再发送天气消息可能才比较准确。

  19. Purplebee
    2008年11月14日08:47 | #19

    各位大大都是在自己的机子上跑这个吗?
    万一哪天晚上7:30时没开电脑,mm岂不是要生气?

  20. 2008年11月19日14:23 | #20

    g++ -Iinclude -O2 -c sendsms.cpp
    g++ sendsms.o -Ilib/libfetion_64.a -L/home/cocobear/opt/lib/ -lcurl -pthread -o sendsms
    sendsms.o(.text+0x1d1): In function `main':
    : undefined reference to `fx_init' ... (评论过长,讨论结束后部分内容被删除)
    collect2: ld returned 1 exit status
    make: *** [sendsms] Error 1

    编译出错了。我的curl安装在/home/cocobear/opt/下

  21. 2008年11月19日15:52 | #21

    没发出去吗:
    g++ sendsms.o lib/libfetion_32.a -L/home/cocobear/opt/lib/ -lcurl -pthread -o sendsms
    /usr/lib/libc_nonshared.a(elf-init.oS)(.gnu.linkonce.t.__i686.get_pc_thunk.bx+0x0): In function `__i686.get_pc_thunk.bx':
    : multiple definition of `__i686.get_pc_thunk.bx' ... (评论过长,讨论结束后部分内容被删除)
    collect2: ld returned 1 exit status
    make: *** [sendsms] Error 1
    [crystallight]$ uname -an
    Linux crystallight 2.6.24.2-grsec-p4-peon-1.1.2-grsec #1 SMP Wed Feb 20 15:30:29 PST 2008 i686 GNU/Linux

  22. 2008年11月20日09:15 | #22

    gcc version 3.3.5 (Debian 1:3.3.5-13)

    我用gcc 4.3是没问题的。

  23. rr
    2008年11月26日15:50 | #23

    g++ sendsms.o lib/libfetion_32.a -lcurl -pthread -o sendsms
    lib/libfetion_32.a(timer.o)(.text+0x576): In function `fetion_timer_init':
    : undefined reference to `__stack_chk_fail_local' ... (评论过长,讨论结束后部分内容被删除)
    collect2: ld returned 1 exit status
    make: *** [sendsms] Error 1

    我的同样报错

  24. rr
    2008年11月26日22:38 | #24

    重新安装升级GCC 问题解决

  25. dawnsong
    2009年1月15日11:29 | #25

    $ g++ sendsms.o lib.bak/libfetion_64.a -lcurl -pthread -o sendsms
    lib.bak/libfetion_64.a(dmd5.o): In function `d_MD5':
    dmd5.c:(.text+0x32): undefined reference to `MD5_Init' ... (评论过长,讨论结束后部分内容被删除)
    这样的错误如何解决呢?

  26. 2009年4月14日10:53 | #26

    好东西,收了~
    谢谢!

  27. 2009年4月17日17:26 | #27

    赞速度!

  28. 2009年4月20日20:08 | #28

    我也弄了一个,用shell下载wap.weather.com.cn的数据,用cpp作的程序发送.现在用linux服务器的crontab每日定时发送.感觉是挺有意思的.有兴趣尝试我的程序的朋友可以点这里:http://tinyurl.com/czqtnh

  29. Purplebee
    2009年4月26日21:27 | #29

    @Solrex Yang
    unix-center的ubuntu是64位的,我本机(32位)编的无法运行。但在上面编找不到curl:
    g++ -Iinclude -O2 -Wall -c sendsms.cpp
    sendsms.cpp: In function 'int main(int, char**)':
    g++ sendsms.o lib/libfetion_64.a -lcurl -pthread -o sendsms
    /usr/bin/ld: cannot find -lcurl
    collect2: ld returned 1 exit status
    make: *** [sendsms] Error 1
    后来登到上面的fedora,可以编,但编出来的程序无法登录飞信,5次尝试login失败return。fs_login只有一个bool的返回值,看不到具体的失败原因,郁闷
    不知道是不是端口有限制,我试过http和https的网页都可以访问。

  30. 2009年4月26日23:34 | #30

    @Purplebee
    呵呵,我也想过占那个便宜,也不知道为何占不了。
    您可以尝试 PyFetion,大概也许是可以的。

  31. xiaket
    2009年4月28日09:37 | #31

    根据DDD的说法,现在改过协议以后不能通过fs_send_sms_by_mobile_no函数来发送短信了...

  32. 2009年6月3日13:54 | #32

    高手啊,以后也争取在 http://www.mofun.cc 提供一个给大家用。

  33. hata19
    2009年7月14日16:05 | #33

    博主你好
    用了你的程序和脚本 编译正常 脚本中get天气部分正常 但是总是收不到天气的短信 放上脚本 麻烦博主帮忙看看

    ... (评论过长,讨论结束后部分内容被删除)

    get_html
    parse_html
    #send_forcast
    #clear_html

    只是修改地区 和号码而已 但是执行 weatherman.sh 后没有收到短信 手工操作
    sendsms -vlf 13xxxxxxxxx -p **** -t 137********,135******** < 北京.txt
    是正常的

  34. 2009年7月14日16:34 | #34

    @hata19
    #send_forcast
    #clear_html
    倒数第二句,我再调试时把 send_forcast 函数的调用注释掉了,去掉前面的注释 # 号就可以了。

  35. rabbit
    2009年9月10日08:37 | #35

    今天(Sep 10 )运行发现出了错误,是不是中国天气网站改了啊?怎么不能用了呢?

  36. rabbit
    2009年9月10日08:40 | #36

    今天(Sep 10)运行出现了错误,天气信息为空,在处理过程中,内容被清空了!
    是不是中国天气网站的网页结构做了改动啊?

  37. 2009年9月10日08:41 | #37

    @rabbit
    是,天气网改版了,等我有时间了就更新脚本。如果你懂 bash 的话,可以自己先改。

  38. rabbit
    2009年9月10日08:44 | #38

    我(thinktech2009@gmail.com)给你发了邮件,不知道你收没收到,我都搞不清你的邮箱地址,汗~
    gmail名字里面好像不让出现+号的吧

  39. 2009年9月10日08:50 | #39

    @rabbit
    不,是可以出现 + 号的,但是 + 号后面只是为了我过滤邮件方便。

  40. Jason Zhang
    2009年9月11日08:16 | #40

    现在这个版本的命令行的sendsms好像只能给 “飞信好友”发送短信息吧
    能否增加个给“手机好友”也能发送短信的功能呢?实现起来会不会太困难?

  41. rabbit
    2009年10月8日09:27 | #41

    @Purplebee
    unix center 可以用来跑这个服务吗?
    你的ubuntu定时开机,关闭计算机后中间断过电,还会定时启动吗?
    我的是不行,试验了,如果中间断过电,似乎定时开机就失效了

  42. 2009年10月8日09:37 | #42

    @rabbit
    不知道什么原因,编译可以成功,但是消息是发不出去的。
    定时开机的功能只和 CMOS 有关,如果你的 CMOS 电池没问题,断电是不影响定时开机设置的。

  43. rabbit
    2009年10月8日09:59 | #43

    @Solrex Yang
    这个,我听说别人也又遇到这种情况的,所以也搞不清楚我的问题在哪里
    比如:我定时在每天早晨7:30开机,但是由于前一天晚上11点多会断电,结果早晨就不能自己启动了
    我也做过试验,比如我在12:00设置在今天13:30开机,12:30我正常关闭计算机,如果什么都不动,
    那么可以成功;如果我在13:00(这个时候系统仍处于关闭状态)将插线板上的按钮按一下(模拟中间断电过程)
    等待一分钟再按一下(模拟开始供电),这样子在13:30就起不来了!但我的bios设置确实是可以保存的,因为再次查看的时候和我设置的一样。

    你确定这个是电池问题?这么说如果我换一块电池就可以解决?

  44. 2009年10月8日10:33 | #44

    @rabbit
    事实上我不了解为什么你会出现这个问题,照理说不应该这样的。定时开关机只与主板有关,与系统软件什么的没什么关系。如果你的主板确实不能这样做,那就没办法了。

  45. 2009年10月9日16:04 | #45

    sendsms.cpp文件中strdup函数返回的字符串所申请的空间需要自己手动释放!否则会内存泄漏!

  46. 2009年11月12日21:38 | #46

    其实除了利用 Google 的服务外,还可以利用 139.com 邮箱的邮件到达短信提醒的功能
    实现免费的天气短信,我们只要找到一个接口,能定时向指定 139.com 邮箱发送天气邮件就可以了,
    这里就有一个自己开发的接口,有兴趣的朋友可以试试:http://api.liqwei.com/weather/subscription.asp
    只要输入 139.com 邮箱地址,选择订阅的城市就可以,而且支持订阅多个城市的天气,
    自我感觉用起来还行,当然别的邮箱也可以,就是无法收到天气短信,哈哈

    @Iron_Feet

  47. 2009年11月12日22:08 | #47

    @liqwei
    自从GPRS流量降价过后,我就用手机直接GPRS查天气预报了-_-!

  48. 2009年12月8日19:28 | #48

    @khb_gl
    谢谢您,只是目前小程序没有什么大问题。下次有大动作的时候,我会将这个bug补上。

  49. hata19
    2009年12月8日19:37 | #49

    to:Solrex Yang
    ms天气网有局部地区改版,能否更新bash,同时时候可以考虑更换一下“天气源”

  50. 2009年12月8日19:50 | #50

    @hata19
    我已经更新过了,你可以到脚本的地址去下载。我也想换一个更人性化适合手机消息的源,奈何没有找到合适的。

  51. 2009年12月9日22:03 | #51

    @Solrex Yang
    其实,飞信的段错误问题一直没有解决.在群发短信的时候经常出现断错误,所以我现在基本放弃使用了.希望有一天段错误这个问题能解决.

  52. 2009年12月10日08:27 | #52

    @khb_gl
    这个我也是无能为力,因为段错误是发生在libfetion的库里,但是ddd不愿意公开源代码,所以想调试都不好调试。而且那个错误不是每次都出现,也增加了找bug的难度。我平时使用来讲,还好吧,虽然不是很稳定,但是总得来说成功率还是很高的。只不过这两天飞信又升级了服务器,看来又得等libfetion升级了。

  53. hata19
    2010年1月2日21:16 | #53

    博主 新浪网推出了天气预报 看过源码 和天气网的很相似

  54. External
    2010年2月26日22:54 | #54

    用包里编译好的那个 sendsms 只能给自己发,给飞信好友就发不出去
    自己编译又出错,郁闷
    $ g++ sendsms.o lib/libfetion_32.a -lcurl -pthread -o sendsms
    collect2: ld 以信号 11 [段错误] 退出。
    /usr/bin/ld: i386:x86-64 architecture of input file `sendsms.o' is incompatible with i386 output

    还有那个验证码,真TMD烦人,看了几十次也写不出来,真想骂人!

  55. Chen Yufei
    2008年11月13日14:51 | #55

    @Solrex Yang

    我也发现了,晚上 8 点的时候发一般都没有问题。

    所以我现在是用 lxml 解析它的网页,我发邮件跟你联系吧。

  56. Solrex Yang
    2008年11月13日15:22 | #56

    @Chen Yufei
    好,把你的代码发给我吧。其实我当初也想到用正则表达式或者 xml 解析会干净一些,但是考虑到将网页作为一个那么大的 string 来处理倒还不如用 Linux 下的行处理工具来得方便,也就没有去想用别的方法。

  57. Solrex Yang
    2008年11月14日09:03 | #57

    @Purplebee
    我的 Ubuntu 一天开到晚的,有定时开机和定时关机。
    要不试试 Unix-Center 提供的免费账户能不能跑这个程序也行。
    只是用脚本保存用户名密码的话,要面临泄漏私人信息的危险,那样就最好把用户名、密码写入到程序里了。

  58. Purplebee
    2008年11月16日15:51 | #58

    @Solrex Yang
    一天开到晚很不环保哦
    我以前在想以后买了房搞个家庭服务器的,智能控制/娱乐/文件/……,想来想去,最大的困难是能耗,7*24开每个月电费不少了

    Unix-Center不错,改天试试

  59. Solrex Yang
    2008年11月19日14:49 | #59

    @cocobear
    不是 curl 的原因,你的编译参数有问题,-I 是指定头文件 include 所在的目录,不能用来连接静态库
    你不需要使用 -I,直接这样写即可:
    g++ sendsms.o lib/libfetion_64.a -L/home/cocobear/opt/lib/ -lcurl -pthread -o sendsms

  60. Solrex Yang
    2008年11月19日16:36 | #60

    @cocobear
    我没有遇到过这种错误,网上的搜索发现说这个错误产生的原因是因为 gcc 的 bug
    有人说 gcc 3.4 可以解决这个问题,有人说需要升级到 4.0 以上
    你的 gcc 是什么版本的?

  61. Solrex Yang
    2009年1月15日12:48 | #61

    @dawnsong
    您 google 一下部分错误信息应该就能找到答案,我觉得有可能是 libfetion 的作者没有把 ssl 静态连接进 64 位的 .a 文件中,您使用一个 -lssl 自己连接一下 ssl 库试试(当然,前提是你安装了 ssl 的开发库,比如 libssl-dev)。

  1. 2009年12月14日09:38 | #1
说明:点击回复/引用, 会发邮件给该用户, 请慎用; 填写非真实电邮地址, 评论可能会被自动过滤, 无法及时显示, 不要责怪我. 卡内基梅隆大学的 reCAPTCHA 计划使用验证码帮助辨认古老典籍扫描时无法识别的文字,输入验证码的同时,您也为保存人类知识做了一分贡献,谢谢!