Python 提供了两个级别访问的网络服务:
- 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
- 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
Socket
Socket 又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
创建套接字
使用 socket() 函数:
socket.socket([family[, type[, proto]]])
- family:套接字家族可以是 AF_UNIX 或者 AF_INET
- type:套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAM 或 SOCK_DGRAM
- proto:一般不填默认为0
Socket 对象(内建)方法
服务器端套接字
- s.bind():绑定地址元组(host,port)到套接字。
- s.listen():开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
- s.accept():被动接受TCP客户端连接,(阻塞式)等待连接的到来客户端套接字
- s.connect():主动初始化TCP服务器连接。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
- s.connect_ex():connect()函数的扩展版本,出错时返回出错码,而不是抛出异常。
公共用途的套接字函数
- s.recv():接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
- s.send():发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
- s.sendall():完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
- s.recvfrom():接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
- s.sendto():发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
- s.close():关闭套接字 s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
- s.getsockname():返回套接字自己的地址。通常是一个元组(ipaddr,port)。
- s.setsockopt(level,optname,value):设置给定套接字选项的值。
- s.getsockopt(level,optname[.buflen]):返回套接字选项的值。
- s.settimeout(timeout):设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())。
- s.gettimeout():返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
- s.fileno():返回套接字的文件描述符。
- s.setblocking(flag):如果 flag 为 False,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用 recv() 没有发现任何数据,或 send() 调用无法立即发送数据,那么将引起 socket.error 异常。
- s.makefile():创建一个与该套接字相关连的文件。
TCP 编程
由于TCP连接具有安全可靠的特性,应用更为广泛。创建TCP连接时,起连接的叫客户端,被动响应连接的叫服务器。一个TCP连接就建立起来了,后面的通信就是发送网页内容了。
创建TCP服务器
完成一个 TCP 服务器功能需要的流程如下:
- 使用socket()创建一个套接字;
- 使用 bind()绑定 IP和port;
- 使用listen()使套接字变为可被动连接;
- 使用accept()等待客户端的连接;
- 使用recv/send()接收发送数据。
server.py
import socket
# 创建 socket 对象:TCP/IP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 8080
# 绑定端口号
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5)
# 建立客户端连接
client_socket, addr = server_socket.accept()
print("TCP连接已建立")
print("连接地址: %s" % str(addr))
# 发送TCP数据
msg = '欢迎访问服务器!' + "\r\n"
client_socket.send(msg.encode('utf-8'))
# 接收来自客户端的数据
info = client_socket.recv(1024).decode()
while info != 'byebye':
if info:
print("服务器接收到的内容:" + info)
send_data = input("回复客户端:")
client_socket.send(send_data.encode())
info = client_socket.recv(1024).decode()
if info == 'byebye':
client_socket.send('byebye'.encode())
# 关闭客户端套接字
client_socket.close()
# 关闭服务器套接字
server_socket.close()
print("已关闭端到端连接")
client.py
import socket
# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 8080
# 连接服务,指定主机和端口
s.connect((host, port))
print("已连接服务器")
# 接收小于 1024 字节的数据
msg = s.recv(1024)
print("收到服务器信息:" + msg.decode('utf-8'))
send_data = ''
while send_data != 'byebye':
# 发送消息
send_data = input("客户端发送消息:")
s.send(send_data.encode())
# 接收服务器的数据
msg = s.recv(1024)
print("收到服务器回复:" + msg.decode('utf-8'))
# 关闭套接字
s.close()
示例结果:
注意:先启动服务器,否则会直接拒绝客服端发起的连接请求。
UDP 编程
UDP 服务器不是面向连接的,除了等待传入连接之外,几乎不需要其他工作。
- 服务器使用 recvfrom() 方法接收客户端的数据和地址信息,然后使用 sendto() 方法将转换后的温度数据发送回客户端。
- 客户端使用 sendto() 方法发送摄氏温度数据到服务器,并使用 recvfrom() 方法接收服务器的回复。
server.py
import socket
# 创建 UDP socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 8080
# 绑定端口号
server_socket.bind((host, port))
print("UDP服务器已启动,等待客户端连接...")
while True:
# 接收来自客户端的数据和地址信息
data, addr = server_socket.recvfrom(1024)
print("收到客户端数据:", data.decode('utf-8'), "来自", addr)
if data.decode('utf-8').lower() == 'byebye':
server_socket.sendto('byebye'.encode(), addr)
print("客户端请求退出,服务器也将退出.")
break
try:
# 将接收到的摄氏温度转换为华氏温度
celsius = float(data.decode('utf-8'))
fahrenheit = (celsius * 9/5) + 32
response = f"{celsius}°C表示的是{fahrenheit}°F"
except ValueError:
response = "输入错误,请输入有效的摄氏温度值!"
# 发送转换后的温度给客户端
server_socket.sendto(response.encode('utf-8'), addr)
# 关闭服务器套接字
server_socket.close()
print("服务器已关闭")
client.py
import socket
# 创建 UDP socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 8080
while True:
# 发送消息
send_data = input("请输入摄氏温度(°C):")
s.sendto(send_data.encode(), (host, port))
# 接收服务器的数据
msg, addr = s.recvfrom(1024)
print("收到服务器回复:", msg.decode('utf-8'))
if send_data.lower() == 'byebye':
break
# 关闭套接字
s.close()
示例结果:
Python Internet 模块
协议 | 功能用处 | 端口号 | Python 模块 |
---|---|---|---|
HTTP | 网页访问 | 80 | httplib, urllib, xmlrpclib |
NNTP | 阅读和张贴新闻文章,俗称为"帖子" | 119 | nntplib |
FTP | 文件传输 | 20 | ftplib, urllib |
SMTP | 发送邮件 | 25 | smtplib |
POP3 | 接收邮件 | 110 | poplib |
IMAP4 | 获取邮件 | 143 | imaplib |
Telnet | 命令行 | 23 | telnetlib |
Gopher | 信息查找 | 70 | gopherlib, urllib |
JSON 模块
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。
- JSON 使用 Javascript 语法来描述数据对象,但是仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态编程语言都支持 JSON。
- JSON 具有自我描述性,更易理解。
Python3 中可以使用 json 模块来对 JSON 数据进行编解码,它包含了两个函数:
- json.dumps(): 对数据进行编码。
- json.loads(): 对数据进行解码。
Python 编码为 JSON 类型转换对应表
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int- & float-derived Enums | number |
True | true |
False | false |
None | null |
JSON 解码为 Python 类型转换对应表
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int) | int |
number (real) | float |
true | True |
false | False |
null | None |
urllib 模块
Python urllib 库用于操作网页 URL,并对网页的内容进行抓取处理。urllib 包 包含以下几个模块:
- urllib.request - 打开和读取 URL。
- urllib.error - 包含 urllib.request 抛出的异常。
- urllib.parse - 解析 URL。
- urllib.robotparser - 解析 robots.txt 文件。
urllib.request
urllib.request 定义了一些打开 URL 的函数和类,包含授权验证、重定向、浏览器 cookies等。urllib.request 可以模拟浏览器的一个请求发起过程。
使用 urllib.request 的 urlopen 方法来打开一个 URL,语法格式:
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
- url:url 地址。
- data:发送到服务器的其他数据对象,默认为 None。
- timeout:设置访问超时时间。
- cafile 和 capath:cafile 为 CA 证书, capath 为 CA 证书的路径,使用 HTTPS 需要用到。
- cadefault:已经被弃用。
- context:ssl.SSLContext类型,用来指定 SSL 设置。
抓取网页一般需要对 headers(网页头信息)进行模拟,需要使用到 urllib.request.Request 类:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
- url:url 地址。
- data:发送到服务器的其他数据对象,默认为 None。
- headers:HTTP 请求的头部信息,字典格式。
- origin_req_host:请求的主机地址,IP 或域名。
- unverifiable:很少用这个参数,用于设置网页是否需要验证,默认是 False。
- method:请求方法, 如 GET、POST、DELETE、PUT等。
示例:
import urllib.request
def get_webpage_content(url):
# 创建一个 Request 对象
request = urllib.request.Request(url)
# 发送 GET 请求
with urllib.request.urlopen(request) as response:
# 读取响应内容
html_content = response.read()
# 将字节内容转换为字符串
content = html_content.decode('utf-8')
return content
# 假设我们要获取的网页是 http://example.com
url = 'https://jsonplaceholder.typicode.com/todos/1'
content = get_webpage_content(url)
print(content) # 打印网页内容
输出结果:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
requests 模块
Python requests 是一个常用的 HTTP 请求库,可以方便地向网站发送 HTTP 请求,并获取响应结果。requests 模块比 urllib 模块更简洁。
# 导入 requests 包
import requests
# 发送请求
x = requests.get('https://www.csdn.com/')
# 返回网页内容
print(x.text)
每次调用 requests 请求之后,会返回一个 response 对象,该对象包含了具体的响应信息,如状态码、响应头、响应内容等:
print(response.status_code) # 获取响应状态码
print(response.headers) # 获取响应头
print(response.content) # 获取响应内容
# 导入 requests 包
import requests
# 发送请求
x = requests.get('https://www.csdn.com/')
# 返回 http 的状态码
print(x.status_code)
# 响应状态的描述
print(x.reason)
# 返回编码
print(x.apparent_encoding)
方法如下:
方法 | 描述 |
---|---|
delete(url, args) | 发送 DELETE 请求到指定 url |
get(url, params, args) | 发送 GET 请求到指定 url |
head(url, args) | 发送 HEAD 请求到指定 url |
patch(url, data, args) | 发送 PATCH 请求到指定 url |
post(url, data, json, args) | 发送 POST 请求到指定 url |
put(url, data, args) | 发送 PUT 请求到指定 url |
request(method, url, args) | 向指定的 url 发送指定的请求方法 |
示例:
import requests
# 假设的 RESTful API URL
api_url = 'https://jsonplaceholder.typicode.com/posts/1'
# 发送 GET 请求
def send_get_request(url, params=None):
response = requests.get(url, params=params)
return response.json() # 假设响应内容是 JSON 格式
# 发送 POST 请求
def send_post_request(url, data=None, json=None):
response = requests.post(url, data=data, json=json)
return response.json() # 假设响应内容是 JSON 格式
# 发送 PUT 请求
def send_put_request(url, data=None):
response = requests.put(url, data=data)
return response.json() # 假设响应内容是 JSON 格式
# 发送 DELETE 请求
def send_delete_request(url, params=None):
response = requests.delete(url, params=params)
return response.json() # 假设响应内容是 JSON 格式
# 发送 PATCH 请求
def send_patch_request(url, data=None):
response = requests.patch(url, data=data)
return response.json() # 假设响应内容是 JSON 格式
# 示例使用
get_response = send_get_request(api_url, params={'query': 'value'})
print('GET 请求响应:', get_response)
post_response = send_post_request(api_url, json={'key': 'value'})
print('POST 请求响应:', post_response)
put_response = send_put_request(api_url, data={'key': 'new_value'})
print('PUT 请求响应:', put_response)
delete_response = send_delete_request(api_url)
print('DELETE 请求响应:', delete_response)
patch_response = send_patch_request(api_url, data={'key': 'updated_value'})
print('PATCH 请求响应:', patch_response)
运行结果:
GET 请求响应: {'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'}
POST 请求响应: {}
PUT 请求响应: {'key': 'new_value', 'id': 1}
DELETE 请求响应: {}
PATCH 请求响应: {'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto', 'key': 'updated_value'}