# PyQt 中的`QNetworkAccessManager`

> 原文： [http://zetcode.com/pyqt/qnetworkaccessmanager/](http://zetcode.com/pyqt/qnetworkaccessmanager/)

PyQt 教程中的`QNetworkAccessManager`展示了如何显示如何使用`QNetworkAccessManager`发送请求和接收响应。

## `QNetworkAccessManager`

`QNetworkAccessManager`允许应用发送网络请求和接收回复。 `QNetworkRequest`保留要与网络管理器发送的请求，`QNetworkReply`包含返回的数据和响应头。

`QNetworkAccessManager`具有异步 API，这意味着其方法总是立即返回，并且不等到完成时才返回。 而是在请求完成时发出信号。 我们通过附加到完成信号的方法来处理响应。

## HTTP GET 请求

HTTP GET 方法请求指定资源的表示形式。

`get_request.py`

```py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''
QNetworkAccessManager in PyQt

In this example we get a web page.

Author: Jan Bodnar
Website: zetcode.com
Last edited: September 2017
'''

from PyQt5 import  QtNetwork
from PyQt5.QtCore import QCoreApplication, QUrl
import sys

class Example:

    def __init__(self):    

        self.doRequest()

    def doRequest(self):   

        url = "http://www.something.com"
        req = QtNetwork.QNetworkRequest(QUrl(url))

        self.nam = QtNetwork.QNetworkAccessManager()
        self.nam.finished.connect(self.handleResponse)
        self.nam.get(req)    

    def handleResponse(self, reply):

        er = reply.error()

        if er == QtNetwork.QNetworkReply.NoError:

            bytes_string = reply.readAll()
            print(str(bytes_string, 'utf-8'))

        else:
            print("Error occured: ", er)
            print(reply.errorString())

        QCoreApplication.quit()    

app = QCoreApplication([])
ex = Example()
sys.exit(app.exec_())

```

该示例检索指定网页的 HTML 代码。

```py
url = "http://www.something.com"
req = QtNetwork.QNetworkRequest(QUrl(url))

```

使用`QNetworkRequest`，我们将请求发送到指定的 URL。

```py
self.nam = QtNetwork.QNetworkAccessManager()
self.nam.finished.connect(self.handleResponse)
self.nam.get(req)  

```

创建一个`QNetworkAccessManager`对象。 请求完成后，将调用`handleResponse()`方法。 使用`get()`方法触发该请求。

```py
def handleResponse(self, reply):

    er = reply.error()

    if er == QtNetwork.QNetworkReply.NoError:

        bytes_string = reply.readAll()
        print(str(bytes_string, 'utf-8'))

    else:
        print("Error occured: ", er)
        print(reply.errorString())

    QCoreApplication.quit()    

```

`handleResponse()`接收`QNetworkReply`对象。 它包含已发送请求的数据和标头。 如果网络回复中没有错误，我们将使用`readAll()`方法读取所有数据； 否则，我们将显示错误消息。 `errorString()`返回人类可读的最后发生的错误的描述。 `readAll()`返回`QByteArray`中必须解码的数据。

```py
$ ./get_request.py 
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>

```

这是输出。

## HTTP POST 请求

HTTP POST 方法将数据发送到服务器。 请求正文的类型由`Content-Type`标头指示。 POST 请求通常通过 HTML 表单发送。 请求中发送的数据可以采用不同的方式进行编码。 在`application/x-www-form-urlencoded`中，值被编码为以`'&'`分隔的键值元组，键和值之间带有`'='`。 非字母数字字符采用百分比编码。 `multipart/form-data`用于二进制数据和文件上传。

`post_request.py`

```py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''
QNetworkAccessManager in PyQt

In this example we post data to a web page.

Author: Jan Bodnar
Website: zetcode.com
Last edited: September 2017
'''

from PyQt5 import QtCore, QtGui, QtNetwork
import sys, json     

class Example:

    def __init__(self):    

        self.doRequest()

    def doRequest(self):   

        data = QtCore.QByteArray()
        data.append("name=Peter&")
        data.append("age=34")

        url = "https://httpbin.org/post"
        req = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
        req.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, 
            "application/x-www-form-urlencoded")

        self.nam = QtNetwork.QNetworkAccessManager()
        self.nam.finished.connect(self.handleResponse)
        self.nam.post(req, data)    

    def handleResponse(self, reply):

        er = reply.error()

        if er == QtNetwork.QNetworkReply.NoError:

            bytes_string = reply.readAll()

            json_ar = json.loads(str(bytes_string, 'utf-8'))
            data = json_ar['form']

            print('Name: {0}'.format(data['name']))
            print('Age: {0}'.format(data['age']))

            print()

        else:
            print("Error occurred: ", er)
            print(reply.errorString())

        QtCore.QCoreApplication.quit()    

app = QtCore.QCoreApplication([])
ex = Example()
sys.exit(app.exec_())

```

该示例将发布请求发送到`https://httpbin.org/post`测试站点，该站点将数据以 JSON 格式发送回。

```py
data = QtCore.QByteArray()
data.append("name=Peter&")
data.append("age=34")

```

根据规范，我们对`QByteArray`中发送的数据进行编码。

```py
url = "https://httpbin.org/post"
req = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
req.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, 
    "application/x-www-form-urlencoded")

```

我们指定`application/x-www-form-urlencoded`编码类型。

```py
bytes_string = reply.readAll()

json_ar = json.loads(str(bytes_string, 'utf-8'))
data = json_ar['form']

print('Name: {0}'.format(data['name']))
print('Age: {0}'.format(data['age']))

print()

```

在处理器方法中，我们读取响应数据并将其解码。 使用内置的`json`模块，我们提取发布的数据。

```py
$ ./post_request.py 
Name: Peter
Age: 34

```

这是输出。

## 使用`QNetworkAccessManager`进行认证

每当最终服务器在传递所请求的内容之前请求认证时，都会发出`authenticationRequired`信号。

`authenticate.py`

```py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''
QNetworkAccessManager in PyQt

In this example, we show how to authenticate
to a web page.

Author: Jan Bodnar
Website: zetcode.com
Last edited: September 2017
'''

from PyQt5 import QtCore, QtGui, QtNetwork
import sys, json

class Example:

    def __init__(self):    

        self.doRequest()

    def doRequest(self):   

        self.auth = 0

        url = "https://httpbin.org/basic-auth/user7/passwd7"
        req = QtNetwork.QNetworkRequest(QtCore.QUrl(url))

        self.nam = QtNetwork.QNetworkAccessManager()
        self.nam.authenticationRequired.connect(self.authenticate)
        self.nam.finished.connect(self.handleResponse)
        self.nam.get(req)  

    def authenticate(self, reply, auth):

        print("Authenticating")

        self.auth += 1

        if self.auth >= 3:
            reply.abort()

        auth.setUser("user7")
        auth.setPassword("passwd7")         

    def handleResponse(self, reply):

        er = reply.error()

        if er == QtNetwork.QNetworkReply.NoError:

            bytes_string = reply.readAll()

            data = json.loads(str(bytes_string, 'utf-8'))

            print('Authenticated: {0}'.format(data['authenticated']))
            print('User: {0}'.format(data['user']))

            print()

        else:
            print("Error occurred: ", er)
            print(reply.errorString())

        QtCore.QCoreApplication.quit()    

app = QtCore.QCoreApplication([])
ex = Example()
sys.exit(app.exec_())

```

在示例中，使用`https://httpbin.org`网站显示如何使用`QNetworkAccessManager`进行认证。

```py
self.nam.authenticationRequired.connect(self.authenticate)

```

我们将`authenticationRequired`信号连接到`authenticate()`方法。

```py
def authenticate(self, reply, auth):

    print("Authenticating")
    ...

```

`authenticate()`方法的第三个参数是`QAuthenticator`，用于传递所需的认证信息。

```py
self.auth += 1

if self.auth >= 3:
    reply.abort()

```

如果验证失败，则`QNetworkAccessManager`会继续发出`authenticationRequired`信号。 在三次失败的尝试之后，我们中止了该过程。

```py
auth.setUser("user7")
auth.setPassword("passwd7")

```

我们将用户和密码设置为`QAuthenticator`。

```py
bytes_string = reply.readAll()

data = json.loads(str(bytes_string, 'utf-8'))

print('Authenticated: {0}'.format(data['authenticated']))
print('User: {0}'.format(data['user']))

print()

```

`https://httpbin.org`用 JSON 数据响应，该数据包含用户名和指示认证成功的布尔值。

```py
$ ./authenticate.py 
Authenticating
Authenticated: True
User: user7

```

这是输出。

## 提取一个网站图标

网站图标是与特定网站相关的小图标。 在以下示例中，我们将从网站上下载网站图标。

`fetch_icon.py`

```py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''
QNetworkAccessManager in PyQt

In this example we fetch a favicon from 
a website.

Author: Jan Bodnar
Website: zetcode.com
Last edited: September 2017
'''

from PyQt5 import QtCore, QtGui, QtNetwork
import sys    

class Example:

    def __init__(self):    

        self.doRequest()

    def doRequest(self):   

        url = "http://www.google.com/favicon.ico"
        req = QtNetwork.QNetworkRequest(QtCore.QUrl(url))

        self.nam = QtNetwork.QNetworkAccessManager()
        self.nam.finished.connect(self.handleResponse)
        self.nam.get(req)    

    def handleResponse(self, reply):

        er = reply.error()

        if er == QtNetwork.QNetworkReply.NoError:

            data = reply.readAll()
            self.saveFile(data)        

        else:
            print("Error occured: ", er)
            print(reply.errorString())

        QtCore.QCoreApplication.quit()    

    def saveFile(self, data):

        f = open('favicon.ico', 'wb')

        with f:

            f.write(data)

app = QtCore.QCoreApplication([])
ex = Example()
sys.exit(app.exec_())

```

该代码示例下载了 Google 的收藏夹图标。

```py
self.nam.get(req)   

```

我们使用`get()`方法下载图标。

```py
data = reply.readAll()
self.saveFile(data)   

```

在`handleResponse()`方法中，我们读取数据并将其保存到文件中。

```py
def saveFile(self, data):

    f = open('favicon.ico', 'wb')

    with f:

        f.write(data)

```

图像数据以`saveFile()`方法保存在磁盘上。

在本教程中，我们使用了`QNetworkAccessManager`。

您可能也对以下相关教程感兴趣： [`QPropertyAnimation`教程](/pyqt/qpropertyanimation/)， [PyQt5 教程](/gui/pyqt5/)和 [Python 教程](/lang/python/)。