datetime —— 處理日期和時間
獲取當前日期和時間
from datetime import datetime
# 獲取當前datetime
now = datetime.now()
獲取指定日期和時間
# # 用指定日期時間創(chuàng)建datetime
dt = datetime(2015, 4, 19, 12, 20)
datetime轉(zhuǎn)換為timestamp
dt = datetime(2015, 4, 19, 12, 20)
# 把datetime轉(zhuǎn)換為timestamp
t = dt.timestamp()
Python 的 timestamp 是一個浮點數(shù)。如果有小數(shù)位,小數(shù)位表示毫秒數(shù)
某些編程語言(如Java和JavaScript)的 timestamp 使用整數(shù)表示毫秒數(shù),這種情況下只需要把 timestamp 除以1000就得到Python的浮點表示方法
timestamp轉(zhuǎn)換為datetime
t = 1429417200.0
# timestamp和本地時間做轉(zhuǎn)換
dt1 = datetime.fromtimestamp(t)
# timestamp是一個浮點數(shù),它沒有時區(qū)的概念,而datetime是有時區(qū)的
# UTC時間,即UTC+0:00時區(qū)的時間
dt2 = datetime.utcfromtimestamp(t)
str轉(zhuǎn)換為datetime
dt = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
datetime轉(zhuǎn)換為str
now = datetime.now()
dtStr = now.strftime('%a, %b %d %H:%M')
datetime加減
from datetime import datetime, timedelta
now = datetime.now()
now + timedelta(hours=10)
now - timedelta(days=1)
now + timedelta(days=2, hours=12)
本地時間轉(zhuǎn)換為UTC時間
一個datetime類型有一個時區(qū)屬性tzinfo,但是默認為None,所以無法區(qū)分這個datetime到底是哪個時區(qū),除非強行給datetime設(shè)置一個時區(qū):
from datetime import datetime, timedelta, timezone
# 創(chuàng)建時區(qū)UTC+8:00
tz_utc_8 = timezone(timedelta(hours=8))
# datetime表示的時間需要時區(qū)信息才能確定一個特定的時間,否則只能視為本地時間
now = datetime.now()
# 強制設(shè)置為UTC+8:00
dt = now.replace(tzinfo=tz_utc_8)
時區(qū)轉(zhuǎn)換
from datetime import datetime, timedelta, timezone
# 通過 utcnow() 拿到當前的UTC時間,并強制設(shè)置為UTC,作為基準時間
utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
print(utc_dt)
# 利用帶時區(qū)的 datetime ,通過 astimezone() 方法,可以轉(zhuǎn)換到任意時區(qū)
bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
print(bj_dt)
tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
print(tokyo_dt)
# 不是必須從UTC+0:00時區(qū)轉(zhuǎn)換到其他時區(qū),任何帶時區(qū)的datetime都可以正確轉(zhuǎn)換
tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
print(tokyo_dt2)
collections
namedtuple
namedtuple
是一個函數(shù),它用來創(chuàng)建一個自定義的 tuple
對象,并且規(guī)定了 tuple
元素的個數(shù),并可以用屬性而不是索引來引用 tuple
的某個元素
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
p.x
p.y
isinstance(p, tuple) ==> True
deque
deque
是為了高效實現(xiàn)插入和刪除操作的雙向列表,適合用于隊列和棧
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x')
q.pop()
q.appendleft('x')
q.popleft()
defaultdict
使用 dict 時,如果引用的 Key 不存在,就會拋出 KeyError 。如果希望 key 不存在時,返回一個默認值,就可以用 defaultdict :
from collections import defaultdict
dd = defaultdict(lambda: 'N/A')
dd['key1'] = 'abc'
dd['key1'] ==> 'abc'
dd['key2'] ==> 'N/A'
OrderedDict
要保持 dict 中 Key 的順序,可以用 OrderedDict ,OrderedDict 的 Key 會按照插入的順序排列
from collections import OrderedDict
# 普通的 dict
d = dict([('a', 1), ('b', 2), ('c', 3)])
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
od ==> OrderedDict([('a', 1), ('b', 2), ('c', 3)])
OrderedDict 可以實現(xiàn)一個 FIFO(先進先出)的 dict ,當容量超出限制時,先刪除最早添加的 Key :
from collections import OrderedDict
class LastUpdatedOrderedDict(OrderedDict):
def __init__(self, capacity):
super(LastUpdatedOrderedDict, self).__init__()
self._capacity = capacity
def __setitem__(self, key, value):
containsKey = 1 if key in self else 0
if len(self) - containsKey >= self._capacity:
last = self.popitem(last=False)
print('remove:', last)
if containsKey:
del self[key]
print('set:', (key, value))
else:
print('add:', (key, value))
OrderedDict.__setitem__(self, key, value)
Counter
Counter 是一個簡單的計數(shù)器,例如,統(tǒng)計字符出現(xiàn)的個數(shù):
from collections import Counter
c = Counter()
for ch in 'programming':
c[ch] = c[ch] + 1
c
base64
Base64編碼會把3字節(jié)(4*6 bit)的二進制數(shù)據(jù)編碼為4字節(jié)的文本數(shù)據(jù),長度增加33%,好處是編碼后的文本數(shù)據(jù)可以在郵件正文、網(wǎng)頁等直接顯示
如果要編碼的二進制數(shù)據(jù)不是3的倍數(shù),Base64用\x00字節(jié)在末尾補足后,再在編碼的末尾加上1個或2個=號,表示補了多少字節(jié),解碼的時候,會自動去掉
"url safe"的base64編碼:針對字符 + 和 / 在URL中就不能直接作為參數(shù),把字符 + 和 / 分別變成 - 和 _
import base64
base64.b64encode(b'binary\x00string') ==> b'YmluYXJ5AHN0cmluZw=='
# b64decode() 可接受一個 str
base64.b64decode(b'YmluYXJ5AHN0cmluZw==') ==> b'binary\x00string'
base64.b64decode('YmluYXJ5AHN0cmluZw==') ==> b'binary\x00string'
base64.b64encode(b'i\xb7\x1d\xfb\xef\xff') ==> b'abcd++//'
base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff') ==> b'abcd--__'
base64.urlsafe_b64decode('abcd--__') ==> b'i\xb7\x1d\xfb\xef\xff'
由于=字符也可能出現(xiàn)在Base64編碼中,但=用在URL、Cookie里面會造成歧義,所以,很多Base64編碼后會把=去掉
因為Base64是把3個字節(jié)變?yōu)?個字節(jié),所以,Base64編碼的長度永遠是4的倍數(shù),因此,需要加上=把Base64字符串的長度變?yōu)?的倍數(shù),就可以正常解碼了
能處理去掉=的base64解碼函數(shù):
import base64
def safe_base64_decode(s):
return base64.b64decode(s + '=' * (4 - len(s) % 4))
struct
準確地講,Python沒有專門處理字節(jié)的數(shù)據(jù)類型
但由于b'str'可以表示字節(jié),所以,字節(jié)數(shù)組=二進制str
Python 提供了一個 struct
模塊來解決 bytes
和其他二進制數(shù)據(jù)類型的轉(zhuǎn)換
struct
的 pack
函數(shù)把任意數(shù)據(jù)類型變成 bytes
:
import struct
# pack的第一個參數(shù)是處理指令,'>I'的意思是:>表示字節(jié)順序是big-endian,也就是網(wǎng)絡(luò)序,I表示4字節(jié)無符號整數(shù)
struct.pack('>I', 10240099) ==> b'\x00\x9c@c'
unpack
把 bytes
變成相應的數(shù)據(jù)類型:
# 根據(jù)>IH的說明,后面的bytes依次變?yōu)?I:4字節(jié)無符號整數(shù) 和 H:2字節(jié)無符號整數(shù)
struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80') ==> (4042322160, 32896)
hashlib
Python 的 hashlib
提供了常見的摘要算法,如MD5,SHA1等等
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
# 生成結(jié)果是固定的128 bit字節(jié),通常用一個32位的16進制字符串表示
print(md5.hexdigest())
# 如果數(shù)據(jù)量很大,可以分塊多次調(diào)用update(),最后計算的結(jié)果是一樣的
md5 = hashlib.md5()
md5.update('how to use md5 in '.encode('utf-8'))
md5.update('python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
# SHA1的結(jié)果是160 bit字節(jié),通常用一個40位的16進制字符串表示
print(sha1.hexdigest())
itertools
Python 的內(nèi)建模塊 itertools
提供了非常有用的用于操作迭代對象的函數(shù)
itertools
模塊提供的全部是處理迭代功能的函數(shù),它們的返回值不是 list
,而是 Iterator
itertools
提供的幾個“無限”迭代器:
-
count()
會創(chuàng)建一個無限的迭代器
import itertools
natuals = itertools.count(1)
for n in natuals:
print(n) ==> 1,2,3,...
-
cycle()
會把傳入的一個序列無限重復下去
import itertools
cs = itertools.cycle('ABC')
for c in cs:
print(c) ==> 'A','b','c','A','b','c',...
-
repeat()
負責把一個元素無限重復下去,不過如果提供第二個參數(shù)就可以限定重復次數(shù)
import itertools
ns = itertools.repeat('A', 3)
for n in ns:
print(n) ==> 'A','A','A'
無限序列雖然可以無限迭代下去,但是通常我們會通過takewhile()等函數(shù)根據(jù)條件判斷來截取出一個有限的序列:
import itertools
natuals = itertools.count(1)
ns = itertools.takewhile(lambda x: x <= 10, natuals)
list(ns) ==> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
itertools
提供的幾個迭代器操作函數(shù)更加有用:
-
chain()
可以把一組迭代對象串聯(lián)起來,形成一個更大的迭代器
import itertools
for c in itertools.chain('ABC', 'XYZ'):
print(c) ==> 'A' 'B' 'C' 'X' 'Y' 'Z'
-
groupby()
把迭代器中相鄰的重復元素挑出來放在一起
import itertools
for key, group in itertools.groupby('AAABBBCCAAA'):
print(key, list(group))
'''
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']
'''
# 實際上挑選規(guī)則是通過函數(shù)完成的,只要作用于函數(shù)的兩個元素返回的值相等,這兩個元素就被認為是在一組的,而函數(shù)返回值作為組的key
for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
print(key, list(group))
'''
A ['A', 'a', 'a']
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']
'''
contextlib
Python 的 with
語句允許我們非常方便地使用資源,而不必擔心資源沒有關(guān)閉
with open('/path/to/file', 'r') as f:
f.read()
并不是只有 open()
函數(shù)返回的fp對象才能使用 with
語句。實際上,任何對象,只要正確實現(xiàn)了上下文管理,就可以用于 with
語句
實現(xiàn)上下文管理是通過 __enter__
和 __exit__
這兩個方法實現(xiàn)的
class Query(object):
def __init__(self, name):
self.name = name
def __enter__(self):
print('Begin')
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print('Error')
else:
print('End')
def query(self):
print('Query info about %s...' % self.name)
with Query('Bob') as q:
q.query()
@contextmanager
編寫 __enter__
和 __exit__
仍然很繁瑣,因此 Python 的標準庫 contextlib
提供了更簡單的寫法
@contextmanager
這個 decorator
接受一個 generator
,用 yield
語句把 with ... as var
把變量輸出出去,然后,with
語句就可以正常地工作了
from contextlib import contextmanager
class Query(object):
def __init__(self, name):
self.name = name
def query(self):
print('Query info about %s...' % self.name)
@contextmanager
def create_query(name):
print('Begin')
q = Query(name)
yield q
print('End')
with create_query('Bob') as q:
q.query()
很多時候,我們希望在某段代碼執(zhí)行前后自動執(zhí)行特定代碼,也可以用 @contextmanager
實現(xiàn)
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
print("world")
代碼的執(zhí)行順序是:
- with語句首先執(zhí)行yield之前的語句,因此打印出<h1>;
- yield調(diào)用會執(zhí)行with語句內(nèi)部的所有語句,因此打印出hello和world;
- 最后執(zhí)行yield之后的語句,打印出</h1>。
因此,@contextmanager 讓我們通過編寫 generator 來簡化上下文管理
@closing
如果一個對象沒有實現(xiàn)上下文,我們就不能把它用于 with
語句
可以用 closing()
來把該對象變?yōu)樯舷挛膶ο?/p>
用 with
語句使用 urlopen()
:
from contextlib import closing
from contextlib import contextmanager
from urllib.request import urlopen
with closing(urlopen('https://www.python.org')) as page:
for line in page:
print(line)
# closing也是一個經(jīng)過@contextmanager裝飾的generator
# 它的作用就是把任意對象變?yōu)樯舷挛膶ο螅⒅С謜ith語句
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
XML
操作XML有兩種方法:DOM和SAX
- DOM會把整個XML讀入內(nèi)存,解析為樹,因此占用內(nèi)存大,解析慢,優(yōu)點是可以任意遍歷樹的節(jié)點
- SAX是流模式,邊讀邊解析,占用內(nèi)存小,解析快,缺點是我們需要自己處理事件
正常情況下,優(yōu)先考慮SAX,因為DOM實在太占內(nèi)存
在 Python 中使用 SAX 解析 XML 非常簡潔,通常我們關(guān)心的事件是 start_element
,end_element
和 char_data
,準備好這3個函數(shù),然后就可以解析 xml 了
from xml.parsers.expat import ParserCreate
class DefaultSaxHandler(object):
def start_element(self, name, attrs):
print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))
def end_element(self, name):
print('sax:end_element: %s' % name)
def char_data(self, text):
print('sax:char_data: %s' % text)
xml = r'''<?xml version="1.0"?>
<ol>
<li><a href="/python">Python</a></li>
<li><a href="/ruby">Ruby</a></li>
</ol>
'''
handler = DefaultSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.start_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(xml)
生成 XML 可以用字符串拼接。復雜 XML 建議改用 JSON
str.join(list)
返回 str
利用SAX編寫程序解析Yahoo的XML格式的天氣預報,獲取當天和第二天的天氣:(未完成)
from xml.parsers.expat import ParserCreate
class WeatherSaxHandler(object):
def __init__(self):
self._city = ''
self._country = ''
self._data = {}
@property
def get_data(self):
return self._data
def start_element(self, name, attrs):
# print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))
if name == 'yweather:location':
self._data['city'] = attrs['city']
self._data['country'] = attrs['country']
elif
def end_element(self, name):
print('sax:end_element: %s' % name)
def char_data(self, text):
print('sax:char_data: %s' % text)
def parse_weather(xml):
handler = WeatherSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.start_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(xml)
return handler.
return {
'city': 'Beijing',
'country': 'China',
'today': {
'text': 'Partly Cloudy',
'low': 20,
'high': 33
},
'tomorrow': {
'text': 'Sunny',
'low': 21,
'high': 34
}
}
測試數(shù)據(jù):
data = r'''<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
<channel>
<title>Yahoo! Weather - Beijing, CN</title>
<lastBuildDate>Wed, 27 May 2015 11:00 am CST</lastBuildDate>
<yweather:location city="Beijing" region="" country="China"/>
<yweather:units temperature="C" distance="km" pressure="mb" speed="km/h"/>
<yweather:wind chill="28" direction="180" speed="14.48" />
<yweather:atmosphere humidity="53" visibility="2.61" pressure="1006.1" rising="0" />
<yweather:astronomy sunrise="4:51 am" sunset="7:32 pm"/>
<item>
<geo:lat>39.91</geo:lat>
<geo:long>116.39</geo:long>
<pubDate>Wed, 27 May 2015 11:00 am CST</pubDate>
<yweather:condition text="Haze" code="21" temp="28" date="Wed, 27 May 2015 11:00 am CST" />
<yweather:forecast day="Wed" date="27 May 2015" low="20" high="33" text="Partly Cloudy" code="30" />
<yweather:forecast day="Thu" date="28 May 2015" low="21" high="34" text="Sunny" code="32" />
<yweather:forecast day="Fri" date="29 May 2015" low="18" high="25" text="AM Showers" code="39" />
<yweather:forecast day="Sat" date="30 May 2015" low="18" high="32" text="Sunny" code="32" />
<yweather:forecast day="Sun" date="31 May 2015" low="20" high="37" text="Sunny" code="32" />
</item>
</channel>
</rss>
'''
測試:
weather = parse_weather(data)
assert weather['city'] == 'Beijing', weather['city']
assert weather['country'] == 'China', weather['country']
assert weather['today']['text'] == 'Partly Cloudy', weather['today']['text']
assert weather['today']['low'] == 20, weather['today']['low']
assert weather['today']['high'] == 33, weather['today']['high']
assert weather['tomorrow']['text'] == 'Sunny', weather['tomorrow']['text']
assert weather['tomorrow']['low'] == 21, weather['tomorrow']['low']
assert weather['tomorrow']['high'] == 34, weather['tomorrow']['high']
print('Weather:', str(weather))
HTMLParser
from html.parser import HTMLParser
from html.entities import name2codepoint
class MyHTMLParser(HTMLParser):
def handle_starttag(self, tag, attrs):
print('<%s>' % tag)
def handle_endtag(self, tag):
print('</%s>' % tag)
def handle_startendtag(self, tag, attrs):
print('<%s/>' % tag)
def handle_data(self, data):
print(data)
def handle_comment(self, data):
print('<!--', data, '-->')
def handle_entityref(self, name):
print('&%s;' % name)
def handle_charref(self, name):
print('&#%s;' % name)
parser = MyHTMLParser()
parser.feed('''<html>
<head></head>
<body>
<!-- test html parser -->
<p>Some <a href=\"#\">html</a> HTML tutorial...<br>END</p>
</body></html>''')
urllib
urllib
提供了一系列用于操作URL的功能
urllib
的 request
模塊可以非常方便地抓取URL內(nèi)容,也就是發(fā)送一個GET請求到指定的頁面,然后返回HTTP的響應:
from urllib import request
with request.urlopen('https://api.douban.com/v2/book/2129650') as f:
data = f.read()
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', data.decode('utf-8'))
模擬瀏覽器發(fā)送GET請求,就需要使用 Request
對象,通過往 Request
對象添加 HTTP 頭,我們就可以把請求偽裝成瀏覽器
from urllib import request
# 模擬iPhone 6去請求豆瓣首頁
req = request.Request('http://www.douban.com/')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
with request.urlopen(req) as f:
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', f.read().decode('utf-8'))
如果要以 POST 發(fā)送一個請求,只需要把參數(shù) data 以 bytes 形式傳入
from urllib import request, parse
# 模擬一個微博登錄
print('Login to weibo.cn...')
email = input('Email: ')
passwd = input('Password: ')
login_data = parse.urlencode([
('username', email),
('password', passwd),
('entry', 'mweibo'),
('client_id', ''),
('savestate', '1'),
('ec', ''),
('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')
])
req = request.Request('https://passport.weibo.cn/sso/login')
req.add_header('Origin', 'https://passport.weibo.cn')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')
with request.urlopen(req, data=login_data.encode('utf-8')) as f:
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', f.read().decode('utf-8'))
如果還需要更復雜的控制,比如通過一個 Proxy 去訪問網(wǎng)站,我們需要利用 ProxyHandler
來處理
proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.example.com:3128/'})
proxy_auth_handler = urllib.request.ProxyBasicAuthHandler()
proxy_auth_handler.add_password('realm', 'host', 'username', 'password')
opener = urllib.request.build_opener(proxy_handler, proxy_auth_handler)
with opener.open('http://www.example.com/login.html') as f:
pass
作業(yè):利用urllib讀取XML,將XML一節(jié)的數(shù)據(jù)由硬編碼改為由urllib獲取:
import re
from xml.parsers.expat import ParserCreate
from urllib import request
class DefaultSaxHandler(object):
def __init__(self):
self.today = 0
self.__data = {}
@property
def get_data(self):
return self.__data
def satrt_element(self,name,attrs):
if name == "yweather:location":
self.__data['city'] = attrs['city']
self.__data['country'] = attrs['country']
if name == "yweather:condition":
self.today = re.split(r'\s?', attrs['date'])[1]
if name == "yweather:forecast":
if re.split(r'\s?', attrs['date'])[0] == self.today:
self.__data['today'] = {}
self.__data['today']['text'] = attrs['text']
self.__data['today']['low'] = int(attrs['low'])
self.__data['today']['high'] = int(attrs['high'])
if int(re.split(r'\s?', attrs['date'])[0]) == int(self.today) + 1:
self.__data['tomorrow'] = {}
self.__data['tomorrow']['text'] = attrs['text']
self.__data['tomorrow']['low'] = int(attrs['low'])
self.__data['tomorrow']['high'] = int(attrs['high'])
def end_element(self,name):
pass
def char_data(self,text):
pass
class get_weather(object):
def __init__(self,city):
self._city = city
@staticmethod
def get_yahoo(city):
url = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22" + city +"%22)&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"
with request.urlopen(url) as f:
data = f.read()
return data
def parse_weather(self):
handler = DefaultSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.satrt_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(self.get_yahoo(self._city))
return str(handler.get_data)
weather = get_weather("shanghai").parse_weather()
print(weather)