引子
最近有需求要在 PyQt 中请求一个链接, 因为比较简单直接用 urllib2 处理了, 但是 urllib2 在 有延时的时候会造成 GUI 界面卡死. 所以今天研究研究 QtNetwork 模块.
QtNetwork 中的请求在 PyQt 中都是异步的.
简单的请求 QHttp
发起一个GET请求
PyQt4.QtNetwork.QHttp
可以发起一个简单请求, 需要注意的是这个对象需要通过调用
setHost
设置请求主机, 然后 调用 get
/post
传入 path
才能正常使用.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore, QtNetwork
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent=parent)
self.http = QtNetwork.QHttp(parent=self)
# 绑定 done 信号
self.http.done.connect(self.on_req_done)
self.url = QtCore.QUrl("http://linuxzen.com/")
# 设置主机
self.http.setHost(self.url.host(), self.url.port(80))
self.getId = self.http.get(self.url.path())
def on_req_done(self, error):
if not error:
print "Success"
print self.http.readAll()
else:
print "Error"
if __name__ == "__main__":
app = QtGui.QApplication([])
main = MainWidget()
main.show()
app.exec_()
保存文件
如果想要下载文件, 可以给 get
方法传一个 QtCore.QFile
对象, 会将请求内容保存
到文件
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore, QtNetwork
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent=parent)
self.http = QtNetwork.QHttp(parent=self)
self.http.done.connect(self.on_req_done)
self.url = QtCore.QUrl("http://www.linuxzen.com/")
self.http.setHost(self.url.host(), self.url.port(80))
self.out = QtCore.QFile("./test")
self.getId = self.http.get(self.url.path(), self.out)
def on_req_done(self, error):
# print self.http.readAll(), error
if not error:
print "Success"
else:
print "Error"
if __name__ == "__main__":
app = QtGui.QApplication([])
main = MainWidget()
main.show()
app.exec_()
上面代码将请求下来的内容保存到当前目录的 test 文件里.
处理头部信息
现在有一个需求就是有一个链接会返回一个 301 或 302 的跳转, 但是 PyQt4.QtNetwork.QHttp
没有实现自动跳转, 需要我们自动判断头信息进行跳转.
我们可以绑定PyQt4.QtNetwork.QHttp.responseHeaderReceived
的信号来处理头部信息,
这个信号将给槽传递一个 PyQt4.QtNetwork.QHttpResponseHeader
的实例.
通过判断状态吗, 并抓取 Location
头实现跳转
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
from PyQt4 import QtGui, QtCore, QtNetwork
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent=parent)
self.http = QtNetwork.QHttp(parent=self)
self.http.done.connect(self.on_req_done)
self.http.responseHeaderReceived.connect(self.on_response_header)
self.url = QtCore.QUrl("http://t.cn/zTocACq")
self.http.setHost(self.url.host(), self.url.port(80))
self.out = QtCore.QFile("./test")
self.getId = self.http.get(self.url.path(), self.out)
def on_response_header(self, response_header):
if response_header.statusCode() in [301, 302]:
location = response_header.value("Location")
print "Redirect to: ", location
self.getId = self.http.get(location, self.out)
tmp = QtCore.QUrl(location)
if str(tmp.host()):
self.url = tmp
self.http.setHost(self.url.host(), self.url.port(80))
else:
self.url.setPath(location)
self.http.get(self.url.path() or "/", self.out)
def on_req_done(self, error):
if not error:
print "Success"
print self.http.readAll()
else:
print "Error"
if __name__ == "__main__":
app = QtGui.QApplication([])
main = MainWidget()
main.show()
app.exec_()
运行上面代码, 你将看到下面输出
Redirect to: http://www.linuxzen.com
Success
处理参数
如果你的 GET 请求的 url 是带着参数传给 QUrl
的, 那么 QUrl.path()
将不会返回带参数的
路径需要自己处理. 可以参见下面的处理函数
def get_query_string(self):
return self.url.queryPairDelimiter().join(
"{0}{1}{2}".format(k, self.url.queryValueDelimiter(), v)
for k, v in self.url.queryItems()
)
请求的时候加上就行了
self.http.get(self.url.path() + "?" + self.get_query_string())
处理 Cookie ---- QNetworkAccessManager
QHttp 是无法自动记录 Cookie 和设置 Cookie的, 如果有这个需求就需要 PyQt4.QtNetwork.QNetworkAccessManager
QNetworkAccessManager
的请求方法不在是传入一个 QString, 而是需要传入一个
PyQt4.QtNetwork.QNetworkRequest
的实例.
QNetworkAccessManager
同样是异步的, 可以绑定 QNetworkAccessManager.finished
信号,
这个信号将会给槽传递一个 PyQt4.QtNetwork.QNetworkReply
的实例.
QNetworkAccessManager.setCookieJar
可以设置一个 PyQt4.QtNetwork.QNetworkCookieJar
对象来
自动保存和设置 Cookie.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
from PyQt4 import QtGui, QtCore, QtNetwork
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self._cookiejar = QtNetwork.QNetworkCookieJar(parent=self)
self.manager = QtNetwork.QNetworkAccessManager(parent=self)
self.manager.setCookieJar(self._cookiejar)
self.manager.finished.connect(self.on_reply)
self.req = QtNetwork.QNetworkRequest(
QtCore.QUrl("http://www.google.com.hk"))
self.manager.get(self.req)
def on_reply(self, reply):
print reply, self._cookiejar.allCookies()
print reply.rawHeaderList()
# print reply.readAll()
if __name__ == "__main__":
app = QtGui.QApplication([])
widget = MainWidget()
widget.show()
app.exec_()
这样就可以携带 Cookie 去请求一些需要 Cookie 认证的请求.
总结
还有很多内容就不一一介绍, 比如配合 QProgressBar
, 添加头部信息, POST 请求, 等.
这里只是简单介绍下 Qt 自己的网络请求处理方法. 在 PyQt 里用 urllib 等库明显不是好的 解决办法, 因为会造成界面卡死. 但是 PyQt4 的方式也实在不是很优雅. 要来来回回的调很多 东西. 但是最起码不会造成界面的卡死.