python 学习笔记 12 -- 写一个脚本获取城市天气信息

最近在玩树莓派,前面写过一篇在树莓派上使用1602液晶显示屏,那么能够显示后最重要的就是显示什么的问题了。最容易想到的就是显示时间啊,CPU利用率啊,IP地址之类的。那么我觉得呢,如果能够显示当前时间、温度也是甚好的,作为一个桌面小时钟还是很精致的。

1. 目前有哪些工具

目前比较好用的应该是 weather-util, 之前我获取天气信息一般都是通过它。

使用起来也很简单:

(1) Debian/Ubuntu 用户使用
sudo apt-get install weather-util 安装

(2) 使用可以直接终端下输入“ weather 城市名拼音”  查询,如

所以我们只要对查询语句进行一点点改进即可得到:

$ weather lanzhou

Searching via name...

[using result Lanzhou / Zhongchuan, China]

Current conditions at Lanzhou / Zhongchuan, China (ZLLL) 36-01-12N 103-45E

Last updated Jul 05, 2014 - 03:00 AM EDT / 2014.07.05 0700 UTC

Temperature: 86 F (30 C)

Relative Humidity: 28%

Wind: Variable at 4 MPH (4 KT)

Sky conditions: partly cloudy

所以,如果我在树莓派的程序/脚本中直接调用,就可很轻松的得到我想要的天气信息。

$ weather "lanzhou" | grep "Temperature" | cut -d "(" -f2 | cut -d ")" -f1

30 C

但是,做很多事都怕都这么一个但是啊!!!在树莓派上调用weather 查询天气耗时太可怕了:

$ time weather "lanzhou" | grep "Temperature" | cut -d "(" -f2 | cut -d ")" -f1

25 C

real  4m30.381s

user  4m27.010s

sys   0m1.560s

而在我的台式机端,耗时虽然比较长,但是也消耗了15s 之久。

$ time weather "lanzhou" | grep "Temperature" | cut -d "(" -f2 | cut -d ")" -f1

30 C

real  0m15.609s

user  0m15.405s

sys   0m0.203s

那么为什么使用weather 查询个天气需要耗时这么久?根据参考资料[1] 中所述,“Weather”工具从METAR(Meteorological Terminal Aviation Routine Weather Report,
航空例行天气报告), NOAA (the USA National Oceanic and Atmospheric Administration,
美国国家海洋和大气管理局)和NWS (the USA National Weather Service,
美国国家气象服务)检索获取天气状况和预报信息。这使得这个工具主要以美国为中心,然而你也能获取到全球有国际机场的地区的天气信息。

原来这个工具是针对美国国土以及一些国际机场,既然我们不在美国国土,那么我们查一下国际机场的信息试试时间会不会快上很多:

$ time weather egll

Searching via station...

[caching result London / Heathrow Airport, United Kingdom]

Current conditions at London / Heathrow Airport, United Kingdom (EGLL) 51-29N 000-27W 0M

Last updated Jul 05, 2014 - 03:20 AM EDT / 2014.07.05 0720 UTC

Temperature: 62 F (17 C)

..

real    0m3.958s

user    0m2.890s

sys    0m0.072s

我们可以看到,速度确实要快上不少。(而且由于此工具的优化,第二次查询时,耗时可达 0.08s 级别!)看来我们使用现成的 weather-util 工具查询天气信息算是泡汤了。

我们需要更快的速度、更全面的区域,所以我觉得我们需要本土的天气查询。

2. 使用什么接口

上文讨论到,我们需要使用本土的天气查询,百度上找到了一个帖子(见参考资料[2]),上面给出了中国天气网(www.weather.com.cn)提供的查询结构,并且下面还给出了城市编码。在此感谢一下作者。

那么我们在此查看一下这些接口会给我们哪些新息:

接口1 : http://www.weather.com.cn/data/cityinfo/***.html  
(“***” 处替换为你想要查询的城市的编码,下同)

这个网址会给我们如下信息(我感觉有用的信息已用颜色标出):

{"weatherinfo":{"city":"兰州","cityid":"101160101","temp1":"19℃","temp2":"33℃","weather":"晴","img1":"n0.gif","img2":"d0.gif","ptime":"18:00"}}

所以我们可以通过此接口得到城市的气候("weather"),低温("temp1"),高温("temp2")。

接口2 : http://www.weather.com.cn/data/sk/***.html

这个接口会给我们如下信息:

{"weatherinfo":{"city":"兰州","cityid":"101160101","temp":"26","WD":"北风","WS":"1 级","SD":"36%","WSE":"1","time":"21:00","isRadar":"1","Radar":"JC_RADAR_AZ9931_JB"}}

所以通过此结构我们可以得到的是一个城市的实时温度("temp")。

接口3:http://m.weather.com.cn/data/***.html

感觉这个接口给的信息比较杂乱:

{"weatherinfo":{"city":"兰州","city_en":"lanzhou","date_y":"2014年3月4日","date":"","week":"星期二","fchh":"11","cityid":"101160101","temp1":"11℃~0℃","temp2":"12℃~0℃","temp3":"11℃~2℃","temp4":"13℃~2℃","temp5":"15℃~2℃","temp6":"18℃~4℃","tempF1":"51.8℉~32℉","tempF2":"53.6℉~32℉","tempF3":"51.8℉~35.6℉","tempF4":"55.4℉~35.6℉","tempF5":"59℉~35.6℉","tempF6":"64.4℉~39.2℉","weather1":"晴","weather2":"多云","weather3":"多云转小雨","weather4":"小雨转多云","weather5":"多云转晴","weather6":"晴","img1":"0","img2":"99","img3":"1","img4":"99","img5":"1","img6":"7","img7":"7","img8":"1","img9":"1","img10":"0","img11":"0","img12":"99","img_single":"0","img_title1":"晴","img_title2":"晴","img_title3":"多云","img_title4":"多云","img_title5":"多云","img_title6":"小雨","img_title7":"小雨","img_title8":"多云","img_title9":"多云","img_title10":"晴","img_title11":"晴","img_title12":"晴","img_title_single":"晴","wind1":"微风","wind2":"微风","wind3":"微风","wind4":"微风","wind5":"微风","wind6":"微风","fx1":"微风","fx2":"微风","fl1":"小于3级","fl2":"小于3级","fl3":"小于3级","fl4":"小于3级","fl5":"小于3级","fl6":"小于3级","index":"冷","index_d":"天气冷,建议着棉服、羽绒服、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣、冬大衣或厚羽绒服。","index48":"冷","index48_d":"天气冷,建议着棉服、羽绒服、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣、冬大衣或厚羽绒服。","index_uv":"中等","index48_uv":"弱","index_xc":"较适宜","index_tr":"适宜","index_co":"较舒适","st1":"10","st2":"1","st3":"12","st4":"0","st5":"11","st6":"2","index_cl":"适宜","index_ls":"基本适宜","index_ag":"极易发"}}

3. 实现

有了这些接口信息,外加城市代码文件,我们可以很轻松滤清所要做的顺序:
根据用户输入的城市在代码文件中查找城市代码,使用城市代码替换结构中的代码,使用python的urllib2 获取此结构的内容,然后解析并输出。

3.1 搜索城市代码

城市代码文件中的内容格式为:

101010100=北京

101010200=海淀

101010300=朝阳

而整个文件有2000多行,如果逐行读取并匹配,个人觉得效率还是不高,所以我使用 os.popen 调用shell 中的grep 来进行全文匹配查找。

def get_city_code(test_city, citycode_f = "citycode"):
    if os.path.exists(citycode_f):      # 先检查城市代码文件是否存在
        ret = os.system("grep %s %s >/dev/null" % (test_city,citycode_f))
        if ret == 0:        # 检测os.system 的返回值从而得知城市名是否在代码文件中存在
            grep_content = os.popen("grep %s %s" % (test_city,citycode_f)).read()
            city_code = grep_content.split("=")[0]  # 如果存在获取匹配行,如"101010100=北京",使用‘=’作为分隔符获取第一列
            return city_code
        else:
            print "\t搜索\"%s\"失败,请重新搜索(市级城市)" % test_city
            sys.exit(-1)
    else:
        print "当前目录没有\"%s\"文件,无法搜索" % citycode_f
        sys.exit(-1)

3.2 替换结构代码获取内容

通过调用上述函数得到城市代码,使用如下的简单字符串方法即可修改城市url :

weather_url = "http://www.weather.com.cn/data/sk/%s.html" % city_code

info_url = "http://www.weather.com.cn/data/cityinfo/%s.html" % city_code

所以我们此时只需要使用urllib2 模块获取url 的内容,并对内容进行解析。

获取url 内容,这里使用了一个简单的函数:

def use_urllib2(url):
    try:
        f = urllib2.urlopen(url, timeout=5).read()
    except urllib2.URLError, e:
        print e.reason
        return -1
    else:
        return f

所以使用此函数打开上面的url ,打开后分析返回值如果是 -1 则打开出错,给出错误信息并调用 sys.exit 退出程序。如果正常打开,对内容进行解析获取关心的内容即可:

    weather_url = "http://www.weather.com.cn/data/sk/%s.html" % city_code
    wea_str = use_urllib2(weather_url)
    if wea_str == -1:
        print "Sorry that I can't find the weather info of this city now, please check or retry"
        sys.exit(-1)
    else:
        wea_list = wea_str.split(",")
        for item in wea_list:
            if "city" in item and "cityid" not in item:
                city_name = get_content(item)
            if "temp" in item:
                city_temp = get_content(item)
            if "time" in item:
                update_time = get_content(item)

3.3 中文输入or英文输入

对于城市的代码文件,我们可以看出:如果想要匹配,我们只能使用中文城市名。那么如果我想用城市名的拼音怎么办?所以只能从根本处借决问题。那么怎么解决?一个个手动打进去?2000多行哦! 这时候我想到了上面的接口3 中有这么一条有用的信息:"city":"兰州","city_en":"lanzhou"

貌似一切都要变得简单。我将原来原来拷贝过来的 citycode 改名为citycode1。并写了一个脚本:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import sys
import os
def file_append(filename,string):
    try:
        f = open(filename,"a")
        try:
            f.write(string+"\n")
        except IOError:
            print("Append %s occurs some errors" % filename)
            return 0
        except:
            print("Append %s occurs some errors" % filename)
            return 0
        else:
            return 1
    except IOError:
        print("open %s file error" % filename)
        return 0
        traceback.print_exc()
    finally:
        f.close()

def use_urllib2(url):
    try:
        f = urllib2.urlopen(url, timeout=5).read()
    except urllib2.URLError, e:
        print e.reason
        return -1
    else:
        return f

def deal_with_file(sour_file, tar_file):
    try:
        open_file = file(sour_file)
        file_len = len(open(sour_file,'rU').readlines())    # 获取文件长度

        for cur_line in range(0, file_len):
            content = open_file.readline().strip("\n")
            print "No.%d\tNow dealing with %s" %(cur_line, content)
            if len(content) == 0:
                file_append(tar_file, content)
            else:
                city_code = content.split("=")[0]
                get_city_content_url = "http://m.weather.com.cn/data/%s.html" % city_code
                web_content = use_urllib2(get_city_content_url)
                if web_content == -1:
                    print "%s has no the web content, pass it " % content
                else:
                    web_list = web_content.split(",")
                    for item in web_list:
                        if "city_en" in item:
                            dealed_content = content+"="+item.split(":")[1].replace("\"", "")
                            file_append(tar_file, dealed_content)

    except IOError:
        print("open %s file error" % sour_file)
        sys.exit(-1)
    finally:
        print("we will close file %s" % sour_file)
        open_file.close()

#### Real start here
file_name = "citycode1"        # 原始文件,格式为"101010100=北京"
result_f = "citycode"            # 最终将改完的格式为"101010100=北京=beijing"内容存入此文件
if os.path.exists(result_f):
    os.remove(result_f)
deal_with_file(file_name, result_f)

此时我们打开城市代码文件,里面的内容都是这个格式的

101010100=北京=beijing

101010200=海淀=haidian

101010300=朝阳=chaoyang

注: 修改完的的城市代码信息,本来我上传csdn下载,但是苦于被删除,所以大家可以克隆此仓库

3.4 中文输出or英文输出

那么这个程序打印到底应该是中文还是英文呢?我采取了本系列前文《python 学习笔记 11 -- 使用参数使你的程序变得更性感》中的方法,添加参数选择"-zh"/"-en"。如果使用的是 "./getweather.py  -zh **" 则以中文输出(不填加选项,直接使用 "./getweather.py ***" 查询采取的默认输出也是中文,不过可通过修改代码中的初始值设置进行修改),如果使用"./getweather.py  -en **" 则以英文输出。

使用的方法很简单:设置一个全局变量叫 "chinese_flag" 并设初始值为1。获取用户参数时,如果有 "-zh" 则不变,如果有"-en" 则改为0。任何printf 打印时对此变量进行判断后使用不同的输出。

4. 总结

本文中虽然总共写了不到300 行的python 代码,但是最终效果以及健壮性还是很不错的:

$ ./getweather.py 兰州

你只使用一个参数"兰州"(且不是"-h"),将其作为城市名搜索

timed out

对不起,暂时搜寻不到此城市信息,请检查或稍后再试

$ ./getweather.py 兰州

你只使用一个参数"兰州"(且不是"-h"),将其作为城市名搜索

今天"兰州市"天气是:

========================

今日天气 : 晴

实时温度 : 32℃

今日的温度区间 : 19℃ ~ 33℃

更新时间: 16:10

========================

更多请参见:http://www.weather.com.cn/weather/101160101.shtml

$ ./getweather.py lanzhou

你只使用一个参数"lanzhou"(且不是"-h"),将其作为城市名搜索

今天"兰州市"天气是:

========================

今日天气 : 晴

实时温度 : 32℃

今日的温度区间 : 19℃ ~ 33℃

更新时间: 16:10

========================

更多请参见:http://www.weather.com.cn/weather/101160101.shtml

$ ./getweather.py -en lanzhou

The weather info of "兰州":

========================

The weather today is : 晴

Currenttly the temperature is : 32℃

The weather section today is : 19℃ ~ 33℃

Update time: 16:10

========================

See more: http://en.weather.com.cn/weather/101160101.shtml

$ ./getweather.py -en lanzhou1

Search city "lanzhou1" fail, please check the city and retry(at lease city size)

$ ./getweather.py -zg lanzhou1

你选择了错误的结果选项,请重新输入或者使用‘-h‘查看如何使用

$ ./getweather.py -zh lanzhou1

搜索"lanzhou1"失败,请重新搜索(市级城市)

关于本文代码,我已经提交在前文《Shell 脚本小试牛刀(番外) -- 捷报》中提到的github 仓库中。且git 仓库已更名: cool_script 。

可直接克隆仓库: git clone https://github.com/longerzone/easy_script  .然后进入 get_weather 目录查看。

================

参考资料:

[1] 关于weather-util :  http://www.iraspberrypi.com/archives/539

[2] 关于中国天气网的结构: http://www.iteye.com/topic/1106241

================================================

注: 转载注明出处: http://blog.csdn.net/longerzone

python 学习笔记 12 -- 写一个脚本获取城市天气信息

时间: 2024-10-24 13:14:10

python 学习笔记 12 -- 写一个脚本获取城市天气信息的相关文章

DuiLib学习笔记2——写一个简单的程序

我们要独立出来自己创建一个项目,在我们自己的项目上加皮肤这才是初衷.我的新建项目名为:duilibTest 在duilib根目录下面有个 Duilib入门文档.doc 我们就按这个教程开始入门 首先新建一个win32项目 去DuiLib根目录,把目录下DuiLib文件夹拷贝到新建项目的根目录.再把这个项目添加进我们解决方案中. 从教程里面把以下代码粘贴到我们项目的stdafx.h中 // Duilib使用设置部分 #pragma once #define WIN32_LEAN_AND_MEAN

[原创]java WEB学习笔记12:一个简单的serlet连接数据库实验

本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 ---------------------------------

MiZ702学习笔记12——封装一个普通的VGA IP

还记得<MiZ702学习笔记(番外篇)--纯PL VGA驱动>这篇文章中,用verilog写了一个VGA驱动.我们今天要介绍的就是将这个工程打包成一个普通的IP,目的是为后面的一篇文章做个铺垫. 打包成一个普通的IP的目的,可以直接将这个IP粘贴到Block文件中.(和用文本实例化是一个意思).应为我们调用zynq的核的时候一般是用Block的形式,为了zynq和我们的VGA模块更方便的组织起来,就需要这种IP打包方式. 为什么是强调是普通的IP,这个主意是区分带AXI接口的IP,这个在后面介

Python学习---Django的request扩展【获取用户设备信息】

关于Django的request扩展[获取用户设备信息] settings.py INSTALLED_APPS = [ ... 'app01', # 注册app ] STATICFILES_DIRS = (os.path.join(BASE_DIR, "statics"),) # 现添加的配置,这里是元组,注意逗号 TEMPLATES = [ ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], ] urls.py from django

《python灰帽子》学习笔记:写一个windos 调试器(一)

一.开发内容介绍 为了对一个进程进行调试,你首先必须用一些方法把调试器和进程连接起来.所以, 我们的调试器要不然就是装载一个可执行程序然后运行它, 要不然就是动态的附加到一个运行的进程.Windows 的调试接口(Windows debugging API)提供了一个非常简单的方法完成这两点. 运行一个程序和附加到一个程序有细微的差别. 打开一个程序的优点在于他能在程序运行任何代码之前完全的控制程序. 这在分析病毒或者恶意代码的时候非常有用. 附加到一个进程,仅仅是强行的进入一个已经运行了的进程

Python学习笔记12(面向对象进阶)

银角大王这一篇知识点整理的特别的清晰明了,直接搬运了.给你们一个传送门地址http://www.cnblogs.com/wupeiqi/p/4766801.html 本篇将详细介绍Python 类的成员.成员修饰符.类的特殊成员. 一.类的成员 类的成员可以分为三大类:字段.方法和属性 注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段.而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份. 一.字段 字段包括:普通字段和静态字

python学习笔记12 ----线程、进程

进程和线程的概念 进程和线程是操作系统中两个很重要的概念,对于一般的程序,可能有若干个进程,每一个进程有若干个同时执行的线程.进程是资源管理的最小单位,线程是程序执行的最小单位(线程可共享同一进程里的所有资源,进程之间则是独立的). 进程(progress) 直观的说,进程就是正在执行的程序(python中每一个.py文件都可以看作是一个进程),是多任务操作系统中的基本单元,是包含了程序指令和相关资源的集合. 操作系统隔绝了每个进程可以访问的地址空间,如果进程间想要传递信息,可使用进程间通信或者

python 学习笔记12(序列一些方法总结)

1.  字符串判断 str.isalnum()        返回:True, 都是字母或数字 str.isalpha()        返回:True,  都是字母 str.isdigit()        返回:True,   都是数字 str.istitle()        返回:True,    首字母都是大写 str.isspace()        返回:True,  都是空格 str.islower()        返回:True,   都是小写字母 str.isupper()

Python学习笔记12:标准库之对象序列化(pickle包,cPickle包)

计算机的内存中存储的是二进制的序列. 我们能够直接将某个对象所相应位置的数据抓取下来,转换成文本流 (这个过程叫做serialize),然后将文本流存入到文件里. 因为Python在创建对象时,要參考对象的类定义,所以当我们从文本中读取对象时,必须在手边要有该对象的类定义,才干懂得怎样去重建这一对象. 从文件读取时,对于Python的内建(built-in)对象 (比方说整数.词典.表等等),因为其类定义已经加载内存,所以不须要我们再在程序中定义类. 但对于用户自行定义的对象,就必需要先定义类,