本章中我们将学习sockets和三种互联网协议:http, ftplib和urllib。我们会学习Python中用于网络的socket模块。http是一个用于处理超文本传输协议的包。ftplib用于执行自动化的FTP相关工作。urllib是一个处理URL相关工作的包。
本章中我们将学习如下内容:
- Sockets
- http包
- ftplib模块
- urllib包
这一部分中, 我们将学习socket(套接字接口)的知识。我们将使用Python的socket模块。socket是本地或远程机器间通过的端点(endpoint)。socket模块有一个socket类,用于处理数据通道。它还包含网络相关操作的函数。要使用socket模块的功能,首先我们要导入socket模块。
我们来看看如何创建一个socket。socket类有一个socket函数,带有两个参数:address_family和socket类型。
语法如下:
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脚本内容如下:
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脚本内容如下:
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.client: 这是一个底层HTTP协议客户端
- http.server: 包含基本HTTP服务器类
- http.cookies: 用于实现带cookie的状态管理
- http.cookiejar: 该模块提供cookie持久化
这一部分中,我们将学习http.client和http.server模块。
我们将来看两种http请求:GET和POST。我们还会来做一个http连接。
首先,我们来探讨一个进行http连接的示例。为此创建一个脚本make_connection.py并在其中编写如下内容:
import http.client
con_obj = http.client.HTTPConnection("https://www.baidu.com", 80, timeout=20)
print(con_obj)
运行脚本,我们将会得到如下输出:
$ python3 make_connection.py
<http.client.HTTPConnection object at 0x7f4f0d6e0438>
在上例中,我们对传入的 URL 的80端口以一个指定的超时时间进行了连接。
下面我们来看 http GET请求方法,使用GET请求方法我们可以看一个获取返回码以及头部列表的示例。创建一个脚本get_example.py并编写如下内容:
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()
运行脚本如下:
$ python3 get_example.py
得到的结果如下:
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并编写如下内容:
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())
运行脚本如下:
$ python3 post_example.py
我们将得到如下的输出:
{
"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包中的一个模块:http.server模块。该模块定义用于实现HTTP服务端的类。它有两个方法:GET和HEAD。通过使用该模块,我们可以在网络上分享文件。我们可以在任意端口上运行http服务端。确保该端口号大于1024。默认端口号为8000。
可像下面这样使用http.server。
首先进行到选定的目录,并运行如下命令:
$ python3 -m http.server 9000
这时打开浏览器,在地址栏中键入localhost:9000并按下Enter键。将会得到如下输出:
$ 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 演示了这两种效果
ftplibj在Python中提供了包含所有FTP协议各类操作的功能。ftplib包含FTP客户端类,以及一些帮助函数。使用该模块我们可以很容易地连接FTP服务器来获取多个文件并对它们进行处理。通过导入ftplib模块,我们就可以使用其中的所有功能了。
这一部分中,我们将讲解如何使用ftplib模块来进行FTP传输。我们会一起来看各类FTP对象。
这一部分中,我们将学习使用ftplib从另一台机器上下载文件。为此,创建一个get_ftp_files.py脚本并编写如下内容:
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()
运行脚本如下:
$ python3 get_ftp_files.py
得到的结果如下:
['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()的示例,创建一个脚本get_welcome_msg.py并编写如下内容:
from ftplib import FTP
ftp = FTP('FTP的域名或 IP 地址')
ftp.login('用户名', '密码')
welcome_msg = ftp.getwelcome()
print(welcome_msg)
ftp.close()
运行脚本如下:
$ python3 get_welcome_msg.py
220 (vsFTPd 3.0.3)
以上代码中,首先我们传入了另一台机器的IP地址、用户名和密码。我们使用了getwelcome()函数来获取初始化连接建立之后的信息。
这一部分中,我们将学习sendcmd()函数。我们可以使用sendcmd()来发送一个简单字符串命令来获取字符串响应。客户端可以发送STAT, PWD, RETR和STOR等FTP命令。ftplib模块中有多个方法能封装这些命令。这些命令可使用sendcmd()或voidcmd()方法进行发送。作为示例,我们将发送一个STAT命令来查看服务端的状态。
创建一个脚本send_command.py并编写如下内容:
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()
运行脚本如下:
$ python3 send_command.py
将得到如下输出:
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命令。
和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并在其中编写如下内容:
import urllib.request
x = urllib.request.urlopen('https://www.imdb.com/')
print(x.read())
运行脚本如下:
$ python3 url_requests_example.py
得到的结果如下:
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解析器来从中提取有用的信息。
我们可以对响应对象调用 info()函数来获取响应头。它返回一个字典,这样我们还可以从响应中提取指定的头信息数据。创建一个脚本url_response_header.py并编写如下内容:
import urllib.request
x = urllib.request.urlopen('https://www.imdb.com/')
print(x.info())
运行脚本如下:
$ python3 url_response_header.py
输出如下:
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模块之间的区别是什么?