Python模块之psutil

[TOC] 用Python来编写脚本简化日常的运维工作是Python的一个重要用途。在Linux下,有许多系统命令可以让我们时刻监控系统运行的状态,如ps,top,free等等。要获取这些系统信息,Python可以通过subprocess模块调用并获取结果。但这样做显得很麻烦,尤其是要写很多解析代码。 在Python中获取系统信息的另一个好办法是使用psutil这个第三方模块。顾名思义,psutil = process and system utilities,它不仅可以通过一两行代码实现系统监控,还可以跨平台使用,支持Linux/UNIX/OSX/Windows等,是系统管理员和运维小伙伴不可或缺的必备模块。 安装psutil 如果安装了Anaconda,psutil就已经可用了。否则,需要在命令行下通过pip安装: $ pip install psutil 获取CPU信息 我们先来获取CPU的信息: >>> import psutil >>> psutil.cpu_count() # CPU逻辑数量 4 >>> psutil.cpu_count(logical=False) # CPU物理核心 2 # 2说明是双核超线程, 4则是4核非超线程 统计CPU的用户/系统/空闲时间: >>> psutil.cpu_times() scputimes(user=10963.31, nice=0.0, system=5138.67, idle=356102.45) 再实现类似top命令的CPU使用率,每秒刷新一次,累计10次: >>> for x in range(10): ... psutil.cpu_percent(interval=1, percpu=True) ... [14.0, 4.0, 4.0, 4.0] [12.0, 3.0, 4.0, 3.0] [8.0, 4.0, 3.0, 4.0] [12.0, 3.0, 3.0, 3.0] [18.8, 5.1, 5.9, 5.0] [10.9, 5.0, 4.0, 3.0] [12.0, 5.0, 4.0, 5.0] [15.0, 5.0, 4.0, 4.0] [19.0, 5.0, 5.0, 4.0] [9.0, 3.0, 2.0, 3.0] 获取内存信息 使用psutil获取物理内存和交换内存信息,分别使用: ...

June 26, 2016

Python模块之chardet

[TOC] 字符串编码一直是令人非常头疼的问题,尤其是我们在处理一些不规范的第三方网页的时候。虽然Python提供了Unicode表示的str和bytes两种数据类型,并且可以通过encode()和decode()方法转换,但是,在不知道编码的情况下,对bytes做decode()不好做。 对于未知编码的bytes,要把它转换成str,需要先“猜测”编码。猜测的方式是先收集各种编码的特征字符,根据特征字符判断,就能有很大概率“猜对”。 当然,我们肯定不能从头自己写这个检测编码的功能,这样做费时费力。chardet这个第三方库正好就派上了用场。用它来检测编码,简单易用。 安装chardet 如果安装了Anaconda,chardet就已经可用了。否则,需要在命令行下通过pip安装: $ pip install chardet 使用chardet 当我们拿到一个bytes时,就可以对其检测编码。用chardet检测编码,只需要一行代码: >>> chardet.detect(b'Hello, world!') {'encoding': 'ascii', 'confidence': 1.0, 'language': ''} 检测出的编码是ascii,注意到还有个confidence字段,表示检测的概率是1.0(即100%)。 我们来试试检测GBK编码的中文: >>> data = '离离原上草,一岁一枯荣'.encode('gbk') >>> chardet.detect(data) {'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'} 检测的编码是GB2312,注意到GBK是GB2312的超集,两者是同一种编码,检测正确的概率是74%,language字段指出的语言是'Chinese'。 对UTF-8编码进行检测: >>> data = '离离原上草,一岁一枯荣'.encode('utf-8') >>> chardet.detect(data) {'encoding': 'utf-8', 'confidence': 0.99, 'language': ''} 我们再试试对日文进行检测: >>> data = '最新の主要ニュース'.encode('euc-jp') >>> chardet.detect(data) {'encoding': 'EUC-JP', 'confidence': 0.99, 'language': 'Japanese'} 可见,用chardet检测编码,使用简单。获取到编码后,再转换为str,就可以方便后续处理。 chardet支持检测的编码列表请参考官方文档Supported encodings。 小结 使用chardet检测编码非常容易,chardet支持检测中文、日文、韩文等多种语言。

June 25, 2016

Python模块之requests

[TOC] Python内置的模块有urllib,用于访问网络资源。但是,它用起来比较麻烦,而且,缺少很多实用的高级功能。 更好的方案是使用requests。它是一个Python第三方库,处理URL资源特别方便。 安装requests 如果安装了Anaconda,requests就已经可用了。否则,需要在命令行下通过pip安装: $ pip install requests 使用requests 要通过GET访问一个页面,只需要几行代码: >>> import requests >>> r = requests.get('https://www.douban.com/') # 豆瓣首页 >>> r.status_code 200 >>> r.text r.text '<!DOCTYPE HTML>\n<html>\n<head>\n<meta name="description" content="提供图书、电影、音乐唱片的推荐、评论和...' 对于带参数的URL,传入一个dict作为params参数: >>> r = requests.get('https://www.douban.com/search', params={'q': 'python', 'cat': '1001'}) >>> r.url # 实际请求的URL 'https://www.douban.com/search?q=python&cat=1001' requests自动检测编码,可以使用encoding属性查看: >>> r.encoding 'utf-8' 无论响应是文本还是二进制内容,我们都可以用content属性获得bytes对象: >>> r.content b'<!DOCTYPE html>\n<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n...' requests的方便之处还在于,对于特定类型的响应,例如JSON,可以直接获取: >>> r = requests.get('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20%3D%202151330&format=json') >>> r.json() {'query': {'count': 1, 'created': '2017-11-17T07:14:12Z', ... 需要传入HTTP Header时,我们传入一个dict作为headers参数: >>> r = requests.get('https://www.douban.com/', headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit'}) >>> r.text '<!DOCTYPE html>\n<html>\n<head>\n<meta charset="UTF-8">\n <title>豆瓣(手机版)</title>...' 要发送POST请求,只需要把get()方法变成post(),然后传入data参数作为POST请求的数据: ...

June 24, 2016

Python模块之pillow

[TOC] 除了内建的模块外,Python还有大量的第三方模块,而且是相当相当的多。 基本上,所有的第三方模块都会在PyPI - the Python Package Index上注册,只要找到对应的模块名字,即可用pip安装。也可以安装Anaconda,安装后,数十个常用的第三方模块就已经就绪,不用pip手动安装。下面讲解一下pillow这个第三方模块. PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。 由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。 安装Pillow 如果安装了Anaconda,Pillow就已经可用了。否则,需要在命令行下通过pip安装: $ pip install pillow 操作图像 来看看最常见的图像缩放操作,只需三四行代码: from PIL import Image # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 获得图像尺寸: w, h = im.size print('Original image size: %sx%s' % (w, h)) # 缩放到50%: im.thumbnail((w//2, h//2)) print('Resize image to: %sx%s' % (w//2, h//2)) # 把缩放后的图像用jpeg格式保存: im.save('thumbnail.jpg', 'jpeg') 其他功能如切片、旋转、滤镜、输出文字、调色板等一应俱全。 比如,模糊效果也只需几行代码: from PIL import Image, ImageFilter # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 应用模糊滤镜: im2 = im.filter(ImageFilter.BLUR) im2.save('blur.jpg', 'jpeg') 效果如下: ...

June 23, 2016

Python内建模块HTMLParser

[TOC] 如果我们要编写一个搜索引擎,第一步是用爬虫把目标网站的页面抓下来,第二步就是解析该HTML页面,看看里面的内容到底是新闻、图片还是视频。 假设第一步已经完成了,第二步应该如何解析HTML呢? HTML本质上是XML的子集,但是HTML的语法没有XML那么严格,所以不能用标准的DOM或SAX来解析HTML。 好在Python提供了HTMLParser来非常方便地解析HTML,只需简单几行代码: 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&nbsp;tutorial...<br>END</p> </body></html>''') feed()方法可以多次调用,也就是不一定一次把整个HTML字符串都塞进去,可以一部分一部分塞进去。 ...

June 22, 2016

Python内建模块xml

[TOC] XML虽然比JSON复杂,在Web中应用也不如以前多了,不过仍有很多地方在用,所以,有必要了解如何操作XML。 DOM vs SAX 操作XML有两种方法:DOM和SAX。DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。SAX是流模式,边读边解析,占用内存小,解析快,缺点是我们需要自己处理事件。 正常情况下,优先考虑SAX,因为DOM实在太占内存。 在Python中使用SAX解析XML非常简洁,通常我们关心的事件是start_element,end_element和char_data,准备好这3个函数,然后就可以解析xml了。 举个例子,当SAX解析器读到一个节点时: <a href="/">python</a> 会产生3个事件: start_element事件,在读取<a href="/">时; char_data事件,在读取python时; end_element事件,在读取</a>时。 用代码实验一下: 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) 需要注意的是读取一大段字符串时,CharacterDataHandler可能被多次调用,所以需要自己保存起来,在EndElementHandler里面再合并。 ...

June 21, 2016

Python内建模块contextlib

[TOC] 在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源的一个方法是使用try...finally: try: f = open('/path/to/file', 'r') f.read() finally: if f: f.close() 写try...finally非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭,所以上面的代码可以简化为: with open('/path/to/file', 'r') as f: f.read() 并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。 实现上下文管理是通过__enter__和__exit__这两个方法实现的。例如,下面的class实现了这两个方法: 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语句: with Query('Bob') as q: q.query() @contextmanager 编写__enter__和__exit__仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法,上面的代码可以改写如下: 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') @contextmanager这个decorator接受一个generator,用yield语句把with ... as var把变量输出出去,然后,with语句就可以正常地工作了: ...

June 20, 2016

Python内建模块urllib

[TOC] urllib提供了一系列用于操作URL的功能。 Get urllib的request模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应: 例如,对豆瓣的一个URLhttps://api.douban.com/v2/book/2129650进行抓取,并返回响应: 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')) 可以看到HTTP响应的头和JSON数据: Status: 200 OK Server: nginx Date: Tue, 26 May 2015 10:02:27 GMT Content-Type: application/json; charset=utf-8 Content-Length: 2049 Connection: close Expires: Sun, 1 Jan 2006 01:00:00 GMT Pragma: no-cache Cache-Control: must-revalidate, no-cache, private X-DAE-Node: pidl1 Data: {"rating":{"max":10,"numRaters":16,"average":"7.4","min":0},"subtitle":"","author":["廖雪峰编著"],"pubdate":"2007-6",...} 如果我们要想模拟浏览器发送GET请求,就需要使用Request对象,通过往Request对象添加HTTP头,我们就可以把请求伪装成浏览器。例如,模拟iPhone 6去请求豆瓣首页: ...

June 20, 2016

Python内建模块itertools

[TOC] Python的内建模块itertools提供了非常有用的用于操作迭代对象的函数。 首先,我们看看itertools提供的几个“无限”迭代器: >>> import itertools >>> natuals = itertools.count(1) >>> for n in natuals: ... print(n) ... 1 2 3 ... 因为count()会创建一个无限的迭代器,所以上述代码会打印出自然数序列,根本停不下来,只能按Ctrl+C退出。 cycle()会把传入的一个序列无限重复下去: >>> import itertools >>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种 >>> for c in cs: ... print(c) ... 'A' 'B' 'C' 'A' 'B' 'C' ... 同样停不下来。 repeat()负责把一个元素无限重复下去,不过如果提供第二个参数就可以限定重复次数: >>> ns = itertools.repeat('A', 3) >>> for n in ns: ... print(n) ... A A A 无限序列只有在for迭代时才会无限地迭代下去,如果只是创建了一个迭代对象,它不会事先把无限个元素生成出来,事实上也不可能在内存中创建无限多个元素。 无限序列虽然可以无限迭代下去,但是通常我们会通过takewhile()等函数根据条件判断来截取出一个有限的序列: >>> 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提供的几个迭代器操作函数更加有用: ...

June 19, 2016

Python内建模块hashlib

[TOC] 摘要算法简介 Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。 什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。 举个例子,你写了一篇文章,内容是一个字符串'how to use python hashlib - by Michael',并附上这篇文章的摘要是'2d73d4f15c0db7f5ecb321b6a65e5d6d'。如果有人篡改了你的文章,并发表为'how to use python hashlib - by Bob',你可以一下子指出Bob篡改了你的文章,因为根据'how to use python hashlib - by Bob'计算出的摘要不同于原始文章的摘要。 可见,摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。 摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同,这种现象称为雪崩效应。 我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值: import hashlib md5 = hashlib.md5() md5.update('how to use md5 in python hashlib?'.encode('utf-8')) print(md5.hexdigest()) 计算结果如下: d26a53750bc40b38b65a520292f69306 如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的: import hashlib md5 = hashlib.md5() md5.update('how to use md5 in '.encode('utf-8')) md5.update('python hashlib?'.encode('utf-8')) print(md5.hexdigest()) 试试改动一个字母,看看计算的结果是否完全不同。 MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。 另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似: import hashlib sha1 = hashlib.sha1() sha1.update('how to use sha1 in '.encode('utf-8')) sha1.update('python hashlib?'.encode('utf-8')) print(sha1.hexdigest()) SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。 ...

June 18, 2016