精通Python自动化脚本-运维人员宝典完整目录:
第一章 Python脚本概述
第二章 Python脚本调试和性能测试
第三章 单元测试-单元测试框架的介绍
第四章 自动化常规运维活动
第五章 文件、目录和数据处理
第六章 文件存档、加密和解密
第七章 文本处理和正则表达式
第八章 文档和报告
第九章 操作各类文件
第十章 网络基础 – Socket编程
第十一章 使用Python脚本处理邮件
第十二章 使用Telnet和SSH远程监控主机
第十三章 创建图形化用户界面
第十四章 处理Apache和其它的日志文件
第十五章 SOAP和REST API通讯
第十六章 网络抓取 – 从网站上提取有用的信息
第十七章 数据收集及报表
第十八章 MySQL和SQLite数据库管理
本章中我们将学习sockets和三种互联网协议:http, ftplib和urllib。我们会学习Python中用于网络的socket模块。http是一个用于处理超文本传输协议的包。ftplib用于执行自动化的FTP相关工作。urllib是一个处理URL相关工作的包。
本章中我们将学习如下内容:
- Sockets
- http包
- ftplib模块
- urllib包
Socket接口
这一部分中, 我们将学习socket(套接字接口)的知识。我们将使用Python的socket模块。socket是本地或远程机器间通过的端点(endpoint)。socket模块有一个socket类,用于处理数据通道。它还包含网络相关操作的函数。要使用socket模块的功能,首先我们要导入socket模块。
我们来看看如何创建一个socket。socket类有一个socket函数,带有两个参数:address_family和socket类型。
语法如下:
1 2 |
import socket s = socket.socket(address_family, socket type) |
address_family控制OSI(Open System Interconnection 开放式系统互联)网络分层协议
socket type控制传输层协议
Python支持三种地址类型:AF_INET, AF_INET6和AF_UNIX。最常用的为AF_INET,用于因特网寻址。AF_INET6用于IPv6因特网寻址。AF_UNIX用于Unix域套接字(Unix Domain Sockets – UDS),是一种跨进程通讯协议。
有两种socket类型:SOCK_DGRAM和SOCK_STREAM。SOCK_DGRAM套接字类型用于面向消息的datagram传输,这些与UDP协议相关联。datagram套接字投递单个消息。SOCK_STREAM处理面向数据流的传输,与TCP协议相关联。流套接字接口在客户端和服务器端之间提供字节流。
socket可配置为服务端和客户端接口。在TCP/IP套接字接口都连接时,通讯是双向的。下面我们来探讨一个客户端-服务端通讯的示例。我们会创建两个脚本文件:server.py和client.py。
server.py脚本内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import socket host_name = socket.gethostname() port = 5000 s_socket = socket.socket() s_socket.bind((host_name, port)) s_socket.listen(2) conn, address = s_socket.accept() print("Connection from: " + str(address)) while True: recv_data = conn.recv(1024).decode() if not recv_data: break print("from connected user: " + str(recv_data)) recv_data = input(' -> ') conn.send(recv_data.encode()) conn.close() |
下面我们来编写客户端脚本。client.py脚本内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import socket host_name = socket.gethostname() port = 5000 c_socket = socket.socket() c_socket.connect((host_name, port)) msg = input(" -> ") while msg.lower().strip() != 'bye': c_socket.send(msg.encode()) recv_data = c_socket.recv(1024).decode() print('Received from server: ' + recv_data) msg = input(" -> ") c_socket.close() |
下面我们要在两个不同的终端中运行这两个程序。第一个终端中运行server.py,在第二个终端中运行client.py。
输出结果如下:
http包
这部分中,我们将学习http包相关知识。http包有四个模块:
- http.client: 这是一个底层HTTP协议客户端
- http.server: 包含基本HTTP服务器类
- http.cookies: 用于实现带cookie的状态管理
- http.cookiejar: 该模块提供cookie持久化
这一部分中,我们将学习http.client和http.server模块。
http.client模块
我们将来看两种http请求:GET和POST。我们还会来做一个http连接。
首先,我们来探讨一个进行http连接的示例。为此创建一个脚本make_connection.py并在其中编写如下内容:
1 2 3 4 |
import http.client con_obj = http.client.HTTPConnection("https://www.baidu.com", 80, timeout=20) print(con_obj) |
运行脚本,我们将会得到如下输出:
1 2 |
$ python3 make_connection.py <http.client.HTTPConnection object at 0x7f4f0d6e0438> |
在上例中,我们对传入的 URL 的80端口以一个指定的超时时间进行了连接。
下面我们来看 http GET请求方法,使用GET请求方法我们可以看一个获取返回码以及头部列表的示例。创建一个脚本get_example.py并编写如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 |
import http.client con_obj = http.client.HTTPSConnection("www.imdb.com") con_obj.request("GET", "/") response = con_obj.getresponse() print("Status: {}".format(response.status)) headers_list = response.getheaders() print("Headers: {}".format(headers_list)) con_obj.close() |
运行脚本如下:
1 |
$ python3 get_example.py |
得到的结果如下:
1 2 |
Status: 200 Headers: [('Content-Type', 'text/html;charset=UTF-8'), ('Transfer-Encoding', 'chunked'), ('Connection', 'keep-alive'), ('Server', 'Server'), ('Date', 'Sun, 10 Mar 2019 23:38:15 GMT'), ('Strict-Transport-Security', 'max-age=47474747; includeSubDomains; preload'), ('X-Frame-Options', 'SAMEORIGIN'), ('Content-Security-Policy', "frame-ancestors 'self' imdb.com *.imdb.com *.media-imdb.com withoutabox.com *.withoutabox.com amazon.com *.amazon.com amazon.co.uk *.amazon.co.uk amazon.de *.amazon.de translate.google.com images.google.com www.google.com www.google.co.uk search.aol.com bing.com www.bing.com"), ('Ad-Unit', 'imdb.home.homepage'), ('Entity-Id', ''), ('Section-Id', 'homepage'), ('Page-Id', 'homepage'), ('Content-Language', 'en-US'), ('Set-Cookie', 'uu=BCYt02mIaqWrtBRATN2QI_74kecgylRA4PBQ84rOmTRgH0lzs_-_4vqASi1sM9EiaFzDTa_vd9WX%0D%0AtQGMqLFOWs4cz8_neIhv1zjnL_V9aq4mq6UUCO4DC9ysALBFhJPLzxgCrBTR7IdTK0Aw4fWCzb2g%0D%0AkA%0D%0A; Domain=.imdb.com; Expires=Sat, 29-Mar-2087 02:52:22 GMT; Path=/; Secure'), ('Set-Cookie', 'session-id=135-0078761-6346505; Domain=.imdb.com; Expires=Sat, 29-Mar-2087 02:52:22 GMT; Path=/; Secure'), ('Set-Cookie', 'session-id-time=2182981095; Domain=.imdb.com; Expires=Sat, 29-Mar-2087 02:52:22 GMT; Path=/; Secure'), ('Vary', 'Accept-Encoding,X-Amzn-CDN-Cache,User-Agent'), ('x-amz-rid', '4QWFEM27QGYNC08J00YP'), ('X-Cache', 'Miss from cloudfront'), ('Via', '1.1 4798af72c7f6cead30e0da0525c1880c.cloudfront.net (CloudFront)'), ('X-Amz-Cf-Id', 'T3rlZyMEo2obc6NzsErVSy-pYEPJ26JE72ff2fPzzNC4SKpEXzTKQg==')] |
上例中,我们使用了HTTPSConnection,因为该网站以HTTPS协议提供服务。我们根据网站的具体情况可以使用HTTPSConnection或HTTPConnection。我们传入了一个 URL 并通过连接对象查看了其状态。然后,我们获取到了一个header列表。该header列表中包含服务器返回数据类型的相关信息。getheaders() 方法可获取到header列表。
下面我们来看一个POST请求的示例。我们可以使用HTTP POST来向URL post数据。下面创建一个脚本post_example.py并编写如下内容:
1 2 3 4 5 6 7 8 9 10 |
import http.client import json con_obj = http.client.HTTPSConnection('www.httpbin.org') headers_list = {'Content-Type': 'application/json'} post_text = {'text': 'Hello World'} json_data = json.dumps(post_text) con_obj.request('POST', '/post', json_data, headers_list) response = con_obj.getresponse() print(response.read().decode()) |
运行脚本如下:
1 |
$ python3 post_example.py |
我们将得到如下的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "args": {}, "data": "{\"text\": \"Hello World\"}", "files": {}, "form": {}, "headers": { "Accept-Encoding": "identity", "Content-Length": "23", "Content-Type": "application/json", "Host": "www.httpbin.org" }, "json": { "text": "Hello World" }, "origin": "112.64.119.235, 112.64.119.235", "url": "https://www.httpbin.org/post" } |
上例中,首先我们创建了一个HTTPSConnection对象。然后,我们创建了一个post_text对象,它post 了Hello World。再后,我们编写了一个POST请求,并接收到了响应信息。
http.server模块
这一部分中,我们将学习http包中的一个模块:http.server模块。该模块定义用于实现HTTP服务端的类。它有两个方法:GET和HEAD。通过使用该模块,我们可以在网络上分享文件。我们可以在任意端口上运行http服务端。确保该端口号大于1024。默认端口号为8000。
可像下面这样使用http.server。
首先进行到选定的目录,并运行如下命令:
1 |
$ python3 -m http.server 9000 |
这时打开浏览器,在地址栏中键入localhost:9000并按下Enter键。将会得到如下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ python3 -m http.server 9000 Serving HTTP on 0.0.0.0 port 9000 (http://0.0.0.0:9000/) ... 127.0.0.1 - - [10/Mar/2019 23:54:51] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [10/Mar/2019 23:54:58] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [10/Mar/2019 23:54:59] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [10/Mar/2019 23:55:00] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [10/Mar/2019 23:55:00] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [10/Mar/2019 23:55:01] "GET / HTTP/1.1" 200 - 172.20.10.2 - - [10/Mar/2019 23:55:27] "GET / HTTP/1.1" 200 - 172.20.10.2 - - [10/Mar/2019 23:55:28] code 404, message File not found 172.20.10.2 - - [10/Mar/2019 23:55:28] "GET /favicon.ico HTTP/1.1" 404 - 172.20.10.2 - - [10/Mar/2019 23:55:51] "GET / HTTP/1.1" 200 - 172.20.10.2 - - [10/Mar/2019 23:56:00] "GET / HTTP/1.1" 200 - |
译者注:纯命令行服务器可通过 curl localhost:9000进行访问,或通过服务器 IP 地址来进行访问,以示 Alan 演示了这两种效果
ftplib模块
ftplibj在Python中提供了包含所有FTP协议各类操作的功能。ftplib包含FTP客户端类,以及一些帮助函数。使用该模块我们可以很容易地连接FTP服务器来获取多个文件并对它们进行处理。通过导入ftplib模块,我们就可以使用其中的所有功能了。
这一部分中,我们将讲解如何使用ftplib模块来进行FTP传输。我们会一起来看各类FTP对象。
下载文件
这一部分中,我们将学习使用ftplib从另一台机器上下载文件。为此,创建一个get_ftp_files.py脚本并编写如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import os from ftplib import FTP ftp = FTP('FTP域名或IP') with ftp: ftp.login('用户名', '密码') ftp.cwd('/home/student/work/') files = ftp.nlst() print(files) # 打印文件 for file in files: print("Downloading..." + file) ftp.retrbinary("RETR " + file, open("/home/student/testing/" + file, 'wb').write) ftp.close() |
运行脚本如下:
1 |
$ python3 get_ftp_files.py |
得到的结果如下:
1 2 3 4 5 6 |
['hello', 'hello.c', 'sample.txt', 'strip_hello', 'test.py'] Downloading...hello Downloading...hello.c Downloading...sample.txt Downloading...strip_hello Downloading...test.py |
译者注:要实现以上,首先要确保存在相关文件和目录并安装了 ftp 服务:sudo apt-get install vsftpdu并对/etc/vsftpd.conf进行相应的配置。
上例中,我们使用ftplib模块从主机上获取了多个文件。首先,我们传入了另一台机器的IP地址、用户名和密码。要从主机上获取所有文件,我们使用了ftp.nlst() 函数,并使用ftp.retrbinary()函数将这些文件下载到了本地电脑。
使用getwelcome()获取欢迎信息
一旦建立了初始化连接,服务端通常会返回一条欢迎信息。这一消息来自getwelcome()函数,有时会包含声明信息以及与可能与用户相关的帮助信息。
下面我们来看一个getwelcome()的示例,创建一个脚本get_welcome_msg.py并编写如下内容:
1 2 3 4 5 6 7 8 9 |
from ftplib import FTP ftp = FTP('FTP的域名或 IP 地址') ftp.login('用户名', '密码') welcome_msg = ftp.getwelcome() print(welcome_msg) ftp.close() |
运行脚本如下:
1 2 |
$ python3 get_welcome_msg.py 220 (vsFTPd 3.0.3) |
以上代码中,首先我们传入了另一台机器的IP地址、用户名和密码。我们使用了getwelcome()函数来获取初始化连接建立之后的信息。
使用sendcmd()函数向服务器发送命令
这一部分中,我们将学习sendcmd()函数。我们可以使用sendcmd()来发送一个简单字符串命令来获取字符串响应。客户端可以发送STAT, PWD, RETR和STOR等FTP命令。ftplib模块中有多个方法能封装这些命令。这些命令可使用sendcmd()或voidcmd()方法进行发送。作为示例,我们将发送一个STAT命令来查看服务端的状态。
创建一个脚本send_command.py并编写如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from ftplib import FTP ftp = FTP('FTP服务器域名或 IP 地址') ftp.login('用户名', '密码') ftp.cwd('/home/student') s_cmd_stat = ftp.sendcmd('STAT') print(s_cmd_stat) print() s_cmd_pwd = ftp.sendcmd('PWD') print(s_cmd_pwd) print() ftp.close() |
运行脚本如下:
1 |
$ python3 send_command.py |
将得到如下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
211-FTP server status: Connected to ::ffff:192.168.xxx.xxx Logged in as student TYPE: ASCII No session bandwidth limit Session timeout in seconds is 300 Control connection is plain text Data connections will be plain text At session startup, client count was 1 vsFTPd 3.0.3 - secure, fast, stable 211 End of status 257 "/home/student" is the current directory |
以上代码中,我们首先传入了另一台机器的IP地址、用户名和密码。接着,我们使用了sendcmd()向另一台机器发送了STAT命令。然后使用sendcmd()发送PWD命令。
urllib包
和http相似,urllib也是包含处理 URL 的诸多模块的一个包。urllib模块允许我们通过脚本访问多个网站。我们可以使用这个模块来下载数据、解决数据、修改header等。
urllib有几个不同的模块,列出如下:
- urllib.request: 用于打开和读取URL
- urllib.error: 包含urllib.request抛出的异常
- urllib.parse: 用于解析URL
- urllib.robotparser: 用于解析robots.txt文件
这一部分中,我们将学习使用urllib来打开URL以及如何从URL读取html文件。我们会看一个urllib用法的简单示例。我们将导入urllib.requests。然后将打开的 URL 分配给一个变量,再后,我们会使用a .read()命令来从URL读取数据。
创建脚本url_requests_example.py并在其中编写如下内容:
1 2 3 4 |
import urllib.request x = urllib.request.urlopen('https://www.imdb.com/') print(x.read()) |
运行脚本如下:
1 |
$ python3 url_requests_example.py |
得到的结果如下:
1 2 3 4 |
b'\n\n<!DOCTYPE html>\n<html\n xmlns:og="http://ogp.me/ns#"\n xmlns:fb="http://www.facebook.com/2008/fbml">\n <head>\n \n <meta charset="utf-8">\n <meta http-equiv="X-UA-Compatible" content="IE=edge">\n\n \ n \n \n\n \n \n \n\n <meta name="apple-itunes-app" content="app-id=342792525, app-argument=imdb:///?src=mdot">\n\n\n\n <script type="text/javascript">var IMDbTimer={starttime: new Date().getTime(),pt:\'java\'};</script>\n\n<script>\n if (typeof uet == \'function\') {\n uet("bb", "LoadTitle", {wb: 1});\n }\n</script>\n <script>(function(t){ (t.events = t.events|| {})["csm_head_pre_title"] = new Date().getTime(); })(IMDbTimer);</script>\n <title>Ratings and Reviews for New Movies and TV Shows - IMDb</title>\n <script>(function(t){ (t.events = t.events || {})["csm_head_post_title"] = new Date().getTime(); })(IMDbTimer);</script>\n<script>\n if (typeof uet == \'function\') {\n uet("be", "LoadTitle", {wb: 1});\n }\n</script>\n<script>\n if (typeof uex == \'function\') {\n uex("ld", "LoadTitle", {wb: 1});\n }\n</script>\n\n <link rel="canonical" href="https://www.imdb.com/" />\n <meta property="og:url" content="http://www.imdb.com/" />\n <link rel="alternate" media="only screen and (max-width: 640px)" href="https://m.imdb.com/">\n\n<script>\n if (typeof uet == \'function\') {\n uet("bb", "Loa dIcons", {wb: 1});\n }\n</script>\n <script>(function(t){ (t.events = t.events || {})["csm_head_pre_icon"] = new Date().getTime(); })(IMDbTimer);</script>\n <link href="https://m.media-amazon.com/images/G/01/imdb/images/safari-favicon-517611381._CB483525257_.svg" mask rel="icon" sizes="any">\n <link rel="icon" type="image/ico" href="https://m.media-amazon.com/images/G/01/imdb/images/favicon-2165806970._CB470047330_.ico" />\n... |
上例中,我们使用了read()方法,它返回一个字节数组。这会以不易于人类阅读的格式打印Imdb首页返回的数据,但我们可以使用HTML解析器来从中提取有用的信息。
Python urllib响应头
我们可以对响应对象调用 info()函数来获取响应头。它返回一个字典,这样我们还可以从响应中提取指定的头信息数据。创建一个脚本url_response_header.py并编写如下内容:
1 2 3 4 |
import urllib.request x = urllib.request.urlopen('https://www.imdb.com/') print(x.info()) |
运行脚本如下:
1 |
$ python3 url_response_header.py |
输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Content-Type: text/html;charset=UTF-8 Transfer-Encoding: chunked Connection: close Server: Server Date: Mon, 11 Mar 2019 14:46:53 GMT Strict-Transport-Security: max-age=47474747; includeSubDomains; preload X-Frame-Options: SAMEORIGIN Content-Security-Policy: frame-ancestors 'self' imdb.com *.imdb.com *.media-imdb.com withoutabox.com *.withoutabox.com amazon.com *.amazon.com amazon.co.uk *.amazon.co.uk amazon.de *.amazon.de translate.google.com images.google.com www.google.com www.google.co.uk search.aol.com bing.com www.bing.com Content-Language: en-US Set-Cookie: uu=BCYk-Lx1Rx5JCui5VFoJirwDNEfFoFvCbsJ5orMtLw1TDK8-fF_BQTLUI_IfGdVeukWHb0gYP9oY%0D%0AuBTtmAFSPcQNwm4gEvjn6kprXoJKRWKIVfNU7LVaAljFoZM-vKFm9ipaaOijaiqEchXtg6piAL9F%0D%0AmQ%0D%0A; Domain=.imdb.com; Expires=Sat, 29-Mar-2087 18:01:00 GMT; Path=/; Secure Set-Cookie: session-id=000-0000000-0000000; Domain=.imdb.com; Expires=Sat, 29-Mar-2087 18:01:00 GMT; Path=/; Secure Set-Cookie: session-id-time=2183035613; Domain=.imdb.com; Expires=Sat, 29-Mar-2087 18:01:00 GMT; Path=/; Secure Vary: Accept-Encoding,X-Amzn-CDN-Cache,User-Agent x-amz-rid: M5WX23YG1PNN3CNBJ6ZP X-Cache: Miss from cloudfront Via: 1.1 3a189d473309dc117059fecb2737991b.cloudfront.net (CloudFront) X-Amz-Cf-Id: uqSqfC60aZbj-SrxB6EVNjjzKAsl1veNjbBxpSBgQ2gMKHcvYgjQQg== |
总结
本章中,我们学习了socket,它用于服务端-客户端双向通讯。我们还学习了三个互联网模块:http, ftplib和urllib。http包中有服务端和客户端模块:分别为http.client和http.server。使用ftplib,我们从另一台机器上下载了文件。我们还看了欢迎消息的知识以及发送send命令。
下一章中将涵盖创建和发送邮件的知识。我们会学习消息格式以及添加多媒体内容。同时,我们会学习SMTP, POP和IMAP服务器。
课后问题
- 什么是socket编程?
- 什么是RPC?
- 导入用户定义的模块或文件有哪些不同的方式?
- 列表和元组之间的区别是什么?
- 字典中的键是否可以重复?
- urllib, urllib2和requests模块之间的区别是什么?