第5章  调用百度API获取数据——小小翻译器



视频讲解



5.1小小翻译器功能介绍





图51小小翻译器运行界面

许多网站提供了API,通过它可获取网站提供的信息,如天气预报、股票交易信息及火车售票信息等。小小翻译器使用百度翻译开放平台提供的API,能够实现简单的翻译功能,用户输入自己需要翻译的单词或者句子,即可得到翻译的结果,运行界面如图51所示。该翻译器不仅能够将英文翻译成中文,也可以将中文翻译成英文或者其他语言。

5.2程序设计的思路


百度翻译开放平台提供了API,可以提供高质量的翻译服务。通过调用百度翻译API可以编写在线翻译程序。


百度翻译开放平台每月提供200万字符的免费翻译服务,只要拥有百度账号并申请成为开发者就可以获得所需要的账号和密码。下面是开发者的申请链接: 



http://api.fanyi.baidu.com/api/trans/product/index




为方便使用,百度翻译开放平台提供了详细的接入文档,链接如下: 



http://api.fanyi.baidu.com/api/trans/product/apidoc




在文档中列出了详细的使用方法。
按照百度翻译开放平台文档中的要求,生成URL请求网页,提交后可返回JSON数据格式的翻译结果,再将得到的JSON格式的翻译结果解析出来。













5.3关键技术


5.3.1urllib库的高级使用
前面章节使用urllib.request.urlopen()打开和读取URL信息,返回的对象response如同一个文本对象,可以调用read()进行读取,再通过print()将读到的信息打印出来。下面学习urllib库的其他一些功能。

1. 获取服务器响应信息
与浏览器的交互过程一样,request.urlopen()代表请求过程,它返回的HTTPResponse对象代表响应。返回内容作为一个对象更便于操作,HTTPResponse对象status属性返回请求HTTP后的状态,在处理数据之前要先判断状态情况。如果请求未被响应,需要终止内容处理。reason属性非常重要,可以得到未被响应的原因,url属性是返回页面URL。HTTPResponse.read()是获取请求的页面内容的二进制形式。
也可以使用getheaders()返回HTTP响应的头信息,例如: 



from urllib import request

f=request.urlopen(' http://fanyi.baidu.com ') 

data = f.read()

print('Status:', f.status, f.reason)

for k, v in f.getheaders():

print('%s: %s' % (k, v))




可以看到HTTP响应的头信息。



Status: 200 OK

Content-Type: text/html

Date: Sat, 15 Jul 2017 02:18:26 GMT

P3p: CP=" OTI DSP COR IVA OUR IND COM "

Server: Apache

Set-Cookie: locale=zh; expires=Fri, 11-May-2018 02:18:26 GMT; path=/; domain=.baidu.com

Set-Cookie: BAIDUID=2335F4F896262887F5B2BCEAD460F5E9:FG=1; expires=Sun, 15-Jul-18 02:18:26 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1

Vary: Accept-Encoding

Connection: close

Transfer-Encoding: chunked




同样也可以使用response对象的geturl()方法、info()方法、getcode()方法获取相关的URL、响应信息和响应HTTP状态码。



#-*- coding: UTF-8 -*-

from urllib import request

if __name__ == "__main__":

req = request.Request("http://fanyi.baidu.com/")

response = request.urlopen(req)

print("geturl打印信息: %s"%(response.geturl()))

print('**********************************************')

print("info打印信息: %s"%(response.info()))

print('**********************************************')

print("getcode打印信息: %s"%(response.getcode()))




可以得到如下运行结果: 



geturl打印信息: http://fanyi.baidu.com/

**********************************************

info打印信息: Content-Type: text/html

Date: Sat, 15 Jul 2017 02:42:32 GMT

P3p: CP=" OTI DSP COR IVA OUR IND COM "

Server: Apache

Set-Cookie: locale=zh; expires=Fri, 11-May-2018 02:42:32 GMT; path=/; domain=.baidu.com

Set-Cookie: BAIDUID=976A41D6B0C3FD6CA816A09BEAC3A89A:FG=1; expires=Sun, 15-Jul-18 02:42:32 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1

Vary: Accept-Encoding

Connection: close

Transfer-Encoding: chunked

**********************************************

getcode打印信息: 200




学会了使用简单的语句对网页进行抓取之后,接下来学习如何向服务器发送数据。
2. 向服务器发送数据
可以使用urlopen()函数中的data参数向服务器发送数据。根据HTTP规范,GET用于信息获取,POST是向服务器提交数据的一种请求。
从客户端向服务器提交数据使用POST; 从服务器获得数据到客户端使用GET。然而,GET也可以提交,与POST的区别如下。
(1) GET方式可以通过URL提交数据,待提交数据是URL的一部分; 采用POST方式,待提交数据放置在HTML HEADER内。
(2) GET方式提交的数据最多不超过1024B,POST没有对提交内容的长度限制。
下面通过具体的GET方式和POST方式提交例子来说明。
(1) 以GET方式提交email和password信息。



LOGIN_URL= "http://www.kiwisns.com/postLogin/"

values={'email':'xmj@user.com','password':'123456'}

data=urllib.parse.urlencode(values).encode()

geturl = LOGIN_URL+ "?"+data#直接以URL链接提交数据,链接中包含了所有的参数 

req = urllib.request.Request(geturl)

response = request.urlopen(req)#传入创建好的Request对象,用GET方式提交




(2) 以POST方式提交email和password信息。



LOGIN_URL='http:// www.kiwisns.com/postLogin/'

values={'email':' xmj@user.com','password':'123456'}

data=urllib.parse.urlencode(values).encode()

req=urllib.request.Request(URL,data) #传送的数据就是这个参数data

response = request.urlopen(req,data)         #传入创建好的Request对象,用POST方式提交




如果没有设置urlopen()函数的data参数,HTTP请求采用GET方式,也就是从服务器获取信息。如果设置data参数,HTTP请求采用POST方式,也就是向服务器传递数据。
data参数有自己的格式,它是一个基于application/xwww.formurlencoded的格式,具体格式不用了解,可以使用urllib.parse.urlencode()函数将字符串自动转换成上面所说的格式。
5.3.2使用User Agent隐藏身份 
1. 为何要设置User Agent 

有一些网站不喜欢被爬虫程序访问,所以会检测连接对象,如果是爬虫程序,也就是非人点击访问,它就会阻止继续访问。所以,为了让程序正常运行,需要隐藏自己的爬虫程序的身份。此时,就可以通过设置User Agent来达到隐藏身份的目的。User Agent的中文名为用户代理,简称UA。
User Agent存放于Headers中,服务器就是通过查看Headers中的User Agent来判断是谁在访问。在Python中,如果不设置User Agent,程序将使用默认的参数,那么这个User Agent就会有Python的字样,如果服务器检查User Agent,那么没有设置User Agent的Python程序将无法正常访问网站。
Python允许修改这个User Agent来模拟浏览器访问,它的强大毋庸置疑。
2. 常见的User Agent
1) Android



 Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19

 Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

 Mozilla/5.0 (Linux; U; Android 2.2; en-gb; GT-P1000 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1




2) Firefox



 Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0

 Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0




3) Google Chrome



 Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36

 Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19




4) iOS



 Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3

 Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3




上面列举了Andriod、Firefox、Google Chrome、iOS的一些User Agent。
3. 设置User Agent的方法
设置User Agent有两种方法。
(1) 在创建Request对象时,填入headers参数(包含User Agent信息),这个headers参数要求为字典。
(2) 在创建Request对象时不添加headers参数,在创建完成之后,使用add_header()的方法,添加headers。
方法一: 
使用上面提到的Android的第一个User Agent,在创建Request对象时传入headers参数,编写代码如下: 



#-*- coding: UTF-8 -*-

from urllib import request

if __name__ == "__main__":

#以CSDN为例,CSDN不更改User Agent是无法访问的

url = 'http://www.csdn.net/'

head = {}

#写入User Agent信息

head['User-Agent'] = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19'

req = request.Request(url, headers=head)#创建Request对象

response = request.urlopen(req)         #传入创建好的Request对象

html = response.read().decode('utf-8')    #读取响应信息并解码

print(html)    #打印信息




方法二: 
使用上面提到的Android的第一个User Agent,在创建Request对象时不传入headers参数,创建之后使用add_header()方法,添加headers,编写代码如下: 



#-*- coding: UTF-8 -*-

from urllib import request






if __name__ == "__main__":

#以CSDN为例,CSDN不更改User Agent是无法访问的

url = 'http://www.csdn.net/'

req = request.Request(url)#创建Request对象

req.add_header('User-Agent', 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19')    #传入headers

response = request.urlopen(req)    #传入创建好的Request对象

html = response.read().decode('utf-8')    #读取响应信息并解码

print(html)    #打印信息




5.3.3JSON使用
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,比XML更小、更快、更易解析,易于读写且占用带宽小,网络传输速度快,适用于数据量大且不要求保留原有类型的情况。它是JavaScript的子集,易于阅读和编写。
前端和后端进行数据交互,就是通过JSON进行的。因为JSON易于被识别的特性,常被作为网络请求的返回数据格式。在爬取动态网页时,经常会遇到JSON格式的数据,Python中可以使用JSON模块来对JSON数据进行解析。
1. JSON的结构
JSON常见形式为“名称/值”对的集合。
例如下面这样: 



{"firstName": "Brett", "lastName": "McLaughlin"}




JSON允许使用数组,采用方括号[]实现。
例如: 



{

"people":[

{"firstName": "Brett",

"lastName":"McLaughlin"

},

{"firstName":"Jason",

"lastName":"Hunter"

}

]

}




在这个示例中,只有一个名为people的名称,值是包含两个元素的数组,每个元素是一个人的信息,其中包含名和姓。
XML和JSON都使用结构化方法来标记数据,下面做一个简单的比较。
用XML表示中国部分省市数据如下: 



<?xml version="1.0" encoding="utf-8"?>

<country>

<name>中国</name>

<province>

<name>黑龙江</name>

<cities>

<city>哈尔滨</city>

<city>大庆</city>

</cities>

</province>

<province>

<name>广东</name>

<cities>

<city>广州</city>

<city>深圳</city>

<city>珠海</city>

</cities>

</province>

<province>

<name>新疆</name>

<cities>

<city>乌鲁木齐</city>

</cities>

</province>

</country>




用JSON表示如下,其中省份采用的是数组。



{

"name": "中国",

"province": [{

"name": "黑龙江",

"cities": {

"city": ["哈尔滨", "大庆"]

}

}, {

"name": "广东",

"cities": {

"city": ["广州", "深圳", "珠海"]

}

}, {

"name": "新疆",

"cities": {

"city": ["乌鲁木齐"]

}

}]

}




由上例可以看出,JSON简单的语法格式和清晰的层次结构明显比XML容易阅读,并且在数据交换方面,因为JSON所使用的字符要比XML少得多。所以,可以节约传输数据所占用的带宽。
JSON中的数据类型和Python中的数据类型转化关系如表51所示。



表51JSON中的数据类型和Python中的数据类型转化关系



JSON的数据类型Python的数据类型JSON的数据类型Python的数据类型

objectdictnumber (real)float
arraylisttrue、falseTrue、False
stringstrnullNone
number (int)int

2. JSON模块中常用的方法
在使用JSON这个模块前,首先要导入JSON库: import json。
JSON模块主要提供了4个方法: dumps、loads、dump和load,如表52所示。



表52JSON模块中常用的方法



方法功 能 描 述

json.dumps()将 Python 对象转换成JSON字符串
json.loads()将JSON字符串转换成Python对象
json.dump()将Python类型数据序列化为JSON对象后写入文件
json.load()读取文件中JSON形式的字符串并转化为Python类型数据

下面通过例子说明这个方法的使用。
1) json.dumps()
json.dumps()的作用是将Python对象转换成JSON字符串。



import json

data = {'name':'nanbei','age':18}

s= json.dumps(data)#将Python对象编码成json字符串

print(s) 




运行结果如下: 



{"name": "nanbei", "age": 18}




JSON注意事项: 
 名称必须用双引号(即"name")来包括;
 值可以是字符串、数字、true、false、null、JavaScript数组或子对象。
从运行结果可见,原先的'name''age'单引号已经变成双引号"name" "age"。
2) json.loads()
json.loads()的作用是将JSON字符串转换成Python对象。



import json 

data = {'name':'nanbei','age':18}


a = json.dumps(data) 

print(json.loads(a))#将json字符串编码成Python对象—dict字典




运行结果如下: 



{'name': 'nanbei', 'age': 18}




对JSON文件要先读文件,然后才能转换成Python对象。



f=open('stus.json',encoding='utf-8')#'stus.json'是一个JSON文件

content=f.read()							#使用loads()方法,需要先读文件成字符串

user_dic=json.loads(content)					#转换成Python的字典对象

print(user_dic)




3) json.load()
json.load()的作用是读取文件中JSON形式的字符串并转化为Python类型数据。



import json

f=open('stus.json',encoding='utf-8')

user_dic=json.load(f)#f是文件对象

print(user_dic)




可见loads()传入的是字符串,而load()传入的是文件对象。使用loads()时需要先读文件成字符串再使用,而load()则不用先读文件成字符串,而是直接传入文件对象。
4) json.dump()
json.dump()的作用是将Python类型数据序列化为JSON对象后写入文件。



stus={ 'xiaojun':88,  'xiaohei':90,  'lrx':100}

f=open('stus2.json','w',encoding='utf-8')#以写方式打开stus2.json文件

json.dump(stus,f)#写入stus2.json文件

f.close()#文件关闭





5.4程序设计的步骤


5.4.1设计界面
采用Tkinter的place几何布局管理器设计GUI图形界面,运行效果如图52所示。


图52place几何布局管理器


新建文件translate_test.py,编写如下代码: 



from tkinter import *

if __name__ == "__main__":

root = Tk()

root.title("单词翻译器")

root['width']=250;root['height']=130

Label(root,text = '输入要翻译的内容: ',width=15).place(x=1,y=1)#绝对坐标(1,1)

Entry1=Entry(root,width=20)

Entry1.place(x=110,y=1)#绝对坐标(110,1)

Label(root,text = '翻译的结果: ',width=18).place(x=1,y=20)     #绝对坐标(1,20)

s=StringVar()                               #一个StringVar()对象  

s.set("大家好,这是测试")

Entry2=Entry(root,width=20,textvariable=s)

Entry2.place(x=110,y=20)                   #绝对坐标(110,20)

Button1=Button(root,text = '翻译',width=8)

Button1.place(x=40,y=80)    #绝对坐标(40,80)

Button2=Button(root,text = '清空',width=8)

Button2.place(x=110,y=80)   #绝对坐标(110,80)

#给Button绑定鼠标监听事件

Button1.bind("<Button-1>",leftClick)                  #翻译按钮

Button2.bind("<Button-1>",leftClick2)                  #清空按钮

root.mainloop()




5.4.2使用百度翻译开放平台API
使用百度翻译需要向http://api.fanyi.baidu.com/api/trans/vip/translate通过POST或GET方法发送表53中的请求参数来访问服务。



表53请求参数



参数名类型必 填 参 数描述备注

qTEXTY请求翻译queryUTF8编码
fromTEXTY翻译源语言语言列表(可设置为auto)
toTEXTY译文语言语言列表(不可设置为auto)
appidINTYAPP ID可在管理控制台查看
saltINTY随机数
signTEXTY签名appid+q+salt+密钥的MD5值

sign签名是为了保证调用安全,使用MD5算法生成的一段字符串,生成的签名长度为32位,签名中的英文字符均为小写格式。为保证翻译质量,请将单次请求长度控制在 6000B以内(汉字约为2000个)。
签名生成方法如下。
(1) 将请求参数中的appid、query(q,注意为UTF8编码)、随机数salt以及平台分配的密钥(可在管理控制台查看)按照appid+q+salt+密钥的顺序拼接得到字符串1。
(2) 对字符串1做md5,得到32位小写的sign。

 先将需要翻译的文本转换为UTF8编码。
 在发送HTTP请求之前需要对各字段做URL encode。
 在生成签名拼接appid+q+salt+密钥字符串时,q不需要做URL encode,在生成签名之后,发送HTTP请求之前才需要对要发送的待翻译文本字段q做URL encode。


例如,将apple从英文翻译成中文,
请求参数如下: 



q=apple

from=en

to=zh

appid=2015063000000001

salt=1435660288

平台分配的密钥: 12345678




生成签名参数sign。
(1) 拼接字符串1。



拼接appid=2015063000000001+q=apple+salt=1435660288+密钥=12345678

得到字符串1 =2015063000000001apple143566028812345678




(2) 计算签名sign(对字符串1做md5加密,注意计算md5之前,串1必须为UTF8编码)。



sign=md5(2015063000000001apple143566028812345678)

sign=f89f9594663708c1605f3d736d01d2d4




通过Python提供的hashlib模块中的hashlib.md5()可以实现签名计算。例如: 



import hashlib  

m = '2015063000000001apple143566028812345678'  

m_MD5 = hashlib.md5(m)  

sign = m_MD5.hexdigest()  

print( 'm = ',m)  

print ('sign = ',sign)




得到签名之后,按照百度文档中的要求生成URL请求,提交后可返回翻译结果。
完整请求如下: 



http://api.fanyi.baidu.com/api/trans/vip/translate?q=apple&from=en&to=zh&appid=2015063000000001&salt=1435660288&sign=f89f9594663708c1605f3d736d01d2d4




也可以使用POST方法传送需要的参数。
本案例采用urllib.request.urlopen()函数中的data参数向服务器发送数据。
下面是发送data实例,向“百度翻译”发送要翻译数据,得到翻译结果。



#-*- coding: UTF-8 -*-

from tkinter import *

from urllib import request

from urllib import parse

import json

import hashlib

def translate_Word(en_str):

#simulation browse load host url,get cookie  

URL = 'http://api.fanyi.baidu.com/api/trans/vip/translate'

#en_str=input("请输入要翻译的内容:")

#创建Form_Data字典,存储向服务器发送的Data

#Form_Data={'from':'en','to':'zh','q':en_str,''appid'':'2015063000000001', 'salt': '1435660288'} 

Form_Data = {}

Form_Data['from'] = 'en'             

Form_Data['to'] = 'zh'

Form_Data['q'] = en_str#要翻译数据

Form_Data['appid'] = '2015063000000001'		#申请的APP ID

Form_Data['salt'] = '1435660288'

Key="12345678" 						#平台分配的密钥

m=Form_Data['appid']+en_str+Form_Data['salt']+Key

m_MD5 = hashlib.md5(m.encode('utf8'))

Form_Data['sign'] = m_MD5.hexdigest()



data = parse.urlencode(Form_Data).encode('utf-8') 	#使用urlencode方法转换标准格式

response = request.urlopen(URL,data)#传递Request对象和转换完格式的数据

html = response.read().decode('utf-8')			#读取信息并解码

translate_results = json.loads(html)				#使用JSON

print(translate_results)						#打印出JSON数据

translate_results = translate_results['trans_result'][0]['dst']#找到翻译结果

print("翻译的结果是: %s" % translate_results)			#打印翻译信息

return  translate_results

def leftClick(event):						#翻译按钮事件函数

en_str=Entry1.get()					#获取要翻译的内容

print(en_str)

vText=translate_Word(en_str)

Entry2.config(Entry2,text=vText)   		#修改翻译结果框文字

s.set("")

Entry2.insert(0,vText) 

def leftClick2(event):						#清空按钮事件函数

s.set("")

Entry2.insert(0,"")




这样就可以查看翻译的结果了,如下所示: 
输入要翻译的内容: I  am  a  teacher
翻译的结果是: 我是个教师。
得到的JSON数据如下: 



{'from': 'en', 'to': 'zh', 'trans_result': [{'dst': '我是个教师。', 'src': 'I  am  a  teacher'}]}




返回结果是JSON格式的,包含表54中的字段。



表54翻译结果的JSON字段



字段名类型描述

fromTEXT翻译源语言
toTEXT译文语言
trans_resultMIXED LIST翻译结果
srcTEXT原文
dstTEXT译文

其中,trans_result包含src和dst字段。
JSON是一种轻量级的数据交换格式,其中保存着想要的翻译结果,我们需要从爬取到的内容中找到JSON格式的数据,再将得到的JSON格式的翻译结果解析出来。
这里向服务器发送数据Form_Data也可以直接按如下方式写: 



Form_Data={'from':'en',  'to':'zh',  'q':en_str,''appid'':'2015063000000001', 'salt': '1435660288'}




现在只做了将英文翻译成中文,稍加改动就可以将中文翻译英文了: 



Form _Data ={ 'from':'zh',  'to':'en', 'q':en_str,''appid'':'2015063000000001', 'salt': '1435660288' }




这一行中的from和to的取值,应该可以用于其他语言之间的翻译。如果源语言语种不确定可设置为auto,目标语言语种不可设置为auto。百度翻译支持的语言简写如表55所示。



表55百度翻译支持的语言简写



语 言 简 写名称语 言 简 写名称

auto自动检测bul保加利亚语
zh中文est爱沙尼亚语
en英语dan丹麦语
yue粤语fin芬兰语
wyw文言文cs捷克语
jp日语rom罗马尼亚语
kor韩语slo斯洛文尼亚语
fra法语swe瑞典语
spa西班牙语hu匈牙利语
th泰语cht繁体中文
ara阿拉伯语vie越南语
ru俄语el希腊语
pt葡萄牙语nl荷兰语
de德语pl波兰语

读者可查阅资料编程向“有道翻译”http://fanyi.youdao.com/translate?smartresult=dict发送要翻译的数据data,得到翻译结果。

5.5API调用拓展——爬取天气预报信息


目前绝大多数网站以动态网页的形式发布信息。所谓动态网页,就是用相同的格式呈现不同的内容。例如,每天访问中国天气网,看到的信息呈现格式是不变的,但天气信息数据是变化的。如果网站没有提供API调用的功能,则可以先获取网页数据,然后将网页数据转换为字符串后利用正则表达式提取所需的内容,即所谓的爬虫方式。利用爬虫经常获取的是网页中动态变化的数据。因此,爬虫程序是自动获取网页中动态变化数据的工具。
中国天气网(http://www.weather.com.cn)向用户提供国内各城市的天气信息,并提供API供程序获取所需的天气数据,返回数据格式为JSON。
中国天气网提供API网址类似http://www.weather.com.cn/data/cityinfo/101180101.html,其中,101180101为郑州的城市编码。各城市编码可通过网络搜索取得。
例如: 荥阳101180103新郑101180106新密101180105登封101180104中牟101180107巩义101180102上街101180108郑州101180101卢氏101181704灵宝101181702三门峡101181701义马101181705渑池101181703陕县101181706南阳101180701新野101180709邓州101180711
南召101180702方城101180703
下面的代码为调用API在中国天气网获取郑州市当天天气预报数据的实例。



import  urllib.request#引入urllib包中的模块request

import  json          #引入json模块

code='101180101'      #郑州市城市编码

#用字符串变量url保存合成的网址

url='http://www.weather.com.cn/data/cityinfo/%s.html'% code 

print('url=',url)

obj=urllib.request.urlopen(url)   #调用函数urlopen()打开给定的网址,结果返回到对象obj中

print('type(obj)=',type(obj))     #输出obj的类型

data_b=obj.read()             #read()从对象obj中读取内容,内容为bytes字节流数据

print('字节流数据=',data_b)

data_s=data_b.decode('utf-8')    #bytes字节流数据转换为字符串类型

print('字符串数据=',data_s)

#调用JSON的函数loads()将data_s中保存的字符串数据转换为字典型数据

data_dict=json.loads(data_s)

print('data_dict=',data_dict)       #输出字典data_dict的内容

rt =data_dict['weatherinfo']       #取得键为"weatherinfo"的内容

print('rt=',rt)                   #rt仍然为字典型变量

#获取城市名称、天气状况、最高温和最低温

my_rt=('%s,%s,%s~%s')% (rt['city'],rt['weather'],rt['temp1'],rt['temp2'])

print(my_rt)




代码中,用字符串变量url保存合成的网址,该网址为给定编码城市的当天天气预报。调用函数urlopen()打开给定的网址,结果返回到对象obj中。调用函数read()从对象obj中读取天气预报内容,最后调用JSON的函数loads()将天气预报内容转换为字典型数据,保存到字典型变量data_dict中。从字典型变量data_dict中取得键为“weatherinfo”的内容,保存到变量rt中,rt仍然为字典型变量。
城市名称、天气状况、最高温和最低温均是从字典型变量rt中取得的,键分别为“city”“weather”“temp1”“temp2”。
运行结果如下: 



url= http://www.weather.com.cn/data/cityinfo/101180101.html

type(obj)= <class 'http.client.HTTPResponse'>

字节流数据= b'{"weatherinfo":{"city":"\xe9\x83\x91\xe5\xb7\x9e","cityid":"101180101","temp1":"17\xe2\x84\x83","temp2":"27\xe2\x84\x83","weather":"\xe9\x98\xb4\xe8\xbd\x99\xb4","img1":"n2.gif","img2":"d0.gif","ptime":"18:00"}}'

字符串数据= {"weatherinfo":{"city":"郑州","cityid":"101180101","temp1":"17℃","temp2":"27℃","weather":"阴转晴","img1":"n2.gif","img2":"d0.gif","ptime":"18:00"}}

data_dict= {'weatherinfo': {'ptime': '18:00', 'img2': 'd0.gif', 'weather': '阴转晴', 'img1': 'n2.gif', 'temp1': '17℃', 'cityid': '101180101', 'temp2': '27℃', 'city': '郑州'}}

rt= {'ptime': '18:00', 'img2': 'd0.gif', 'weather': '阴转晴', 'img1': 'n2.gif', 'temp1': '17℃', 'cityid': '101180101', 'temp2': '27℃', 'city': '郑州'}

郑州,阴转晴,17℃~27℃




从结果可知,函数urlopen()的返回值为来自服务器的回应对象,调用其read()函数可得bytes字节流类型的数据,将bytes字节流类型的数据转换为字符串类型,即为JSON数据。调用JSON函数loads()可将JSON数据转换为字典型数据,而中国天气网返回的数据为嵌套1层的字典型数据。因此,首先通过rt=data_dict['weatherinfo']取得城市天气预报信息,再通过rt['city']、rt['weather']、rt['temp1']和rt['temp2']取得具体的数据。