模块知识

a='我是中国人'.encode('utf-8') #3.0默认是unicode,转换成utf-8html

一、导入模块:node

默认是在以下目录的能够直接导入:python

import sys
print sys.path
   
结果:
['/Users/wupeiqi/PycharmProjects/calculator/p1/pp1', '/usr/local/lib/python2.7/site-packages/setuptools-15.2-py2.7.egg', '/usr/local/lib/python2.7/site-packages/distribute-0.6.28-py2.7.egg', '/usr/local/lib/python2.7/site-packages/MySQL_python-1.2.4b4-py2.7-macosx-10.10-x86_64.egg', '/usr/local/lib/python2.7/site-packages/xlutils-1.7.1-py2.7.egg', '/usr/local/lib/python2.7/site-packages/xlwt-1.0.0-py2.7.egg', '/usr/local/lib/python2.7/site-packages/xlrd-0.9.3-py2.7.egg', '/usr/local/lib/python2.7/site-packages/tornado-4.1-py2.7-macosx-10.10-x86_64.egg', '/usr/local/lib/python2.7/site-packages/backports.ssl_match_hostname-3.4.0.2-py2.7.egg', '/usr/local/lib/python2.7/site-packages/certifi-2015.4.28-py2.7.egg', '/usr/local/lib/python2.7/site-packages/pyOpenSSL-0.15.1-py2.7.egg', '/usr/local/lib/python2.7/site-packages/six-1.9.0-py2.7.egg', '/usr/local/lib/python2.7/site-packages/cryptography-0.9.1-py2.7-macosx-10.10-x86_64.egg', '/usr/local/lib/python2.7/site-packages/cffi-1.1.1-py2.7-macosx-10.10-x86_64.egg', '/usr/local/lib/python2.7/site-packages/ipaddress-1.0.7-py2.7.egg', '/usr/local/lib/python2.7/site-packages/enum34-1.0.4-py2.7.egg', '/usr/local/lib/python2.7/site-packages/pyasn1-0.1.7-py2.7.egg', '/usr/local/lib/python2.7/site-packages/idna-2.0-py2.7.egg', '/usr/local/lib/python2.7/site-packages/pycparser-2.13-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Django-1.7.8-py2.7.egg', '/usr/local/lib/python2.7/site-packages/paramiko-1.10.1-py2.7.egg', '/usr/local/lib/python2.7/site-packages/gevent-1.0.2-py2.7-macosx-10.10-x86_64.egg', '/usr/local/lib/python2.7/site-packages/greenlet-0.4.7-py2.7-macosx-10.10-x86_64.egg', '/Users/wupeiqi/PycharmProjects/calculator', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages', '/Library/Python/2.7/site-packages']
若是sys.path路径列表没有你想要的路径,能够经过 sys.path.append('路径') 添加。
View Code

 

导入模块其实就是告诉Python解释器去解释那个py文件linux

  • 导入一个py文件,解释器解释该py文件
  • 导入一个包,解释器解释该包下的 __init__.py 文件 【py2.7】

那么问题来了,导入模块时是根据那个路径做为基准来进行的呢?即:sys.pathgit

import module
from module.xx.xx import xx
from module.xx.xx import xx as rename 
from module.xx.xx import *

二、导入自定义模块:github

import sys
import os
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(project_path)

本身写个模块web

python tab补全模块正则表达式

 for mac算法

 #!/usr/bin/env python 
 # python startup file 
 import sys
 import readline
 import rlcompleter
 import atexit
 import os
 # tab completion 
 readline.parse_and_bind('tab: complete')
 # history file 
 histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
 try:
     readline.read_history_file(histfile)
 except IOError:
     pass
 atexit.register(readline.write_history_file, histfile)
 del os, histfile, readline, rlcompleter
for linux

写完保存后就可使用了shell

localhost:~ jieli$ python
Python 2.7.10 (default, Oct 23 2015, 18:05:06)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tab

你会发现,上面本身写的tab.py模块只能在当前目录下导入,若是想在系统的何何一个地方都使用怎么办呢? 此时你就要把这个tab.py放到python全局环境变量目录里啦,基本通常都放在一个叫 Python/2.7/site-packages 目录下,这个目录在不一样的OS里放的位置不同,用 print(sys.path) 能够查看python环境变量列表。

自定义模块 和开源模块的使用参考 http://www.cnblogs.com/wupeiqi/articles/4963027.html 

三、经常使用内置模块整理:

Getpass模块:

输入密码时,若是想要不可见,须要利用getpass 模块中的 getpass方法,即:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
  
import getpass
  
# 将用户输入的内容赋值给 name 变量
pwd = getpass.getpass("请输入密码:")
  
# 打印输入的内容
print(pwd)

 

 

3.1 SYS模块:用于提供对Python解释器相关的操做:

sys.argv           命令行参数List,第一个元素是程序自己路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操做系统平台名称
sys.stdin          输入相关
sys.stdout         输出相关
sys.stderror       错误相关

例子:进度条:

import sys
import time


def view_bar(num, total):
    rate = float(num) / float(total)
    rate_num = int(rate * 100)
    r = '\r%d%%' % (rate_num, )
    sys.stdout.write(r)
    sys.stdout.flush()


if __name__ == '__main__':
    for i in range(0, 100):
        time.sleep(0.1)
        view_bar(i, 100)
View Code

3.2 OS模块:用于提供系统级别的操做:

os.getcwd()                 获取当前工做目录,即当前python脚本工做的目录路径
os.chdir("dirname")         改变当前脚本工做目录;至关于shell下cd
os.curdir                   返回当前目录: ('.')
os.pardir                   获取当前目录的父目录字符串名:('..')
os.makedirs('dir1/dir2')    可生成多层递归目录
os.removedirs('dirname1')   若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')         生成单级目录;至关于shell中mkdir dirname
os.rmdir('dirname')         删除单级空目录,若目录不为空则没法删除,报错;至关于shell中rmdir dirname
os.listdir('dirname')       列出指定目录下的全部文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()                 删除一个文件
os.rename("oldname","new")  重命名文件/目录
os.stat('path/filename')    获取文件/目录信息
os.sep                      操做系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep                  当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep                  用于分割文件路径的字符串
os.name                     字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")   运行shell命令,直接显示
os.environ                  获取系统环境变量
os.path.abspath(path)       返回path规范化的绝对路径
os.path.split(path)         将path分割成目录和文件名二元组返回
os.path.dirname(path)       返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path)      返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path)        若是path存在,返回True;若是path不存在,返回False
os.path.isabs(path)         若是path是绝对路径,返回True
os.path.isfile(path)        若是path是一个存在的文件,返回True。不然返回False
os.path.isdir(path)         若是path是一个存在的目录,则返回True。不然返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径以前的参数将被忽略
os.path.getatime(path)      返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path)      返回path所指向的文件或者目录的最后修改时间

3.3 time&datetime 模块

#_*_coding:utf-8_*_
__author__ = 'Alex Li'

import time


# print(time.clock()) #返回处理器时间,3.3开始已废弃 , 改为了time.process_time()测量处理器运算时间,不包括sleep时间,不稳定,mac上测不出来
# print(time.altzone)  #返回与utc时间的时间差,以秒计算\
# print(time.asctime()) #返回时间格式"Fri Aug 19 11:14:16 2016",
# print(time.localtime()) #返回本地时间 的struct time对象格式
# print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式

# print(time.asctime(time.localtime())) #返回时间格式"Fri Aug 19 11:14:16 2016",
#print(time.ctime()) #返回Fri Aug 19 12:38:29 2016 格式, 同上



# 日期字符串 转成  时间戳
# string_2_struct = time.strptime("2016/05/22","%Y/%m/%d") #将 日期字符串 转成 struct时间对象格式
# print(string_2_struct)
# #
# struct_2_stamp = time.mktime(string_2_struct) #将struct时间对象转成时间戳
# print(struct_2_stamp)



#将时间戳转为字符串格式
# print(time.gmtime(time.time()-86640)) #将utc时间戳转换成struct_time格式
# print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将utc struct_time格式转成指定的字符串格式





#时间加减
import datetime

# print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925
#print(datetime.date.fromtimestamp(time.time()) )  # 时间戳直接转成日期格式 2016-08-19
# print(datetime.datetime.now() )
# print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天
# print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天
# print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时
# print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分


#
# c_time  = datetime.datetime.now()
# print(c_time.replace(minute=3,hour=2)) #时间替换
View Code
Directive Meaning Notes
%a Locale’s abbreviated weekday name.  
%A Locale’s full weekday name.  
%b Locale’s abbreviated month name.  
%B Locale’s full month name.  
%c Locale’s appropriate date and time representation.  
%d Day of the month as a decimal number [01,31].  
%H Hour (24-hour clock) as a decimal number [00,23].  
%I Hour (12-hour clock) as a decimal number [01,12].  
%j Day of the year as a decimal number [001,366].  
%m Month as a decimal number [01,12].  
%M Minute as a decimal number [00,59].  
%p Locale’s equivalent of either AM or PM. (1)
%S Second as a decimal number [00,61]. (2)
%U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3)
%w Weekday as a decimal number [0(Sunday),6].  
%W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3)
%x Locale’s appropriate date representation.  
%X Locale’s appropriate time representation.  
%y Year without century as a decimal number [00,99].  
%Y Year with century as a decimal number.  
%z Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].  
%Z Time zone name (no characters if no time zone exists).  
%% A literal '%' character.

 

3.4 datetime模块 能够算天和秒级计算,多用于时间运算

两个模块经常使用实例:

import time
import datetime
print(time.altzone/3600)
print(time.asctime())
t=time.localtime() #本地时间
t=time.localtime(time.time() + 3600*3) #加3小时,不能进行天数运算
print(t.tm_year,t.tm_yday)  #year to day =ytd,mtd=month to day
print(time.time())   #打印时间戳 从1970到如今
print(time.gmtime()) #utc time
print(time.ctime()) #返回当前时间

print(time.strptime('2016-11-11 23:20','%Y-%m-%d %H:%M')) #字符串转成时间对象
t2 = time.strptime('2016-11-11 23:20','%Y-%m-%d %H:%M')
t2_stamp= time.mktime(t2)
print(time.mktime(t2)) #转换成时间戳再计算

t3 = time.localtime(t2_stamp) #stamp to time struct
t3_str = time.strftime('%Y_%m_%d_%H_%M.log',t3)
print(t3_str)

print('datetime'.center(60,'_'))
print(datetime.datetime.now()) #打印当前时间

print(datetime.datetime.fromtimestamp(time.time()))

print(datetime.datetime.now() + datetime.timedelta(days=3)) #当前日期加3天
print(datetime.datetime.now() + datetime.timedelta(hours=3)) #加3小时

now=datetime.datetime.now()
print(now.replace(month=1,day=1))
View Code

3.5 json & pickle 模块

用于序列化的两个模块

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换

Json模块提供了四个功能:dumps、dump、loads、load

pickle模块提供了四个功能:dumps、dump、loads、load

3.6 shelve 模块

shelve模块是一个简单的k,v将内存数据经过文件持久化的模块,能够持久化任何pickle可支持的python数据格式

import shelve
 
d = shelve.open('shelve_test') #打开一个文件
 
class Test(object):
    def __init__(self,n):
        self.n = n
 
 
t = Test(123) 
t2 = Test(123334)
 
name = ["alex","rain","test"]
d["test"] = name #持久化列表
d["t1"] = t      #持久化类
d["t2"] = t2
 
d.close()

 

3.4 hashlib:

用于加密相关的操做,代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

例子:

import hashlib
 
# ######## md5 ########
hash = hashlib.md5()
# help(hash.update)
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
print(hash.digest())
 
 
######## sha1 ########
 
hash = hashlib.sha1()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
 
# ######## sha256 ########
 
hash = hashlib.sha256()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
 
 
# ######## sha384 ########
 
hash = hashlib.sha384()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
 
# ######## sha512 ########
 
hash = hashlib.sha512()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
View Code

以上加密算法虽然依然很是厉害,但时候存在缺陷,即:经过撞库能够反解。因此,有必要对加密算法中添加自定义key再来作加密。

import hashlib
 
# ######## md5 ########
 
hash = hashlib.md5(bytes('898oaFs09f',encoding="utf-8"))
hash.update(bytes('admin',encoding="utf-8"))
print(hash.hexdigest())

更多关于md5,sha1,sha256等介绍的文章看这里https://www.tbs-certificates.co.uk/FAQ/en/sha256.html

一样的对象,md5后结果同样

python内置还有一个 hmac 模块,它内部对咱们建立 key 和 内容 进行进一步的处理而后再加密

import hmac
 
h = hmac.new(bytes('898oaFs09f',encoding="utf-8"))
h.update(bytes('admin',encoding="utf-8"))
print(h.hexdigest())
import hmac
h_obj=hmac.new(b'salt',b'hello') #加密消息
print(h_obj.hexdigest())
/usr/bin/python3.5 /home/ld/mytest/day3/test_hashlib.py
3a2484b4f0df4f4157d069598a334b31
View Code

 

3.4 RANDOM模块:随机数

import random
 
print(random.random())
print(random.randint(1, 2))
print(random.randrange(1, 10))
print(random.sample(range(100),5)) #从范围100中随机选取5个数字
import random
import string  #string模块
str_source=string.ascii_letters + string.digits
print(random.sample(str_source,5)) #从上面的字符中随机选5个
随机验证码一种写法
import random
checkcode = ''
for i in range(4):
    current = random.randrange(0,4)
    if current != i:
        temp = chr(random.randint(65,90))
    else:
        temp = random.randint(0,9)
    checkcode += str(temp)
print checkcode
生成随机验证码另一种写法

例子:

import random
checkcode = ''
for i in range(4):
    current = random.randrange(0,4)
    if current != i:
        temp = chr(random.randint(65,90))
    else:
        temp = random.randint(0,9)
    checkcode += str(temp)
print checkcode
View Code

3.5 re

python中re模块提供了正则表达式相关操做

 

字符:

 

  . 匹配除换行符之外的任意字符
  \w 匹配字母或数字或下划线或汉字
  \s 匹配任意的空白符
  \d 匹配数字
  \b 匹配单词的开始或结束
  ^ 匹配字符串的开始
  $ 匹配字符串的结束

 

次数:

 

  * 重复零次或更屡次
  + 重复一次或更屡次
  ? 重复零次或一次
  {n} 重复n次
  {n,} 重复n次或更屡次
  {n,m} 重复n到m次

经常使用正则表达式符号

'.'     默认匹配除\n以外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^'     匹配字符开头,若指定flags MULTILINE,这种也能够匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$'     匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也能够
'*'     匹配*号前的字符0次或屡次,re.findall("ab*","cabb3abcbbac")  结果为['abb', 'ab', 'a']
'+'     匹配前一个字符1次或屡次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?'     匹配前一个字符1次或0次
'{m}'   匹配前一个字符m次
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|'     匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c
 
 
'\A'    只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的
'\Z'    匹配字符结尾,同$
'\d'    匹配数字0-9
'\D'    匹配非数字
'\w'    匹配[A-Za-z0-9]
'\W'    匹配非[A-Za-z0-9]
's'     匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
 
'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}
  

身份证分组实例:

>>> re.search("(\d{2})(\d{2})(\d{4})","371481199206143421 name alex").groups()
('37', '14', '8119')
View Code

实例:

>>> import re
>>> re.findall("\D+","ab3c4sdfd45634sfsd26ds6")
['ab', 'c', 'sdfd', 'sfsd', 'ds']
>>> re.findall("\d+","ab3c4sdfd45634sfsd26ds6")
['3', '4', '45634', '26', '6']
>>> re.split("\d+","ab3c4sdfd45634sfsd26ds6")
['ab', 'c', 'sdfd', 'sfsd', 'ds', '']
>>> re.sub("\d+","|","ab3c4sdfd45634sfsd26ds6")
'ab|c|sdfd|sfsd|ds|'

 

反斜杠的困扰

与大多数编程语言相同,正则表达式里使用"\"做为转义字符,这就可能形成反斜杠困扰。假如你须要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将须要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可使用r"\\"表示。一样,匹配一个数字的"\\d"能够写成r"\d"。有了原生字符串,你不再用担忧是否是漏写了反斜杠,写出来的表达式也更直观。

仅需轻轻知道的几个匹配模式

re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
M(MULTILINE): 多行模式,改变'^''$'的行为(参见上图)
S(DOTALL): 点任意匹配模式,改变'.'的行为

 

match

# match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
 
 
 match(pattern, string, flags=0)
 # pattern: 正则模型
 # string : 要匹配的字符串
 # falgs  : 匹配模式
     X  VERBOSE     Ignore whitespace and comments for nicer looking RE's.
     I  IGNORECASE  Perform case-insensitive matching.
     M  MULTILINE   "^" matches the beginning of lines (after a newline)
                    as well as the string.
                    "$" matches the end of lines (before a newline) as well
                    as the end of the string.
     S  DOTALL      "." matches any character at all, including the newline.
 
     A  ASCII       For string patterns, make \w, \W, \b, \B, \d, \D
                    match the corresponding ASCII character categories
                    (rather than the whole Unicode categories, which is the
                    default).
                    For bytes patterns, this flag is the only available
                    behaviour and needn't be specified.
      
     L  LOCALE      Make \w, \W, \b, \B, dependent on the current locale.
     U  UNICODE     For compatibility only. Ignored for string patterns (it
                    is the default), and forbidden for bytes patterns.

demo:

# 无分组
        r = re.match("h\w+", origin)
        print(r.group())     # 获取匹配到的全部结果
        print(r.groups())    # 获取模型中匹配到的分组结果
        print(r.groupdict()) # 获取模型中匹配到的分组结果

        # 有分组

        # 为什么要有分组?提取匹配成功的指定内容(先匹配成功所有正则,再匹配成功的局部内容提取出来)

        r = re.match("h(\w+).*(?P<name>\d)$", origin)
        print(r.group())     # 获取匹配到的全部结果
        print(r.groups())    # 获取模型中匹配到的分组结果
        print(r.groupdict()) # 获取模型中匹配到的分组中全部执行了key的组
View Code

 search:

# search,浏览整个字符串去匹配第一个,未匹配成功返回None

# search(pattern, string, flags=0)
 # 无分组

        r = re.search("a\w+", origin)
        print(r.group())     # 获取匹配到的全部结果
        print(r.groups())    # 获取模型中匹配到的分组结果
        print(r.groupdict()) # 获取模型中匹配到的分组结果

        # 有分组

        r = re.search("a(\w+).*(?P<name>\d)$", origin)
        print(r.group())     # 获取匹配到的全部结果
        print(r.groups())    # 获取模型中匹配到的分组结果
        print(r.groupdict()) # 获取模型中匹配到的分组中全部执行了key的组
View Code
>>> re.search("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}","inet 地址:192.168.12.55 广播:192.168.12.255").group()
'192.168.12.55'
>>> print(re.search("(\d{1,3}\.){3}\d{1,3}", "inet 地址:192.168.12.55 广播:192.168.12.255"))
<_sre.SRE_Match object; span=(8, 21), match='192.168.12.55'>
>>> print(re.search("(\d{1,3}\.){3}\d{1,3}", "inet 地址:192.168.12.55 广播:192.168.12.255").group(0))
192.168.12.55

 

findall:

# findall,获取非重复的匹配列表;若是有一个组则以列表形式返回,且每个匹配均是字符串;若是模型中有多个组,则以列表形式返回,且每个匹配均是元祖;

# 空的匹配也会包含在结果中
#findall(pattern, string, flags=0)
demo:
 # 无分组
        r = re.findall("a\w+",origin)
        print(r)

        # 有分组
        origin = "hello alex bcd abcd lge acd 19"
        r = re.findall("a((\w*)c)(d)", origin)
        print(r)
View Code

sub:

# sub,替换匹配成功的指定位置字符串

 
sub(pattern, repl, string, count = 0 , flags = 0 )
# pattern: 正则模型
# repl   : 要替换的字符串或可执行对象
# string : 要匹配的字符串
# count  : 指定匹配个数
# flags  : 匹配模式
demo:
 # 与分组无关

        origin = "hello alex bcd alex lge alex acd 19"
        r = re.sub("a\w+", "999", origin, 2)
        print(r)
View Code

split:

# split,根据正则匹配分割字符串

 
split(pattern, string, maxsplit = 0 , flags = 0 )
# pattern: 正则模型
# string : 要匹配的字符串
# maxsplit:指定分割个数
# flags  : 匹配模式
demo:
# 无分组
        origin = "hello alex bcd alex lge alex acd 19"
        r = re.split("alex", origin, 1)
        print(r)

        # 有分组
        
        origin = "hello alex bcd alex lge alex acd 19"
        r1 = re.split("(alex)", origin, 1)
        print(r1)
        r2 = re.split("(al(ex))", origin, 1)
        print(r2)
View Code

 

经常使用正则表达式:

IP:
^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$
手机号:
^1[3|4|5|8][0-9]\d{8}$
邮箱:
[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+
View Code

 

 

 

四、序列化:

Python中用于序列化的两个模块

  • json     用于【字符串】和 【python基本数据类型】 间进行转换
  • pickle   用于【python特有的类型】 和 【python基本数据类型】间进行转换

Json模块提供了四个功能:dumps、dump、loads、load

pickle模块提供了四个功能:dumps、dump、loads、load.

demo:

import pickle
data = {'k1':123,'k2':'hello'}
#pickle.dumps 将数据经过特殊的形式转换为只有python语言认识的字符串
p_str = pickle.dumps(data)
#pickle.dump将经过特殊的形式转换为只有Python语言认识的字符串,并写入文件
with open('d:/result.pk','w') as fp:
    picke.dump(data,fp)
import json
#json.dumps将经过特殊的形式转换为全部程序 语言都认识的字符串
j_str=json.dumps(data)
print j_str
#json.dumps将经过特殊的形式转换为全部程序 语言都认识的字符串并写入文件
with open('d:/result.json','w') as fp:
    json.dump(data,fp)
View Code

5.configparser:configparser用于处理特定格式的文件,其本质上是利用open来操做文件。

# 注释1
;  注释2
 
[section1] # 节点
k1 = v1    #
k2:v2       #
 
[section2] # 节点
k1 = v1    #

本身生成特定格式文件当如何作?

import configparser
 
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
                      'Compression': 'yes',
                     'CompressionLevel': '9'}
 
config['bitbucket.org'] = {}
config['bitbucket.org']['User'] = 'hg'
config['topsecret.server.com'] = {}
topsecret = config['topsecret.server.com']
topsecret['Host Port'] = '50022'     # mutates the parser
topsecret['ForwardX11'] = 'no'  # same here
config['DEFAULT']['ForwardX11'] = 'yes'
with open('example.ini', 'w') as configfile:
   config.write(configfile)
View Code

写完了还能够再读出来哈。

>>> import configparser
>>> config = configparser.ConfigParser()
>>> config.sections()
[]
>>> config.read('example.ini')
['example.ini']
>>> config.sections()
['bitbucket.org', 'topsecret.server.com']
>>> 'bitbucket.org' in config
True
>>> 'bytebong.com' in config
False
>>> config['bitbucket.org']['User']
'hg'
>>> config['DEFAULT']['Compression']
'yes'
>>> topsecret = config['topsecret.server.com']
>>> topsecret['ForwardX11']
'no'
>>> topsecret['Port']
'50022'
>>> for key in config['bitbucket.org']: print(key)
...
user
compressionlevel
serveraliveinterval
compression
forwardx11
>>> config['bitbucket.org']['ForwardX11']
'yes'
View Code

 

5.1 获取全部节点:

只能获取[]格式,做为节点项目。

import configparser
 
config = configparser.ConfigParser()
config.read('xxxooo', encoding='utf-8')
ret = config.sections()
print(ret)
View Code

5.2 获取指定节点下全部的键值对:

import configparser
 
config = configparser.ConfigParser()
config.read('xxxooo', encoding='utf-8')
ret = config.items('section1')
print(ret)
View Code

5.3 获取指定节点下全部的键:

import configparser
 
config = configparser.ConfigParser()
config.read('xxxooo', encoding='utf-8')
ret = config.options('section1')
print(ret)
View Code

5.4 获取指定节点下指定key的值

import configparser
 
config = configparser.ConfigParser()
config.read('xxxooo', encoding='utf-8')
 
 
v = config.get('section1', 'k1')
# v = config.getint('section1', 'k1')
# v = config.getfloat('section1', 'k1')
# v = config.getboolean('section1', 'k1')
 
print(v)
View Code

5.5 检查、删除、添加节点

import configparser
 
config = configparser.ConfigParser()
config.read('xxxooo', encoding='utf-8')
 
 
# 检查
has_sec = config.has_section('section1')
print(has_sec)
 
# 添加节点
config.add_section("SEC_1")
config.write(open('xxxooo', 'w'))
 
# 删除节点
config.remove_section("SEC_1")
config.write(open('xxxooo', 'w'))
View Code

5.6 检查、删除、设置指定组内的键值对

import configparser
 
config = configparser.ConfigParser()
config.read('xxxooo', encoding='utf-8')
 
# 检查
has_opt = config.has_option('section1', 'k1')
print(has_opt)
 
# 删除
config.remove_option('section1', 'k1')
config.write(open('xxxooo', 'w'))
 
# 设置
config.set('section1', 'k10', "123")
config.write(open('xxxooo', 'w'))
View Code

六、XML

XML是实现不一样语言或程序之间进行数据交换的协议,XML文件格式以下:

<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2023</year>
        <gdppc>141100</gdppc>
        <neighbor direction="E" name="Austria" />
        <neighbor direction="W" name="Switzerland" />
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2026</year>
        <gdppc>59900</gdppc>
        <neighbor direction="N" name="Malaysia" />
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2026</year>
        <gdppc>13600</gdppc>
        <neighbor direction="W" name="Costa Rica" />
        <neighbor direction="E" name="Colombia" />
    </country>
</data>

6.1 解析XML

from xml.etree import ElementTree as ET


# 打开文件,读取XML内容
str_xml = open('xo.xml', 'r').read()

# 将字符串解析成xml特殊对象,root代指xml文件的根节点
root = ET.XML(str_xml)
利用ElementTree.XML将字符串解析成xml对象
from xml.etree import ElementTree as ET

# 直接解析xml文件
tree = ET.parse("xo.xml")

# 获取xml文件的根节点
root = tree.getroot()
利用ElementTree.parse将文件直接解析成xml对象

6.2 操做XML

XML格式类型是节点嵌套节点,对于每个节点均有如下功能,以便对当前节点进行操做:

class Element:
    """An XML element.

    This class is the reference implementation of the Element interface.

    An element's length is its number of subelements.  That means if you
    want to check if an element is truly empty, you should check BOTH
    its length AND its text attribute.

    The element tag, attribute names, and attribute values can be either
    bytes or strings.

    *tag* is the element name.  *attrib* is an optional dictionary containing
    element attributes. *extra* are additional element attributes given as
    keyword arguments.

    Example form:
        <tag attrib>text<child/>...</tag>tail

    """

    当前节点的标签名
    tag = None
    """The element's name."""

    当前节点的属性

    attrib = None
    """Dictionary of the element's attributes."""

    当前节点的内容
    text = None
    """
    Text before first subelement. This is either a string or the value None.
    Note that if there is no text, this attribute may be either
    None or the empty string, depending on the parser.

    """

    tail = None
    """
    Text after this element's end tag, but before the next sibling element's
    start tag.  This is either a string or the value None.  Note that if there
    was no text, this attribute may be either None or an empty string,
    depending on the parser.

    """

    def __init__(self, tag, attrib={}, **extra):
        if not isinstance(attrib, dict):
            raise TypeError("attrib must be dict, not %s" % (
                attrib.__class__.__name__,))
        attrib = attrib.copy()
        attrib.update(extra)
        self.tag = tag
        self.attrib = attrib
        self._children = []

    def __repr__(self):
        return "<%s %r at %#x>" % (self.__class__.__name__, self.tag, id(self))

    def makeelement(self, tag, attrib):
        建立一个新节点
        """Create a new element with the same type.

        *tag* is a string containing the element name.
        *attrib* is a dictionary containing the element attributes.

        Do not call this method, use the SubElement factory function instead.

        """
        return self.__class__(tag, attrib)

    def copy(self):
        """Return copy of current element.

        This creates a shallow copy. Subelements will be shared with the
        original tree.

        """
        elem = self.makeelement(self.tag, self.attrib)
        elem.text = self.text
        elem.tail = self.tail
        elem[:] = self
        return elem

    def __len__(self):
        return len(self._children)

    def __bool__(self):
        warnings.warn(
            "The behavior of this method will change in future versions.  "
            "Use specific 'len(elem)' or 'elem is not None' test instead.",
            FutureWarning, stacklevel=2
            )
        return len(self._children) != 0 # emulate old behaviour, for now

    def __getitem__(self, index):
        return self._children[index]

    def __setitem__(self, index, element):
        # if isinstance(index, slice):
        #     for elt in element:
        #         assert iselement(elt)
        # else:
        #     assert iselement(element)
        self._children[index] = element

    def __delitem__(self, index):
        del self._children[index]

    def append(self, subelement):
        为当前节点追加一个子节点
        """Add *subelement* to the end of this element.

        The new element will appear in document order after the last existing
        subelement (or directly after the text, if it's the first subelement),
        but before the end tag for this element.

        """
        self._assert_is_element(subelement)
        self._children.append(subelement)

    def extend(self, elements):
        为当前节点扩展 n 个子节点
        """Append subelements from a sequence.

        *elements* is a sequence with zero or more elements.

        """
        for element in elements:
            self._assert_is_element(element)
        self._children.extend(elements)

    def insert(self, index, subelement):
        在当前节点的子节点中插入某个节点,即:为当前节点建立子节点,而后插入指定位置
        """Insert *subelement* at position *index*."""
        self._assert_is_element(subelement)
        self._children.insert(index, subelement)

    def _assert_is_element(self, e):
        # Need to refer to the actual Python implementation, not the
        # shadowing C implementation.
        if not isinstance(e, _Element_Py):
            raise TypeError('expected an Element, not %s' % type(e).__name__)

    def remove(self, subelement):
        在当前节点在子节点中删除某个节点
        """Remove matching subelement.

        Unlike the find methods, this method compares elements based on
        identity, NOT ON tag value or contents.  To remove subelements by
        other means, the easiest way is to use a list comprehension to
        select what elements to keep, and then use slice assignment to update
        the parent element.

        ValueError is raised if a matching element could not be found.

        """
        # assert iselement(element)
        self._children.remove(subelement)

    def getchildren(self):
        获取全部的子节点(废弃)
        """(Deprecated) Return all subelements.

        Elements are returned in document order.

        """
        warnings.warn(
            "This method will be removed in future versions.  "
            "Use 'list(elem)' or iteration over elem instead.",
            DeprecationWarning, stacklevel=2
            )
        return self._children

    def find(self, path, namespaces=None):
        获取第一个寻找到的子节点
        """Find first matching element by tag name or path.

        *path* is a string having either an element tag or an XPath,
        *namespaces* is an optional mapping from namespace prefix to full name.

        Return the first matching element, or None if no element was found.

        """
        return ElementPath.find(self, path, namespaces)

    def findtext(self, path, default=None, namespaces=None):
        获取第一个寻找到的子节点的内容
        """Find text for first matching element by tag name or path.

        *path* is a string having either an element tag or an XPath,
        *default* is the value to return if the element was not found,
        *namespaces* is an optional mapping from namespace prefix to full name.

        Return text content of first matching element, or default value if
        none was found.  Note that if an element is found having no text
        content, the empty string is returned.

        """
        return ElementPath.findtext(self, path, default, namespaces)

    def findall(self, path, namespaces=None):
        获取全部的子节点
        """Find all matching subelements by tag name or path.

        *path* is a string having either an element tag or an XPath,
        *namespaces* is an optional mapping from namespace prefix to full name.

        Returns list containing all matching elements in document order.

        """
        return ElementPath.findall(self, path, namespaces)

    def iterfind(self, path, namespaces=None):
        获取全部指定的节点,并建立一个迭代器(能够被for循环)
        """Find all matching subelements by tag name or path.

        *path* is a string having either an element tag or an XPath,
        *namespaces* is an optional mapping from namespace prefix to full name.

        Return an iterable yielding all matching elements in document order.

        """
        return ElementPath.iterfind(self, path, namespaces)

    def clear(self):
        清空节点
        """Reset element.

        This function removes all subelements, clears all attributes, and sets
        the text and tail attributes to None.

        """
        self.attrib.clear()
        self._children = []
        self.text = self.tail = None

    def get(self, key, default=None):
        获取当前节点的属性值
        """Get element attribute.

        Equivalent to attrib.get, but some implementations may handle this a
        bit more efficiently.  *key* is what attribute to look for, and
        *default* is what to return if the attribute was not found.

        Returns a string containing the attribute value, or the default if
        attribute was not found.

        """
        return self.attrib.get(key, default)

    def set(self, key, value):
        为当前节点设置属性值
        """Set element attribute.

        Equivalent to attrib[key] = value, but some implementations may handle
        this a bit more efficiently.  *key* is what attribute to set, and
        *value* is the attribute value to set it to.

        """
        self.attrib[key] = value

    def keys(self):
        获取当前节点的全部属性的 key

        """Get list of attribute names.

        Names are returned in an arbitrary order, just like an ordinary
        Python dict.  Equivalent to attrib.keys()

        """
        return self.attrib.keys()

    def items(self):
        获取当前节点的全部属性值,每一个属性都是一个键值对
        """Get element attributes as a sequence.

        The attributes are returned in arbitrary order.  Equivalent to
        attrib.items().

        Return a list of (name, value) tuples.

        """
        return self.attrib.items()

    def iter(self, tag=None):
        在当前节点的子孙中根据节点名称寻找全部指定的节点,并返回一个迭代器(能够被for循环)。
        """Create tree iterator.

        The iterator loops over the element and all subelements in document
        order, returning all elements with a matching tag.

        If the tree structure is modified during iteration, new or removed
        elements may or may not be included.  To get a stable set, use the
        list() function on the iterator, and loop over the resulting list.

        *tag* is what tags to look for (default is to return all elements)

        Return an iterator containing all the matching elements.

        """
        if tag == "*":
            tag = None
        if tag is None or self.tag == tag:
            yield self
        for e in self._children:
            yield from e.iter(tag)

    # compatibility
    def getiterator(self, tag=None):
        # Change for a DeprecationWarning in 1.4
        warnings.warn(
            "This method will be removed in future versions.  "
            "Use 'elem.iter()' or 'list(elem.iter())' instead.",
            PendingDeprecationWarning, stacklevel=2
        )
        return list(self.iter(tag))

    def itertext(self):
        在当前节点的子孙中根据节点名称寻找全部指定的节点的内容,并返回一个迭代器(能够被for循环)。
        """Create text iterator.

        The iterator loops over the element and all subelements in document
        order, returning all inner text.

        """
        tag = self.tag
        if not isinstance(tag, str) and tag is not None:
            return
        if self.text:
            yield self.text
        for e in self:
            yield from e.itertext()
            if e.tail:
                yield e.tail
节点功能一览表

因为 每一个节点 都具备以上的方法,而且在上一步骤中解析时均获得了root(xml文件的根节点),so   能够利用以上方法进行操做xml文件。

a. 遍历XML文档的全部内容

from xml.etree import ElementTree as ET

############ 解析方式一 ############
"""
# 打开文件,读取XML内容
str_xml = open('xo.xml', 'r').read()

# 将字符串解析成xml特殊对象,root代指xml文件的根节点
root = ET.XML(str_xml)
"""
############ 解析方式二 ############

# 直接解析xml文件
tree = ET.parse("xo.xml")

# 获取xml文件的根节点
root = tree.getroot()


### 操做

# 顶层标签
print(root.tag)


# 遍历XML文档的第二层
for child in root:
    # 第二层节点的标签名称和标签属性
    print(child.tag, child.attrib)
    # 遍历XML文档的第三层
    for i in child:
        # 第二层节点的标签名称和内容
        print(i.tag,i.text)
View Code

b、遍历XML中指定的节点

from xml.etree import ElementTree as ET

############ 解析方式一 ############
"""
# 打开文件,读取XML内容
str_xml = open('xo.xml', 'r').read()

# 将字符串解析成xml特殊对象,root代指xml文件的根节点
root = ET.XML(str_xml)
"""
############ 解析方式二 ############

# 直接解析xml文件
tree = ET.parse("xo.xml")

# 获取xml文件的根节点
root = tree.getroot()


### 操做

# 顶层标签
print(root.tag)


# 遍历XML中全部的year节点
for node in root.iter('year'):
    # 节点的标签名称和内容
    print(node.tag, node.text)
View Code

c、修改节点内容

因为修改的节点时,均是在内存中进行,其不会影响文件中的内容。因此,若是想要修改,则须要从新将内存中的内容写到文件。

from xml.etree import ElementTree as ET

############ 解析方式一 ############

# 打开文件,读取XML内容
str_xml = open('xo.xml', 'r').read()

# 将字符串解析成xml特殊对象,root代指xml文件的根节点
root = ET.XML(str_xml)

############ 操做 ############

# 顶层标签
print(root.tag)

# 循环全部的year节点
for node in root.iter('year'):
    # 将year节点中的内容自增一
    new_year = int(node.text) + 1
    node.text = str(new_year)

    # 设置属性
    node.set('name', 'alex')
    node.set('age', '18')
    # 删除属性
    del node.attrib['name']


############ 保存文件 ############
tree = ET.ElementTree(root)
tree.write("newnew.xml", encoding='utf-8')
解析字符串方式,修改,保存
from xml.etree import ElementTree as ET

############ 解析方式二 ############

# 直接解析xml文件
tree = ET.parse("xo.xml")

# 获取xml文件的根节点
root = tree.getroot()

############ 操做 ############

# 顶层标签
print(root.tag)

# 循环全部的year节点
for node in root.iter('year'):
    # 将year节点中的内容自增一
    new_year = int(node.text) + 1
    node.text = str(new_year)

    # 设置属性
    node.set('name', 'alex')
    node.set('age', '18')
    # 删除属性
    del node.attrib['name']


############ 保存文件 ############
tree.write("newnew.xml", encoding='utf-8')
解析文件方式,修改,保存

d、删除节点

from xml.etree import ElementTree as ET

############ 解析字符串方式打开 ############

# 打开文件,读取XML内容
str_xml = open('xo.xml', 'r').read()

# 将字符串解析成xml特殊对象,root代指xml文件的根节点
root = ET.XML(str_xml)

############ 操做 ############

# 顶层标签
print(root.tag)

# 遍历data下的全部country节点
for country in root.findall('country'):
    # 获取每个country节点下rank节点的内容
    rank = int(country.find('rank').text)

    if rank > 50:
        # 删除指定country节点
        root.remove(country)

############ 保存文件 ############
tree = ET.ElementTree(root)
tree.write("newnew.xml", encoding='utf-8')
解析字符串方式,删除,保存
from xml.etree import ElementTree as ET

############ 解析文件方式 ############

# 直接解析xml文件
tree = ET.parse("xo.xml")

# 获取xml文件的根节点
root = tree.getroot()

############ 操做 ############

# 顶层标签
print(root.tag)

# 遍历data下的全部country节点
for country in root.findall('country'):
    # 获取每个country节点下rank节点的内容
    rank = int(country.find('rank').text)

    if rank > 50:
        # 删除指定country节点
        root.remove(country)

############ 保存文件 ############
tree.write("newnew.xml", encoding='utf-8')
解析文件方式打开,删除,保存

6.3 建立XML文档

from xml.etree import ElementTree as ET


# 建立根节点
root = ET.Element("famliy")


# 建立节点大儿子
son1 = ET.Element('son', {'name': '儿1'})
# 建立小儿子
son2 = ET.Element('son', {"name": '儿2'})

# 在大儿子中建立两个孙子
grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson2 = ET.Element('grandson', {'name': '儿12'})
son1.append(grandson1)
son1.append(grandson2)


# 把儿子添加到根节点中
root.append(son1)
root.append(son1)

tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
方式一
from xml.etree import ElementTree as ET

# 建立根节点
root = ET.Element("famliy")


# 建立大儿子
# son1 = ET.Element('son', {'name': '儿1'})
son1 = root.makeelement('son', {'name': '儿1'})
# 建立小儿子
# son2 = ET.Element('son', {"name": '儿2'})
son2 = root.makeelement('son', {"name": '儿2'})

# 在大儿子中建立两个孙子
# grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson1 = son1.makeelement('grandson', {'name': '儿11'})
# grandson2 = ET.Element('grandson', {'name': '儿12'})
grandson2 = son1.makeelement('grandson', {'name': '儿12'})

son1.append(grandson1)
son1.append(grandson2)


# 把儿子添加到根节点中
root.append(son1)
root.append(son1)

tree = ET.ElementTree(root)
tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
方式二
from xml.etree import ElementTree as ET


# 建立根节点
root = ET.Element("famliy")


# 建立节点大儿子
son1 = ET.SubElement(root, "son", attrib={'name': '儿1'})
# 建立小儿子
son2 = ET.SubElement(root, "son", attrib={"name": "儿2"})

# 在大儿子中建立一个孙子
grandson1 = ET.SubElement(son1, "age", attrib={'name': '儿11'})
grandson1.text = '孙子'


et = ET.ElementTree(root)  #生成文档对象
et.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False)
方式三

因为原生保存的XML时默认无缩进,若是想要设置缩进的话, 须要修改保存方式:

from xml.etree import ElementTree as ET
from xml.dom import minidom


def prettify(elem):
    """将节点转换成字符串,并添加缩进。
    """
    rough_string = ET.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="\t")

# 建立根节点
root = ET.Element("famliy")


# 建立大儿子
# son1 = ET.Element('son', {'name': '儿1'})
son1 = root.makeelement('son', {'name': '儿1'})
# 建立小儿子
# son2 = ET.Element('son', {"name": '儿2'})
son2 = root.makeelement('son', {"name": '儿2'})

# 在大儿子中建立两个孙子
# grandson1 = ET.Element('grandson', {'name': '儿11'})
grandson1 = son1.makeelement('grandson', {'name': '儿11'})
# grandson2 = ET.Element('grandson', {'name': '儿12'})
grandson2 = son1.makeelement('grandson', {'name': '儿12'})

son1.append(grandson1)
son1.append(grandson2)


# 把儿子添加到根节点中
root.append(son1)
root.append(son1)


raw_str = prettify(root)

f = open("xxxoo.xml",'w',encoding='utf-8')
f.write(raw_str)
f.close()
缩进功能

6.4 命名空间

详细介绍,猛击这里

 

7.requests

Python标准库中提供了:urllib等模块以供Http请求,可是,它的 API 太渣了。它是为另外一个时代、另外一个互联网所建立的。它须要巨量的工做,甚至包括各类方法覆盖,来完成最简单的任务。

7.1发送GET请求

import urllib.request

f = urllib.request.urlopen('http://www.webxml.com.cn//webservices/qqOnlineWebService.asmx/qqCheckOnline?qqCode=424662508')
result = f.read().decode('utf-8')

7.2 发送携带请求头的GET请求:

import urllib.request

req = urllib.request.Request('http://www.example.com/')
req.add_header('Referer', 'http://www.python.org/')
r = urllib.request.urlopen(req)

result = f.read().decode('utf-8')

注:更多见Python官方文档:https://docs.python.org/3.5/library/urllib.request.html#module-urllib.request

Requests 是使用 Apache2 Licensed 许可证的 基于Python开发的HTTP 库,其在Python内置模块的基础上进行了高度的封装,从而使得Pythoner进行网络请求时,变得美好了许多,使用Requests能够垂手可得的完成浏览器可有的任何操做。

一、安装模块

pip3 install requests

二、使用模块 GET请求

# 一、无参数实例
 
import requests
 
ret = requests.get('https://github.com/timeline.json')
 
print(ret.url)
print(ret.text)
 
 
 
# 二、有参数实例
 
import requests
 
payload = {'key1': 'value1', 'key2': 'value2'}
ret = requests.get("http://httpbin.org/get", params=payload)
 
print(ret.url)
print(ret.text)
GET请求
# 一、基本POST实例
 
import requests
 
payload = {'key1': 'value1', 'key2': 'value2'}
ret = requests.post("http://httpbin.org/post", data=payload)
 
print(ret.text)
 
 
# 二、发送请求头和数据实例
 
import requests
import json
 
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}
 
ret = requests.post(url, data=json.dumps(payload), headers=headers)
 
print(ret.text)
print(ret.cookies)
POST请求
requests.get(url, params=None, **kwargs)
requests.post(url, data=None, json=None, **kwargs)
requests.put(url, data=None, **kwargs)
requests.head(url, **kwargs)
requests.delete(url, **kwargs)
requests.patch(url, data=None, **kwargs)
requests.options(url, **kwargs)
 
# 以上方法均是在此方法的基础上构建
requests.request(method, url, **kwargs)
其余请求

更多requests模块相关的文档见:http://cn.python-requests.org/zh_CN/latest/

三、Http请求和XML实例

实例:检测QQ帐号是否在线

import urllib
import requests
from xml.etree import ElementTree as ET

# 使用内置模块urllib发送HTTP请求,或者XML格式内容
"""
f = urllib.request.urlopen('http://www.webxml.com.cn//webservices/qqOnlineWebService.asmx/qqCheckOnline?qqCode=424662508')
result = f.read().decode('utf-8')
"""


# 使用第三方模块requests发送HTTP请求,或者XML格式内容
r = requests.get('http://www.webxml.com.cn//webservices/qqOnlineWebService.asmx/qqCheckOnline?qqCode=424662508')
result = r.text

# 解析XML格式内容
node = ET.XML(result)

# 获取内容
if node.text == "Y":
    print("在线")
else:
    print("离线")
View Code

实例:查看火车停靠信息

import urllib
import requests
from xml.etree import ElementTree as ET

# 使用内置模块urllib发送HTTP请求,或者XML格式内容
"""
f = urllib.request.urlopen('http://www.webxml.com.cn/WebServices/TrainTimeWebService.asmx/getDetailInfoByTrainCode?TrainCode=G666&UserID=')
result = f.read().decode('utf-8')
"""

# 使用第三方模块requests发送HTTP请求,或者XML格式内容
r = requests.get('http://www.webxml.com.cn/WebServices/TrainTimeWebService.asmx/getDetailInfoByTrainCode?TrainCode=G666&UserID=')
result = r.text

# 解析XML格式内容
root = ET.XML(result)
for node in root.iter('TrainDetailInfo'):
    print(node.find('TrainStation').text,node.find('StartTime').text,node.tag,node.attrib)
View Code

注:更多接口猛击这里

 八、loggin模块

用于便捷记录日志且线程安全的模块

 8.1单文件日志

import logging
  
  
logging.basicConfig(filename='log.log',
                    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',
                    level=10)
  
logging.debug('debug')
logging.info('info')
logging.warning('warning')
logging.error('error')
logging.critical('critical')
logging.log(10,'log')
View Code

最简单用法:

import logging
 
logging.warning("user [alex] attempted wrong password more than 3 times")
logging.critical("server is down")
 
#输出
WARNING:root:user [alex] attempted wrong password more than 3 times
CRITICAL:root:server is down
View Code

 

日志等级:

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

注:只有【当前写等级】大于【日志等级】时,日志文件才被记录。

日志记录格式:

日志格式

%(name)s

Logger的名字

%(levelno)s

数字形式的日志级别

%(levelname)s

文本形式的日志级别

%(pathname)s

调用日志输出函数的模块的完整路径名,可能没有

%(filename)s

调用日志输出函数的模块的文件名

%(module)s

调用日志输出函数的模块名

%(funcName)s

调用日志输出函数的函数名

%(lineno)d

调用日志输出函数的语句所在的代码行

%(created)f

当前时间,用UNIX标准的表示时间的浮 点数表示

%(relativeCreated)d

输出日志信息时的,自Logger建立以 来的毫秒数

%(asctime)s

字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒

%(thread)d

线程ID。可能没有

%(threadName)s

线程名。可能没有

%(process)d

进程ID。可能没有

%(message)s

用户输出的消息

8.2 多文件日志

对于上述记录日志的功能,只能将日志记录在单文件中,若是想要设置多个日志文件,logging.basicConfig将没法完成,须要自定义文件和日志操做对象。

# 定义文件
file_1_1 = logging.FileHandler('l1_1.log', 'a', encoding='utf-8')
fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s")
file_1_1.setFormatter(fmt)

file_1_2 = logging.FileHandler('l1_2.log', 'a', encoding='utf-8')
fmt = logging.Formatter()
file_1_2.setFormatter(fmt)

# 定义日志
logger1 = logging.Logger('s1', level=logging.ERROR)
logger1.addHandler(file_1_1)
logger1.addHandler(file_1_2)


# 写日志
logger1.critical('1111')
日志一
# 定义文件
file_2_1 = logging.FileHandler('l2_1.log', 'a')
fmt = logging.Formatter()
file_2_1.setFormatter(fmt)

# 定义日志
logger2 = logging.Logger('s2', level=logging.INFO)
logger2.addHandler(file_2_1)
日志二

如上述建立的两个日志对象

  • 当使用【logger1】写日志时,会将相应的内容写入 l1_1.log 和 l1_2.log 文件中
  • 当使用【logger2】写日志时,会将相应的内容写入 l2_1.log 文件中

若是想同时把log打印在屏幕和文件日志里,就须要了解一点复杂的知识 了


Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的归纳最为合适:

logger提供了应用程序能够直接使用的接口;

handler将(logger建立的)日志记录发送到合适的目的输出;

filter提供了细度设备来决定输出哪条日志记录;

formatter决定日志记录的最终输出格式。

logger
每一个程序在输出信息以前都要得到一个Logger。Logger一般对应了程序的模块名,好比聊天工具的图形界面模块能够这样得到它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模块能够这样:
LOG=logging.getLogger(”chat.kernel”)

Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增长或删除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():能够设置的日志级别

handler

handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可使用。有些Handler能够把信息输出到控制台,有些Logger能够把信息输出到文件,还有些 Handler能够把信息发送到网络上。若是以为不够用,还能够编写本身的Handler。能够经过addHandler()方法添加多个多handler
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象


每一个Logger能够附加多个Handler。接下来咱们就来介绍一些经常使用的Handler:
1) logging.StreamHandler
使用这个Handler能够向相似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr


2) logging.FileHandler
和StreamHandler相似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名。
mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。

3) logging.handlers.RotatingFileHandler
这个Handler相似于上面的FileHandler,可是它能够管理文件大小。当文件达到必定大小以后,它会自动将当前日志文件更名,而后建立 一个新的同名日志文件继续输出。好比日志文件是chat.log。当chat.log达到指定的大小以后,RotatingFileHandler自动把 文件更名为chat.log.1。不过,若是chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后从新建立 chat.log,继续输出日志信息。它的构造函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler同样。
maxBytes用于指定日志文件的最大文件大小。若是maxBytes为0,意味着日志文件能够无限大,这时上面描述的重命名过程就不会发生。
backupCount用于指定保留的备份文件的个数。好比,若是指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被改名,而是被删除。


4) logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler相似,不过,它没有经过判断文件大小来决定什么时候从新建立日志文件,而是间隔必定时间就 自动建立新的日志文件。重命名的过程与RotatingFileHandler相似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具备相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有如下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时表明星期一)
midnight 天天凌晨

import logging
 
#create logger
logger = logging.getLogger('TEST-LOG')
logger.setLevel(logging.DEBUG)
 
 
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
 
# create file handler and set level to warning
fh = logging.FileHandler("access.log")
fh.setLevel(logging.WARNING)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
# add formatter to ch and fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)
 
# add ch and fh to logger
logger.addHandler(ch)
logger.addHandler(fh)
 
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
View Code

文件自动截断例子

import logging

from logging import handlers

logger = logging.getLogger(__name__)

log_file = "timelog.log"
#fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3)
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)


formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')

fh.setFormatter(formatter)

logger.addHandler(fh)


logger.warning("test1")
logger.warning("test12")
logger.warning("test13")
logger.warning("test14")
View Code

 

 

九、系统命令,subprocess模块:

能够执行shell命令的相关模块和函数有:

  • os.system
  • os.spawn*
  • os.popen*          --废弃
  • popen2.*           --废弃
  • commands.*      --废弃,3.x中被移除
import commands

result = commands.getoutput('cmd')
result = commands.getstatus('cmd')
result = commands.getstatusoutput('cmd')
commands
以上执行shell命令的相关的模块和函数的功能均在 subprocess 模块中实现,并提供了更丰富的功能。

call 

执行命令,返回状态码

ret = subprocess.call(["ls", "-l"], shell=False)
ret = subprocess.call("ls -l", shell=True)

check_call

执行命令,若是执行状态码是 0 ,则返回0,不然抛异常

subprocess.check_call(["ls", "-l"])
subprocess.check_call("exit 1", shell=True)

check_output

执行命令,若是状态码是 0 ,则返回执行结果,不然抛异常

subprocess.check_output(["echo", "Hello World!"])
subprocess.check_output("exit 1", shell=True)

subprocess.Popen(...)

用于执行复杂的系统命令

参数:

    • args:shell命令,能够是字符串或者序列类型(如:list,元组)
    • bufsize:指定缓冲。0 无缓冲,1 行缓冲,其余 缓冲区大小,负值 系统缓冲
    • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
    • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行以前被调用
    • close_sfs:在windows平台下,若是close_fds被设置为True,则新建立的子进程将不会继承父进程的输入、输出、错误管道。
      因此不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
    • shell:同上
    • cwd:用于设置子进程的当前目录
    • env:用于指定子进程的环境变量。若是env = None,子进程的环境变量将从父进程中继承。
    • universal_newlines:不一样系统的换行符不一样,True -> 赞成使用 \n
    • startupinfo与createionflags只在windows下有效
      将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等 
import subprocess
ret1 = subprocess.Popen(["mkdir","t1"])
ret2 = subprocess.Popen("mkdir t2", shell=True)

终端输入的命令分为两种:

  • 输入便可获得输出,如:ifconfig
  • 输入进行某环境,依赖再输入,如:python
1.import subprocess

obj = subprocess.Popen("mkdir t3", shell=True, cwd='/home/dev',)

2.import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")
obj.stdin.close()

cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close()

print(cmd_out)
print(cmd_error)

3.import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")

out_error_list = obj.communicate()
print(out_error_list)

4.import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
out_error_list = obj.communicate('print("hello")')
print(out_error_list)
实例代码

 

十、shutil

高级的 文件、文件夹、压缩包 处理模块

shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另外一个文件中

import shutil
 
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)
拷贝文件

shutil.copyfile('f1.log', 'f2.log')

shutil.copymode(src, dst)

仅拷贝权限。内容、组、用户均不变

shutil.copymode('f1.log', 'f2.log')

shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

shutil.copystat('f1.log', 'f2.log')

shutil.copy(src, dst)
拷贝文件和权限

import shutil
 
shutil.copy('f1.log', 'f2.log')

shutil.copy2(src, dst)拷贝文件和状态信息

import shutil
 
shutil.copy2('f1.log', 'f2.log')

shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹

import shutil
 
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))

import shutil

shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))

shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件

import shutil
 
shutil.rmtree('folder1')

shutil.move(src, dst)
递归的去移动文件,它相似mv命令,其实就是重命名。

import shutil
 
shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,...)

建立压缩包并返回文件路径,例如:zip、tar

建立压缩包并返回文件路径,例如:zip、tar

    • base_name: 压缩包的文件名,也能够是压缩包的路径。只是文件名时,则保存至当前目录,不然保存至指定路径,
      如:www                        =>保存至当前路径
      如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
    • format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
    • root_dir: 要压缩的文件夹路径(默认当前目录)
    • owner: 用户,默认当前用户
    • group: 组,默认当前组
    • logger: 用于记录日志,一般是logging.Logger对象
#将 /Users/wupeiqi/Downloads/test 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')
  
  
#将 /Users/wupeiqi/Downloads/test 下的文件打包放置 /Users/wupeiqi/目录
import shutil
ret = shutil.make_archive("/Users/wupeiqi/wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:

import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall()
z.close()
zipfile解压缩
import tarfile

# 压缩
tar = tarfile.open('your.tar','w')
tar.add('/Users/wupeiqi/PycharmProjects/bbs2.log', arcname='bbs2.log')
tar.add('/Users/wupeiqi/PycharmProjects/cmdb.log', arcname='cmdb.log')
tar.close()

# 解压
tar = tarfile.open('your.tar','r')
tar.extractall()  # 可设置解压地址
tar.close()
tarfile解压缩

十、PyYAML模块

Python也能够很容易的处理ymal文档格式,只不过须要安装一个模块,参考文档:http://pyyaml.org/wiki/PyYAMLDocumentation  

十一、paramiko

paramiko是一个用于作远程控制的模块,使用该模块能够对远程服务器进行命令或文件操做,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。

一、下载安装

pycrypto,因为 paramiko 模块内部依赖pycrypto,因此先下载安装pycrypto

pip3 install pycrypto
pip3 install paramiko
二、模块使用
#!/usr/bin/env python
#coding:utf-8

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.1.108', 22, 'alex', '123')
stdin, stdout, stderr = ssh.exec_command('df')
print stdout.read()
ssh.close();
执行命令 - 用户名+密码
import paramiko

private_key_path = '/home/auto/.ssh/id_rsa'
key = paramiko.RSAKey.from_private_key_file(private_key_path)

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('主机名 ', 端口, '用户名', key)

stdin, stdout, stderr = ssh.exec_command('df')
print stdout.read()
ssh.close()
执行命令 - 密钥
import os,sys
import paramiko

t = paramiko.Transport(('182.92.219.86',22))
t.connect(username='wupeiqi',password='123')
sftp = paramiko.SFTPClient.from_transport(t)
sftp.put('/tmp/test.py','/tmp/test.py') 
t.close()


import os,sys
import paramiko

t = paramiko.Transport(('182.92.219.86',22))
t.connect(username='wupeiqi',password='123')
sftp = paramiko.SFTPClient.from_transport(t)
sftp.get('/tmp/test.py','/tmp/test2.py')
t.close()
上传或下载文件 - 用户名+密码
import paramiko

pravie_key_path = '/home/auto/.ssh/id_rsa'
key = paramiko.RSAKey.from_private_key_file(pravie_key_path)

t = paramiko.Transport(('182.92.219.86',22))
t.connect(username='wupeiqi',pkey=key)

sftp = paramiko.SFTPClient.from_transport(t)
sftp.put('/tmp/test3.py','/tmp/test3.py') 

t.close()

import paramiko

pravie_key_path = '/home/auto/.ssh/id_rsa'
key = paramiko.RSAKey.from_private_key_file(pravie_key_path)

t = paramiko.Transport(('182.92.219.86',22))
t.connect(username='wupeiqi',pkey=key)

sftp = paramiko.SFTPClient.from_transport(t)
sftp.get('/tmp/test3.py','/tmp/test4.py') 

t.close()
上传或下载文件 - 密钥

十二、time模块

时间相关的操做,时间有三种表示方式:

  • 时间戳               1970年1月1日以后的秒,即:time.time()
  • 格式化的字符串    2014-11-11 11:11,    即:time.strftime('%Y-%m-%d')
  • 结构化时间          元组包含了:年、日、星期等... time.struct_time    即:time.localtime()
 
print time.time()
print time.mktime(time.localtime())
   
print time.gmtime()    #可加时间戳参数
print time.localtime() #可加时间戳参数
print time.strptime('2014-11-11', '%Y-%m-%d')
   
print time.strftime('%Y-%m-%d') #默认当前时间
print time.strftime('%Y-%m-%d',time.localtime()) #默认当前时间
print time.asctime()
print time.asctime(time.localtime())
print time.ctime(time.time())
   
import datetime
'''
datetime.date:表示日期的类。经常使用的属性有year, month, day
datetime.time:表示时间的类。经常使用的属性有hour, minute, second, microsecond
datetime.datetime:表示日期时间
datetime.timedelta:表示时间间隔,即两个时间点之间的长度
timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]])
strftime("%Y-%m-%d")
'''
import datetime
print datetime.datetime.now()
print datetime.datetime.now() - datetime.timedelta(days=5)
%Y  Year with century as a decimal number.
    %m  Month as a decimal number [01,12].
    %d  Day of the month as a decimal number [01,31].
    %H  Hour (24-hour clock) as a decimal number [00,23].
    %M  Minute as a decimal number [00,59].
    %S  Second as a decimal number [00,61].
    %z  Time zone offset from UTC.
    %a  Locale's abbreviated weekday name.
    %A  Locale's full weekday name.
    %b  Locale's abbreviated month name.
    %B  Locale's full month name.
    %c  Locale's appropriate date and time representation.
    %I  Hour (12-hour clock) as a decimal number [01,12].
    %p  Locale's equivalent of either AM or PM.
格式化点位符

一、经过HTTP请求和XML实现获取电视节目

     API:http://www.webxml.com.cn/webservices/ChinaTVprogramWebService.asmx  

二、经过HTTP请求和JSON实现获取天气情况

     API:http://wthrcdn.etouch.cn/weather_mini?city=北京

相关文章
相关标签/搜索