備知識:
Zabbix3.x比較之前的2.0界面有了很大的變化,但是安裝部署過程與2.x基本完全一樣。
1、Zabbix2.x安裝圖解教程
http://www.osyunwei.com/archives/7984.html
2、CentOS 7.0編譯安裝Nginx1.6.0+MySQL5.6.19+PHP5.5.14
http://www.osyunwei.com/archives/7891.html
3、zabbix軟件包下載
zabbix-3.0.1.tar.gz
http://heanet.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/3.0.1/zabbix-3.0.1.tar.gz
上傳zabbix-3.0.1.tar.gz到服務器/usr/local/src目錄下面
安裝部署:
請參考Zabbix2.x安裝圖解教程
一、創建、導入zabbix數據庫
cd /usr/local/src #進入軟件包下載目錄
tar zabbix-3.0.1.tar.gz #解壓
cd /usr/local/src/zabbix-3.0.1/database/mysql #進入mysql數據庫創建腳本目錄
ls #列出文件,可以看到有schema.sql、images.sql、data.sql這三個文件
mysql -u root -p #輸入密碼,進入MySQL控制臺
create database zabbix character set utf8; #創建數據庫zabbix,并且數據庫編碼使用utf8
insert into mysql.user(Host,User,Password) values('localhost','zabbix',password('123456')); #新建賬戶zabbix,密碼123456
flush privileges; #刷新系統授權表
grant all on zabbix.* to 'zabbix'@'localhost' identified by '123456' with grant option; #允許賬戶zabbix能從本機連接到數據庫zabbix
flush privileges; #再次刷新系統授權表
use zabbix #進入數據庫
source /usr/local/src/zabbix-3.0.1/database/mysql/schema.sql #導入腳本文件到zabbix數據庫
source /usr/local/src/zabbix-3.0.1/database/mysql/images.sql #導入腳本文件到zabbix數據庫
source /usr/local/src/zabbix-3.0.1/database/mysql/data.sql #導入腳本文件到zabbix數據庫
注意:請按照以上順序進行導入,否則會出錯。
exit #退出
或者這樣導入
mysql -uzabbix -p123456 -hlocalhost zabbix < /usr/local/src/zabbix-3.0.1/database/mysql/schema.sql
mysql -uzabbix -p123456 -hlocalhost zabbix < /usr/local/src/zabbix-3.0.1/database/mysql/images.sql
mysql -uzabbix -p123456 -hlocalhost zabbix < /usr/local/src/zabbix-3.0.1/database/mysql/data.sql
cd /usr/lib64/mysql #32位系統為/usr/lib/mysql,注意系統版本同,文件版本可能不一樣,這里是16.0.0
ln -s libmysqlclient.so.16.0.0 libmysqlclient.so #添加軟連接
ln -s libmysqlclient_r.so.16.0.0 libmysqlclient_r.so #添加軟連接
二、安裝zabbix
1、添加用戶:
groupadd zabbix #創建用戶組zabbix
useradd zabbix -g zabbix -s /bin/false #創建用戶zabbix,并且把用戶zabbix加入到用戶組zabbix中
2、安裝依賴包 #CentOS使用yum命令安裝
yum install net-snmp-devel curl curl-devel mysql-devel
備注:以上軟件包如果在安裝Web環境LAMP或者LNMP時已經安裝過,此步驟可忽略
3、安裝zabbix
ln -s /usr/local/lib/libiconv.so.2 /usr/lib/libiconv.so.2 #添加軟連接
/sbin/ldconfig #使配置立即生效
cd /usr/local/src/zabbix-3.0.1 #進入安裝目錄
./configure --prefix=/usr/local/zabbix --enable-server --enable-agent --with-net-snmp --with-libcurl --enable-proxy --with-mysql=/usr/bin/mysql_config #配置
make #編譯
make install #安裝
ln -s /usr/local/zabbix/sbin/* /usr/local/sbin/ #添加系統軟連接
ln -s /usr/local/zabbix/bin/* /usr/local/bin/ #添加系統軟連接
說明:find / -name mysql_config 查找位置,如果沒有mysql_config,需要安裝yum install mysql-devel
4、添加zabbix服務對應的端口
vi /etc/services #編輯,在最后添加以下代碼
# Zabbix
zabbix-agent 10050/tcp # Zabbix Agent
zabbix-agent 10050/udp # Zabbix Agent
zabbix-trapper 10051/tcp # Zabbix Trapper
zabbix-trapper 10051/udp # Zabbix Trapper
:wq! #保存退出
5、修改zabbix配置文件
cd /usr/local/zabbix/etc
vi /usr/local/zabbix/etc/zabbix_server.conf
DBName=zabbix #數據庫名稱
DBUser=zabbix #數據庫用戶名
DBPassword=123456 #數據庫密碼
ListenIP=localhost #數據庫ip地址
AlertScriptsPath=/usr/local/zabbix/share/zabbix/alertscripts #zabbix運行腳本存放目錄
:wq! #保存退出
vi /usr/local/zabbix/etc/zabbix_agentd.conf
Include=/usr/local/zabbix/etc/zabbix_agentd.conf.d/
UnsafeUserParameters=1 #啟用自定義key
:wq! #保存退出
6、添加開機啟動腳本
cp /usr/local/src/zabbix-3.0.1/misc/init.d/fedora/core/zabbix_server /etc/rc.d/init.d/zabbix_server #服務端
cp /usr/local/src/zabbix-3.0.1/misc/init.d/fedora/core/zabbix_agentd /etc/rc.d/init.d/zabbix_agentd #客戶端
chmod +x /etc/rc.d/init.d/zabbix_server #添加腳本執行權限
chmod +x /etc/rc.d/init.d/zabbix_agentd #添加腳本執行權限
chkconfig zabbix_server on #添加開機啟動
chkconfig zabbix_agentd on #添加開機啟動
7、修改zabbix開機啟動腳本中的zabbix安裝目錄
vi /etc/rc.d/init.d/zabbix_server #編輯服務端配置文件
BASEDIR=/usr/local/zabbix/ #zabbix安裝目錄
:wq! #保存退出
vi /etc/rc.d/init.d/zabbix_agentd #編輯客戶端配置文件
BASEDIR=/usr/local/zabbix/ #zabbix安裝目錄
:wq! #保存退出
三、配置web站點
cd /usr/local/src/zabbix-3.0.1
cp -r /usr/local/src/zabbix-3.0.1/frontends/php /usr/local/nginx/html/zabbix
chown www.www -R /usr/local/nginx/html/zabbix
備注:/usr/local/nginx/html為Nginx默認站點目錄 www為Nginx運行賬戶
service zabbix_server start #啟動zabbix服務端
service zabbix_agentd start #啟動zabbix客戶端
四、修改php配置文件參數
1、vi /etc/php.ini #編輯修改
post_max_size=16M
max_execution_time=300
max_input_time=300
:wq! #保存退出
2、vi /usr/local/php/etc/php-fpm.conf #編輯修改
request_terminate_timeout=300
:wq! #保存退出
service php-fpm reload #重啟php-fpm
五、安裝web
在瀏覽器中打開:
下面是部分安裝截圖
Next step
系統運維 www.osyunwei.com 溫馨提醒:qihang01原創內容?版權所有,轉載請注明出處及原文鏈接
檢查系統環境設置,必須全部都為ok,才能繼續
Next step
配置MySQL數據庫信息
Database:MySQL
Database host:localhost
Database port:3306
Database name:zabbix
User:zabbix
Password:123456
Next step
Next step
Next step
Finish 安裝完成
賬號:admin
密碼:zabbix
Sign in 登錄
如下圖所示
修改web界面為中文
1、修改系統配置文件,讓web頁面支持簡體中文顯示
vi /usr/local/nginx/html/zabbix/include/locales.inc.php #編輯修改
'zh_CN'=> array('name'=> _('Chinese (zh_CN)'), 'display'=> false),
修改為
'zh_CN'=> array('name'=> _('Chinese (zh_CN)'), 'display'=> true),
:wq! #保存退出
2、替換監控圖像上系統默認的字體 #默認字體不支持中文,如果不替換,圖像上會顯示亂碼
在Windows系統中的C:\Windows\Fonts目錄中復制出一個中文字體文件,例如msyh.ttf
把字體文件msyh.ttf上傳到zabbix站點根目錄下fonts文件夾中
例如:/usr/local/nginx/html/zabbix/fonts
備份默認的字體文件:DejaVusSans.ttf-bak
修改msyh.ttf名稱為DejaVusSans.ttf
最后,修改配置信息,把默認語言修改為中文
Language:Chinese(zh_CN) #簡體中文
至此,Zabbix3.x安裝圖解教程完成。
信公眾號:思快奇
?
前言
有人說人生在世要“與時俱進”,還有人說”識時務者為俊杰”。然而作為金融IT從業者,卻發現實際并不是這樣。
我們都知道金融系統瞬息萬變,稍微有點風吹草動就會反映在市場上,這些通過大盤的漲跌可見一斑,但是后面保障金融數據穩定呈現的金融信息系統卻猶如大山一般,任你外面互聯網技術如何更新換代、風云變幻,它卻巋然不動,當然這里面的原因有很多,最主要的還是求“穩”。比如銀行、券商和基金公司,里面的信息系統普遍較老舊,甚至充斥著大量的Windows服務器。
近年來金融公司的互聯網轉型也如火如荼的進行著,各種創新業務層出不窮,而這也依賴新型的架構支撐。業務的激增帶來的是海量的數據和服務,以及服務器資源的擴容等,其中“自動化”就顯得尤為重要。
本著新技術的調研,以及實現自動化的目標,我們引入了docker容器來管理應用,并結合zabbix監控部署嘗試容器化,遂成此文。
為什么要容器化?
很多高大上的金融公司,大部分情況下一個系統通常從商務立項到開發實施到運維上線僅由一個或者幾個人來負責,從而形成的大大小小系統生態。不僅對技術人員要求較高,而且自動化測試、自動化運維程度低,系統出現異常時定位問題慢耗時久。
通常一個服務上線是一臺PC一套應用環境,不易復制,搭建環境麻煩。而且資源利用率低。即使利用虛擬化,后期也會由OS消耗大量的計算、存儲資源和物理機的運維成本相當。并且這種部署方式擴容慢,遇到突發流量,疲于奔命,系統遷移慢且繁瑣。我們之前有遇到系統是window服務器的,需要擴容20臺,一臺臺系統通過離線電話激活,可想而知的痛苦。
而實現容器化,可以做到應用隔離,某個服務消耗資源不會占用其他應用資源。可移植支持多種云部署公有云,私有云,混合云,多重云;可擴展: 模塊化,插件化,可掛載,可組合。在自動化方面表現尤為突出,利用dockerswarm,mesos,k8s等跨服務器的編排工具可以實現自動部署,自動重啟,自動復制,自動伸縮擴展。其中docker和虛擬機對比如下:
企業級應用容器化,一般怎么做?
1、部署docker應用,省去依賴環境的配置。由依賴JVM、weblogic、Nginx等二進制來部署的,過渡到基于docker鏡像的單主機模式使用Docker部署。鏡像倉庫服務,本地打包好上傳,服務器直接拉取即可,和yum安裝軟件一樣簡單。
2、用docker compose來管理各個應用的容器部署,部署docker swarm集群,實現容器集群管理,由單機容器到集群容器。
3、通過K8s這種分布式系統管理平臺,實現各容器服務的調度和管理。K8S的抽象性允許將容器化的應用程序部署到集群,而不必專門將其綁定到單個計算機??梢詫崿F自動部署,自動重啟,自動復制,自動伸縮/擴展等智能運維操作。
以上三步,每一步的調升都是一個技術棧的升級,千里之行始于足下,本文實現基于docker的zabbix監控部署,即完成容器化戰略的第一步。
基于docker的zabbix監控部署實現
zabbix是一個基于WEB界面的提供分布式系統監視以及網絡監視功能的企業級的開源解決方案。通過C/S模式采集數據通過B/S模式在web端展示和配置,通過SNMP協議傳輸,而被監控對象只需要支持SNMP協議或者運行Zabbix-agents代理程序即可。
既然是分布式監控系統,那么用分布式部署方案才能發揮其最大功效,官方通過Zabbix proxies提供有效可用的分布式監控。zabbix proxy 可以代替 zabbix server 收集性能和可用性數據,然后把數據匯報給 zabbix server,并且在一定程度上分擔了zabbix server 的壓力。架構如下:
假設我們有五個生產網段需要監控,分別為第三方外聯區、DMZ、F5前置、業務網段、應用網段。那么至少需要部署一臺zabbix-server、5臺zabbix-proxy,根據需要部署zabbix-agent若干。
部署步驟
參考zabbix官網配置建議,要監控1000+臺服務器,以頻率1件每秒的告警頻率,歷史趨勢數據存儲90天,我們需要內存4g,硬盤300G,8核CPU即可滿足server要求,而proxy只是數據轉發,我們硬盤用100G也綽綽有余了,所需資源情況如下:
一、配置環境準備:
1、因為需要掛在本地目錄到docker容器,所以需要關閉selinux #setenforce 0 永久關閉 vi /etc/sysconfig/selinux SELINUX=enforcing 改為 SELINUX=disabled 2、關閉防火墻或者增加防火墻規則 3、添加zabbix用戶和組,做到最小權限原則 #groupadd zabbix #useradd -g zabbix zabbix 如果普通用戶下docker不可使用將用戶加入docker組 #groupadd docker #gpasswd -a zabbix docker 重啟docker生效 #systemctl restart docker 4、環境準備好后,情況如下: [zabbix@N-VM-ZABBIXSRV ~]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE [zabbix@N-VM-ZABBIXSRV ~]$ docker -v Docker version 1.13.1, build b2f74b2/1.13.1
二、server部署
由于我們網絡規則限制,這里server需要的docker鏡像是離線下載好再導入的,Load三個鏡像:mysql5.7(數據庫)、zabbix-server(服務接口)、zabbix-web-nginx(前置頁面)
直接啟動相關即可
三、proxy部署
Proxy服務器同樣通過離線方式導入需要的mysql5.7(必須與server分離)、zabbix-proxy的鏡像,并啟動服務即可,不過為了安全通訊,這里server和proxy我們使用了共享密鑰方式部署(配置注意參見附錄部分)
通過簡單的鏡像導入與啟動即可快捷的使用到zabbix監控服務器,只要培訓運維人員執行腳本啟動和停止就可以部署發布了,啟動停止時間秒級,這在使用docker之前是不可以想象的便捷,下圖便是添加了路由器和幾臺主機通過proxy方式接入的配置狀態。
總結
一種技術的選擇,就像兩地交通工具的選擇,比如從上海到北京,有汽車、火車、飛機等等,每一種方式都可以到達,但是耗費的經歷和時間是不一樣的,當然花費的錢財和人力也不一樣,以前一個項目的上線部署需要10幾天,用docker容器化后可能只要一兩天。尤其是在擴展方面,在server服務器上用docker可以一個腳本就再起一個server服務只不過換一個端口即可,可以用于測試或者升級亦或是升級前的回退備份,但是傳統方式可能需要另準備一臺獨立的服務器或者虛擬機了。當然要面實現信息系統的容器化和自動化還有更多的事情等著我們去挑戰,路漫漫其修遠兮,吾將上下而求索,我們已經邁出了這一步
附錄1 中文亂碼問題
我們切換了中文語言后會發現圖形里面有不可失敗的亂碼,主要是因為web的字體配置不支持引起,通過上傳中文字體文件和更改defines.inc.php配置信息即可解決:
上傳字體文件和更改defines.inc.php配置信息
# docker cp simkai.ttf zabbix-web-nginx-mysql:/usr/share/zabbix/assets/fonts/. # docker cp defines.inc.php zabbix-web-nginx-mysql:/usr/share/zabbix/include/.
中文字體取的是windows自帶的楷體
重啟服務,中文亂碼問題解決
附錄2 Proxy配置注意事項
附錄 3 zabbix相關啟動腳本
------------------------------server服務------------------- 1. 首先,啟動空的 MySQL 服務器實例。 # docker run --name mysql-server -t \ -p 3306:3306 \ -v /var/zdocker/data:/var/lib/mysql \ -e MYSQL_DATABASE="zabbix" \ -e MYSQL_USER="zabbix" \ -e MYSQL_PASSWORD="zabbix" \ -e MYSQL_ROOT_PASSWORD="root" \ -d mysql:5.7 \ --character-set-server=utf8 --collation-server=utf8_bin 2. 然后,啟動 Zabbix server 實例,并將其關聯到已創建的 MySQL server 實例。 # docker run --name zabbix-server-mysql -t \ -e DB_SERVER_HOST="mysql-server" \ -e MYSQL_DATABASE="zabbix" \ -e MYSQL_USER="zabbix" \ -e MYSQL_PASSWORD="zabbix" \ -e MYSQL_ROOT_PASSWORD="root" \ --link mysql-server:mysql \ -p 10051:10051 \ -d zabbix/zabbix-server-mysql:latest Zabbix server 實例將 10051/TCP 端口(Zabbix trapper)暴露給主機。 3. 最后,啟動 Zabbix Web 界面,并將其關聯到已創建的 MySQL server 和 Zabbix server 實例。 # docker run --name zabbix-web-nginx-mysql -t \ -e DB_SERVER_HOST="mysql-server" \ -e MYSQL_DATABASE="zabbix" \ -e MYSQL_USER="zabbix" \ -e MYSQL_PASSWORD="zabbix" \ -e MYSQL_ROOT_PASSWORD="root" \ --link mysql-server:mysql \ --link zabbix-server-mysql:zabbix-server \ -p 80:80 \ -d zabbix/zabbix-web-nginx-mysql:latest ------------------------proxy服務--------------------------- 生成psk密鑰并寫入文件zabbix_proxy.psk #openssl rand -hex 32 1. mysql 安裝同server 2. proxy 安裝 # docker run --name zabbix-proxy-mysql -t \ -e DB_SERVER_HOST="mysql-server" \ -e MYSQL_DATABASE="zabbix" \ -e MYSQL_USER="zabbix" \ -e MYSQL_PASSWORD="zabbix" \ -e MYSQL_ROOT_PASSWORD="root" \ -e ZBX_SERVER_HOST="HOSTIP" \ -e ZBX_SERVER_PORT="10051" \ -e ZBX_TLSCONNECT="psk" \ -e ZBX_TLSPSKIDENTITY="zbxproxy" \ -e ZBX_TLSPSKFILE="zabbix_proxy.psk" \ -e ZBX_HOSTNAME="zbxproxy" \ -e ZBX_CONFIGFREQUENCY="90" \ -v /var/zdocker/enc:/var/lib/zabbix/enc \ --link mysql-server:mysql \ -p 10051:10051 \ -d zabbix/zabbix-proxy-mysql:latest Zabbix proxy 實例將 10051/TCP 端口(Zabbix trapper)暴露給主機。
參考資料:
1、zabbix官網文檔
2、docker官網文檔
3、其他互聯網相關檢索資料
equests 唯一的一個非轉基因的 Python HTTP 庫,人類可以安全享用。
警告:非專業使用其他 HTTP 庫會導致危險的副作用,包括:安全缺陷癥、冗余代碼癥、重新發明輪子癥、啃文檔癥、抑郁、頭疼、甚至死亡。
可怕吧。所以趕緊用起超級好用的Request庫。
Requests是用python語言基于urllib編寫的,采用的是Apache2 Licensed開源協議的HTTP庫。
Requests 允許你發送 HTTP/1.1 請求,無需手工勞動。你不需要手動為 URL 添加查詢字串,也不需要對 POST 數據進行表單編碼。Keep-alive 和 HTTP 連接池的功能是 100% 自動化的,一切動力都來自于根植在 Requests 內部的 urllib3。
Requests 完全滿足今日 web 的需求。
Requests 支持 Python 2.6—2.7以及3.3—3.7,而且能在 PyPy 下完美運行。
使用 Requests 發送網絡請求非常簡單。
一開始要導入 Requests 模塊:
import requests
然后,嘗試獲取某個網頁。本例子中,我們來獲取 Github 的公共時間線:
r=requests.get('https://github.com/timeline.json')
現在,我們有一個名為 r 的 Response 對象。我們可以從這個對象中獲取所有我們想要的信息。
Requests 簡便的 API 意味著所有 HTTP 請求類型都是顯而易見的。例如,你可以這樣發送一個 HTTP POST 請求:
r=requests.post("http://httpbin.org/post")
漂亮,對吧?那么其他 HTTP 請求類型:PUT,DELETE,HEAD 以及 OPTIONS 又是如何的呢?都是一樣的簡單:
r=requests.put("http://httpbin.org/put") r=requests.delete("http://httpbin.org/delete") r=requests.head("http://httpbin.org/get") r=requests.options("http://httpbin.org/get")
都很不錯吧,但這也僅是 Requests 的冰山一角呢。
import requests response=requests.get("https://www.baidu.com") print(type(response)) print(response.status_code) print(type(response.text)) print(response.text) print(response.cookies) print(response.content) print(response.content.decode("utf-8"))
我們可以看出response使用起來確實非常方便,這里有個問題需要注意一下:
很多情況下的網站如果直接response.text會出現亂碼的問題,所以這個使用response.content
這樣返回的數據格式其實是二進制格式,然后通過decode()轉換為utf-8,這樣就解決了通過response.text直接返回顯示亂碼的問題。
請求發出后,Requests 會基于 HTTP 頭部對響應的編碼作出有根據的推測。當你訪問 response.text 之時,Requests 會使用其推測的文本編碼。你可以找出 Requests 使用了什么編碼,并且能夠使用 response.encoding 屬性來改變它.如:
response=requests.get("http://www.baidu.com") response.encoding="utf-8" print(response.text)
不管是通過response.content.decode("utf-8)的方式還是通過response.encoding="utf-8"的方式都可以避免亂碼的問題發生。
import requests response=requests.get("http://httpbin.org/get?name=zhaofan&age=23") print(response.text)
如果我們想要在URL查詢字符串傳遞數據,通常我們會通過 httpbin.org/get?key=val 方式傳遞。Requests模塊允許使用params關鍵字傳遞參數,以一個字典來傳遞這些參數,例子如下:
import requests data={ "name":"zhaofan", "age":22 } response=requests.get("http://httpbin.org/get",params=data) print(response.url) print(response.text)
上述兩種的結果是相同的,通過params參數傳遞一個字典內容,從而直接構造url。
注意:第二種方式通過字典的方式的時候,如果字典中的參數為None則不會添加到url上。
Requests 中也有一個內置的 JSON 解碼器,助你處理 JSON 數據:
import requests import json response=requests.get("http://httpbin.org/get") print(type(response.text)) print(response.json()) print(json.loads(response.text)) print(type(response.json()))
從結果可以看出requests里面集成的json其實就是執行了json.loads()方法,兩者的結果是一樣的。
如果 JSON 解碼失敗。r.json() 就會拋出一個異常。例如,響應內容是 401 (Unauthorized),嘗試訪問 r.json() 將會拋出 ValueError: No JSON object could be decoded 異常。
需要注意的是,成功調用 r.json() 并不意味著響應的成功。有的服務器會在失敗的響應中包含一個 JSON 對象(比如 HTTP 500 的錯誤細節)。這種 JSON 會被解碼返回。要檢查請求是否成功,請使用 r.raise_for_status() 或者檢查 r.status_code 是否和你的期望相同。
和前面我們將urllib模塊的時候一樣,我們同樣可以定制headers的信息,如當我們直接通過requests請求知乎網站的時候,默認是無法訪問的。
import requests response=requests.get("https://www.zhihu.com") print(response.text)
因為訪問知乎需要頭部信息,這個時候我們在谷歌瀏覽器里輸入chrome://version,就可以看到用戶代理,將用戶代理添加到頭部信息。
如果你想為請求添加 HTTP 頭部,只要簡單地傳遞一個 dict 給 headers 參數就可以了。
import requests headers={ "User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" } response=requests.get("https://www.zhihu.com",headers=headers) print(response.text)
這樣就可以正常的訪問知乎了。
注意: 定制 header 的優先級低于某些特定的信息源,例如:
更進一步講,Requests 不會基于定制 header 的具體情況改變自己的行為。只不過在最后的請求中,所有的 header 信息都會被傳遞進去。
注意: 所有的 header 值必須是 string、bytestring 或者 unicode。盡管傳遞 unicode header 也是允許的,但不建議這樣做。
你也許經常想為 URL 的查詢字符串(query string)傳遞某種數據。如果你是手工構建 URL,那么數據會以鍵/值對的形式置于 URL 中,跟在一個問號的后面。例如, httpbin.org/get?key=val。 Requests 允許你使用 params 關鍵字參數,以一個字符串字典來提供這些參數。舉例來說,如果你想傳遞 key1=value1 和 key2=value2 到 httpbin.org/get ,那么你可以使用如下代碼:
payload={'key1': 'value1', 'key2': 'value2'} r=requests.get("http://httpbin.org/get", params=payload)
通過打印輸出該 URL,你能看到 URL 已被正確編碼:
print(r.url) http://httpbin.org/get?key2=value2&key1=value1
注意字典里值為 None 的鍵都不會被添加到 URL 的查詢字符串里。
你還可以將一個列表作為值傳入:
payload={'key1': 'value1', 'key2': ['value2', 'value3']} r=requests.get('http://httpbin.org/get', params=payload) print(r.url) http://httpbin.org/get?key1=value1&key2=value2&key2=value3
我們能讀取服務器響應的內容。再次以 GitHub 時間線為例:
import requests r=requests.get('https://github.com/timeline.json') r.text u'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests 會自動解碼來自服務器的內容。大多數 unicode 字符集都能被無縫地解碼。
請求發出后,Requests 會基于 HTTP 頭部對響應的編碼作出有根據的推測。當你訪問 r.text 之時,Requests 會使用其推測的文本編碼。你可以找出 Requests 使用了什么編碼,并且能夠使用 r.encoding 屬性來改變它:
r.encoding r.encoding='ISO-8859-1'
如果你改變了編碼,每當你訪問 r.text ,Request 都將會使用 r.encoding 的新值。你可能希望在使用特殊邏輯計算出文本的編碼的情況下來修改編碼。比如 HTTP 和 XML 自身可以指定編碼。這樣的話,你應該使用 r.content 來找到編碼,然后設置 r.encoding 為相應的編碼。這樣就能使用正確的編碼解析 r.text 了。
在你需要的情況下,Requests 也可以使用定制的編碼。如果你創建了自己的編碼,并使用 codecs 模塊進行注冊,你就可以輕松地使用這個解碼器名稱作為 r.encoding 的值, 然后由 Requests 來為你處理編碼。
你也能以字節的方式訪問請求響應體,對于非文本請求:
r.content b'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests 會自動為你解碼 gzip 和 deflate 傳輸編碼的響應數據。
例如,以請求返回的二進制數據創建一張圖片,你可以使用如下代碼:
from PIL import Image from io import BytesIO i=Image.open(BytesIO(r.content))
在罕見的情況下,你可能想獲取來自服務器的原始套接字響應,那么你可以訪問 r.raw。 如果你確實想這么干,那請你確保在初始請求中設置了 stream=True。具體你可以這么做:
r=requests.get('https://github.com/timeline.json', stream=True) r.raw <requests.packages.urllib3.response.HTTPResponse object at 0x101194810> r.raw.read(10) '\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
但一般情況下,你應該以下面的模式將文本流保存到文件:
with open(filename, 'wb') as fd: for chunk in r.iter_content(chunk_size): fd.write(chunk)
使用 Response.iter_content 將會處理大量你直接使用 Response.raw 不得不處理的。 當流下載時,上面是優先推薦的獲取內容方式。 Note that chunk_size can be freely adjusted to a number that may better fit your use cases.
通常,你想要發送一些編碼為表單形式的數據——非常像一個 HTML 表單。要實現這個,只需簡單地傳遞一個字典給 data 參數。你的數據字典在發出請求時會自動編碼為表單形式:
>>> payload={'key1': 'value1', 'key2': 'value2'} >>> r=requests.post("http://httpbin.org/post", data=payload) >>> print(r.text) { ... "form": { "key2": "value2", "key1": "value1" }, ... }
你還可以為 data 參數傳入一個元組列表。在表單中多個元素使用同一 key 的時候,這種方式尤其有效:
>>> payload=(('key1', 'value1'), ('key1', 'value2')) >>> r=requests.post('http://httpbin.org/post', data=payload) >>> print(r.text) { ... "form": { "key1": [ "value1", "value2" ] }, ... }
很多時候你想要發送的數據并非編碼為表單形式的。如果你傳遞一個 string 而不是一個 dict,那么數據會被直接發布出去。
例如,Github API v3 接受編碼為 JSON 的 POST/PATCH 數據:
>>> import json >>> url='https://api.github.com/some/endpoint' >>> payload={'some': 'data'} >>> r=requests.post(url, data=json.dumps(payload))
此處除了可以自行對 dict 進行編碼,你還可以使用 json 參數直接傳遞,然后它就會被自動編碼。這是 2.4.2 版的新加功能:
>>> url='https://api.github.com/some/endpoint' >>> payload={'some': 'data'} >>> r=requests.post(url, json=payload)
Requests 使得上傳多部分編碼文件變得很簡單:
>>> url='http://httpbin.org/post' >>> files={'file': open('report.xls', 'rb')} >>> r=requests.post(url, files=files) >>> r.text { ... "files": { "file": "<censored...binary...data>" }, ... }
你可以顯式地設置文件名,文件類型和請求頭:
>>> url='http://httpbin.org/post' >>> files={'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})} >>> r=requests.post(url, files=files) >>> r.text { ... "files": { "file": "<censored...binary...data>" }, ... }
如果你想,你也可以發送作為文件來接收的字符串:
>>> url='http://httpbin.org/post' >>> files={'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')} >>> r=requests.post(url, files=files) >>> r.text { ... "files": { "file": "some,data,to,send\\nanother,row,to,send\\n" }, ... }
如果你發送一個非常大的文件作為 multipart/form-data 請求,你可能希望將請求做成數據流。默認下 requests 不支持, 但有個第三方包 requests-toolbelt 是支持的。你可以閱讀 toolbelt 文檔 來了解使用方法。
警告
我們強烈建議你用二進制模式(binary mode)打開文件。這是因為 Requests 可能會試圖為你提供 Content-Length header,在它這樣做的時候,這個值會被設為文件的字節數(bytes)。如果用文本模式(text mode)打開文件,就可能會發生錯誤。
8、響應狀態碼
我們可以檢測響應狀態碼:
>>> r=requests.get('http://httpbin.org/get') >>> r.status_code 200
為方便引用,Requests還附帶了一個內置的狀態碼查詢對象:
>>> r.status_code==requests.codes.ok True
如果發送了一個錯誤請求(一個 4XX 客戶端錯誤,或者 5XX 服務器錯誤響應),我們可以通過 Response.raise_for_status() 來拋出異常:
>>> bad_r=requests.get('http://httpbin.org/status/404') >>> bad_r.status_code 404 >>> bad_r.raise_for_status() Traceback (most recent call last): File "requests/models.py", line 832, in raise_for_status raise http_error requests.exceptions.HTTPError: 404 Client Error
但是,由于我們的例子中 r 的 status_code 是 200 ,當我們調用 raise_for_status() 時,得到的是:
>>> r.raise_for_status() None
我們可以查看以一個 Python 字典形式展示的服務器響應頭:
>>> r.headers { 'content-encoding': 'gzip', 'transfer-encoding': 'chunked', 'connection': 'close', 'server': 'nginx/1.0.4', 'x-runtime': '148ms', 'etag': '"e1ca502697e5c9317743dc078f67693f"', 'content-type': 'application/json' }
但是這個字典比較特殊:它是僅為 HTTP 頭部而生的。根據 RFC 2616, HTTP 頭部是大小寫不敏感的。
因此,我們可以使用任意大寫形式來訪問這些響應頭字段:
>>> r.headers['Content-Type'] 'application/json' >>> r.headers.get('content-type') 'application/json'
它還有一個特殊點,那就是服務器可以多次接受同一 header,每次都使用不同的值。但 Requests 會將它們合并,這樣它們就可以用一個映射來表示出來,參見 RFC 7230:
A recipient MAY combine multiple header fields with the same field name into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field value to the combined field value in order, separated by a comma.
接收者可以合并多個相同名稱的 header 欄位,把它們合為一個 "field-name: field-value" 配對,將每個后續的欄位值依次追加到合并的欄位值中,用逗號隔開即可,這樣做不會改變信息的語義。
如果某個響應中包含一些 cookie,你可以快速訪問它們:
>>> url='http://example.com/some/cookie/setting/url' >>> r=requests.get(url) >>> r.cookies['example_cookie_name'] 'example_cookie_value'
要想發送你的cookies到服務器,可以使用 cookies 參數:
>>> url='http://httpbin.org/cookies' >>> cookies=dict(cookies_are='working') >>> r=requests.get(url, cookies=cookies) >>> r.text '{"cookies": {"cookies_are": "working"}}'
Cookie 的返回對象為 RequestsCookieJar,它的行為和字典類似,但界面更為完整,適合跨域名跨路徑使用。你還可以把 Cookie Jar 傳到 Requests 中:
>>> jar=requests.cookies.RequestsCookieJar() >>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies') >>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere') >>> url='http://httpbin.org/cookies' >>> r=requests.get(url, cookies=jar) >>> r.text '{"cookies": {"tasty_cookie": "yum"}}'
默認情況下,除了 HEAD, Requests 會自動處理所有重定向。
可以使用響應對象的 history 方法來追蹤重定向。
Response.history 是一個 Response 對象的列表,為了完成請求而創建了這些對象。這個對象列表按照從最老到最近的請求進行排序。
例如,Github 將所有的 HTTP 請求重定向到 HTTPS:
>>> r=requests.get('http://github.com') >>> r.url 'https://github.com/' >>> r.status_code 200 >>> r.history [<Response [301]>]
如果你使用的是GET、OPTIONS、POST、PUT、PATCH 或者 DELETE,那么你可以通過 allow_redirects 參數禁用重定向處理:
>>> r=requests.get('http://github.com', allow_redirects=False) >>> r.status_code 301 >>> r.history []
如果你使用了 HEAD,你也可以啟用重定向:
>>> r=requests.head('http://github.com', allow_redirects=True) >>> r.url 'https://github.com/' >>> r.history [<Response [301]>]
你可以告訴 requests 在經過以 timeout 參數設定的秒數時間之后停止等待響應?;旧纤械纳a代碼都應該使用這一參數。如果不使用,你的程序可能會永遠失去響應:
>>> requests.get('http://github.com', timeout=0.001) Traceback (most recent call last): File "<stdin>", line 1, in <module> requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
注意
timeout 僅對連接過程有效,與響應體的下載無關。 timeout 并不是整個下載響應的時間限制,而是如果服務器在 timeout 秒內沒有應答,將會引發一個異常(更精確地說,是在 timeout 秒內沒有從基礎套接字上接收到任何字節的數據時)If no timeout is specified explicitly, requests do not time out.
遇到網絡問題(如:DNS 查詢失敗、拒絕連接等)時,Requests 會拋出一個 ConnectionError 異常。
如果 HTTP 請求返回了不成功的狀態碼, Response.raise_for_status() 會拋出一個 HTTPError 異常。
若請求超時,則拋出一個 Timeout 異常。
若請求超過了設定的最大重定向次數,則會拋出一個 TooManyRedirects 異常。
所有Requests顯式拋出的異常都繼承自 requests.exceptions.RequestException 。
會話對象讓你能夠跨請求保持某些參數。它也會在同一個 Session 實例發出的所有請求之間保持 cookie, 期間使用 urllib3 的 connection pooling 功能。所以如果你向同一主機發送多個請求,底層的 TCP 連接將會被重用,從而帶來顯著的性能提升。 (參見 HTTP persistent connection).
會話對象具有主要的 Requests API 的所有方法。
我們來跨請求保持一些 cookie:
s=requests.Session() s.get('http://httpbin.org/cookies/set/sessioncookie/123456789') r=s.get("http://httpbin.org/cookies") print(r.text) # '{"cookies": {"sessioncookie": "123456789"}}'
會話也可用來為請求方法提供缺省數據。這是通過為會話對象的屬性提供數據來實現的:
s=requests.Session() s.auth=('user', 'pass') s.headers.update({'x-test': 'true'}) # both 'x-test' and 'x-test2' are sent s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
任何你傳遞給請求方法的字典都會與已設置會話層數據合并。方法層的參數覆蓋會話的參數。
不過需要注意,就算使用了會話,方法級別的參數也不會被跨請求保持。下面的例子只會和第一個請求發送 cookie ,而非第二個:
s=requests.Session() r=s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'}) print(r.text) # '{"cookies": {"from-my": "browser"}}' r=s.get('http://httpbin.org/cookies') print(r.text) # '{"cookies": {}}'
如果你要手動為會話添加 cookie,就使用 Cookie utility 函數 來操縱 Session.cookies。
會話還可以用作前后文管理器:
with requests.Session() as s: s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
這樣就能確保 with 區塊退出后會話能被關閉,即使發生了異常也一樣。
從字典參數中移除一個值:有時你會想省略字典參數中一些會話層的鍵。要做到這一點,你只需簡單地在方法層參數中將那個鍵的值設置為 None ,那個鍵就會被自動省略掉。
包含在一個會話中的所有數據你都可以直接使用。學習更多細節請閱讀 會話 API 文檔。
任何時候進行了類似 requests.get() 的調用,你都在做兩件主要的事情。其一,你在構建一個 Request 對象, 該對象將被發送到某個服務器請求或查詢一些資源。其二,一旦 requests 得到一個從服務器返回的響應就會產生一個 Response 對象。該響應對象包含服務器返回的所有信息,也包含你原來創建的 Request 對象。如下是一個簡單的請求,從 Wikipedia 的服務器得到一些非常重要的信息:
>>> r=requests.get('http://en.wikipedia.org/wiki/Monty_Python')
如果想訪問服務器返回給我們的響應頭部信息,可以這樣做:
>>> r.headers {'Content-Length': '74016', 'Content-language': 'en', 'X-Powered-By': 'HHVM/3.18.6-dev', 'X-Cache-Status': 'hit-front', 'Last-Modified': 'Sun, 18 Mar 2018 03:21:10 GMT', 'X-Client-IP': '58.22.60.34', 'Link': '</static/images/project-logos/enwiki.png>;rel=preload;as=image;media=not all and (min-resolution: 1.5dppx),</static/images/project-logos/enwiki-1.5x.png>;rel=preload;as=image;media=(min-resolution: 1.5dppx) and (max-resolution: 1.999999dppx),</static/images/project-logos/enwiki-2x.png>;rel=preload;as=image;media=(min-resolution: 2dppx)', 'Date': 'Mon, 26 Mar 2018 07:59:49 GMT', 'Accept-Ranges': 'bytes', 'X-Varnish': '344560760 788283452, 149384905 105598013, 1070294656 1052310325, 958221504 648703599', 'X-Cache': 'cp1065 hit/9, cp2007 hit/5, cp4032 hit/1, cp4029 hit/40', 'Set-Cookie': 'GeoIP=CN:FJ:Xiamen:24.48:118.08:v4; Path=/; secure; Domain=.wikipedia.org', 'Age': '114692', 'Strict-Transport-Security': 'max-age=106384710; includeSubDomains; preload', 'Server': 'mw1261.eqiad.wmnet', 'Connection': 'keep-alive', 'P3P': 'CP="This is not a P3P policy! See https://en.wikipedia.org/wiki/Special:CentralAutoLogin/P3P for more info."', 'Via': '1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)', 'X-Analytics': 'ns=0;page_id=18942;WMF-Last-Access=26-Mar-2018;WMF-Last-Access-Global=26-Mar-2018;https=1', 'X-Content-Type-Options': 'nosniff', 'Content-Encoding': 'gzip', 'Vary': 'Accept-Encoding,Cookie,Authorization', 'X-UA-Compatible': 'IE=Edge', 'Cache-Control': 'private, s-maxage=0, max-age=0, must-revalidate', 'Content-Type': 'text/html; charset=UTF-8', 'Backend-Timing': 'D=117879 t=1521768700105575'}
然而,如果想得到發送到服務器的請求的頭部,我們可以簡單地訪問該請求,然后是該請求的頭部:
>>> r.request.headers {'Connection': 'keep-alive', 'Cookie': 'WMF-Last-Access-Global=26-Mar-2018; WMF-Last-Access=26-Mar-2018', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'User-Agent': 'python-requests/2.18.4'}
當你從 API 或者會話調用中收到一個 Response 對象時,request 屬性其實是使用了 PreparedRequest。有時在發送請求之前,你需要對 body 或者 header (或者別的什么東西)做一些額外處理,下面演示了一個簡單的做法:
from requests import Request, Session s=Session() req=Request('GET', url, data=data, headers=header ) prepped=req.prepare() # do something with prepped.body # do something with prepped.headers resp=s.send(prepped, stream=stream, verify=verify, proxies=proxies, cert=cert, timeout=timeout ) print(resp.status_code)
由于你沒有對 Request 對象做什么特殊事情,你立即準備和修改了 PreparedRequest 對象,然后把它和別的參數一起發送到 requests.* 或者 Session.*。
然而,上述代碼會失去 Requests Session 對象的一些優勢, 尤其 Session 級別的狀態,例如 cookie 就不會被應用到你的請求上去。要獲取一個帶有狀態的 PreparedRequest, 請用 Session.prepare_request() 取代 Request.prepare() 的調用,如下所示:
from requests import Request, Session s=Session() req=Request('GET', url, data=data headers=headers ) prepped=s.prepare_request(req) # do something with prepped.body # do something with prepped.headers resp=s.send(prepped, stream=stream, verify=verify, proxies=proxies, cert=cert, timeout=timeout ) print(resp.status_code)
Requests 可以為 HTTPS 請求驗證 SSL 證書,就像 web 瀏覽器一樣。SSL 驗證默認是開啟的,如果證書驗證失敗,Requests 會拋出 SSLError:
>>> requests.get('https://requestb.in') requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
在該域名上我沒有設置 SSL,所以失敗了。但 Github 設置了 SSL:
>>> requests.get('https://github.com', verify=True) <Response [200]>
你可以為 verify 傳入 CA_BUNDLE 文件的路徑,或者包含可信任 CA 證書文件的文件夾路徑:
>>> requests.get('https://github.com', verify='/path/to/certfile')
或者將其保持在會話中:
s=requests.Session() s.verify='/path/to/certfile'
注解:如果 verify 設為文件夾路徑,文件夾必須通過 OpenSSL 提供的 c_rehash 工具處理。
你還可以通過 REQUESTS_CA_BUNDLE 環境變量定義可信任 CA 列表。
如果你將 verify 設置為 False,Requests 也能忽略對 SSL 證書的驗證。
>>> requests.get('https://kennethreitz.org', verify=False) <Response [200]>
默認情況下, verify 是設置為 True 的。選項 verify 僅應用于主機證書。
# 對于私有證書,你也可以傳遞一個 CA_BUNDLE 文件的路徑給 verify。你也可以設置 # REQUEST_CA_BUNDLE 環境變量。
你也可以指定一個本地證書用作客戶端證書,可以是單個文件(包含密鑰和證書)或一個包含兩個文件路徑的元組:
>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key')) <Response [200]>
或者保持在會話中:
s=requests.Session() s.cert='/path/client.cert'
如果你指定了一個錯誤路徑或一個無效的證書:
>>> requests.get('https://kennethreitz.org', cert='/wrong_path/client.pem') SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
警告:本地證書的私有 key 必須是解密狀態。目前,Requests 不支持使用加密的 key。
Requests 默認附帶了一套它信任的根證書,來自于 Mozilla trust store。然而它們在每次 Requests 更新時才會更新。這意味著如果你固定使用某一版本的 Requests,你的證書有可能已經 太舊了。
從 Requests 2.4.0 版之后,如果系統中裝了 certifi 包,Requests 會試圖使用它里邊的 證書。這樣用戶就可以在不修改代碼的情況下更新他們的可信任證書。
為了安全起見,我們建議你經常更新 certifi!
默認情況下,當你進行網絡請求后,響應體會立即被下載。你可以通過 stream 參數覆蓋這個行為,推遲下載響應體直到訪問 Response.content 屬性:
tarball_url='https://github.com/kennethreitz/requests/tarball/master' r=requests.get(tarball_url, stream=True)
此時僅有響應頭被下載下來了,連接保持打開狀態,因此允許我們根據條件獲取內容:
if int(r.headers['content-length']) < TOO_LONG: content=r.content ...
你可以進一步使用 Response.iter_content 和 Response.iter_lines 方法來控制工作流,或者以 Response.raw 從底層 urllib3 的 urllib3.HTTPResponse <urllib3.response.HTTPResponse 讀取未解碼的響應體。
如果你在請求中把 stream 設為 True,Requests 無法將連接釋放回連接池,除非你消耗了所有的數據,或者調用了 Response.close。 這樣會帶來連接效率低下的問題。如果你發現你在使用 stream=True 的同時還在部分讀取請求的 body(或者完全沒有讀取 body),那么你就應該考慮使用 with 語句發送請求,這樣可以保證請求一定會被關閉:
with requests.get('http://httpbin.org/get', stream=True) as r: # 在此處理響應。
好消息——歸功于 urllib3,同一會話內的持久連接是完全自動處理的!同一會話內你發出的任何請求都會自動復用恰當的連接!
注意:只有所有的響應體數據被讀取完畢連接才會被釋放為連接池;所以確保將 stream 設置為 False 或讀取 Response 對象的 content 屬性。
Requests支持流式上傳,這允許你發送大的數據流或文件而無需先把它們讀入內存。要使用流式上傳,僅需為你的請求體提供一個類文件對象即可:
with open('massive-body') as f: requests.post('http://some.url/streamed', data=f)
警告:我們強烈建議你用二進制模式(binary mode)打開文件。這是因為 requests 可能會為你提供 header 中的 Content-Length,在這種情況下該值會被設為文件的字節數。如果你用文本模式打開文件,就可能碰到錯誤。
對于出去和進來的請求,Requests 也支持分塊傳輸編碼。要發送一個塊編碼的請求,僅需為你的請求體提供一個生成器(或任意沒有具體長度的迭代器):
def gen(): yield 'hi' yield 'there' requests.post('http://some.url/chunked', data=gen())
對于分塊的編碼請求,我們最好使用 Response.iter_content() 對其數據進行迭代。在理想情況下,你的 request 會設置 stream=True,這樣你就可以通過調用 iter_content 并將分塊大小參數設為 None,從而進行分塊的迭代。如果你要設置分塊的最大體積,你可以把分塊大小參數設為任意整數。
你可以在一個請求中發送多個文件。例如,假設你要上傳多個圖像文件到一個 HTML 表單,使用一個多文件 field 叫做 "images":
<input type="file" name="images" multiple="true" required="true"/>
要實現,只要把文件設到一個元組的列表中,其中元組結構為 (form_field_name, file_info):
>>> url='http://httpbin.org/post' >>> multiple_files=[ ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')), ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))] >>> r=requests.post(url, files=multiple_files) >>> r.text { ... 'files': {'images': 'data:image/png;base64,iVBORw ....'} 'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a', ... }
Requests有一個鉤子系統,你可以用來操控部分請求過程,或信號事件處理。
可用的鉤子:
response: 從一個請求產生的響應
你可以通過傳遞一個 {hook_name: callback_function} 字典給 hooks 請求參數為每個請求分配一個鉤子函數:
hooks=dict(response=print_url)
callback_function 會接受一個數據塊作為它的第一個參數。
def print_url(r, *args, **kwargs): print(r.url)
若執行你的回調函數期間發生錯誤,系統會給出一個警告。
若回調函數返回一個值,默認以該值替換傳進來的數據。若函數未返回任何東西,也沒有什么其他的影響。
我們來在運行期間打印一些請求方法的參數:
>>> requests.get('http://httpbin.org', hooks=dict(response=print_url)) http://httpbin.org <Response [200]>
Requests 允許你使用自己指定的身份驗證機制。
任何傳遞給請求方法的 auth 參數的可調用對象,在請求發出之前都有機會修改請求。
自定義的身份驗證機制是作為 requests.auth.AuthBase 的子類來實現的,也非常容易定義。Requests 在 requests.auth 中提供了兩種常見的的身份驗證方案: HTTPBasicAuth 和 HTTPDigestAuth 。
假設我們有一個web服務,僅在 X-Pizza 頭被設置為一個密碼值的情況下才會有響應。雖然這不太可能,但就以它為例好了。
from requests.auth import AuthBase class PizzaAuth(AuthBase): """Attaches HTTP Pizza Authentication to the given Request object.""" def __init__(self, username): # setup any auth-related data here self.username=username def __call__(self, r): # modify and return the request r.headers['X-Pizza']=self.username return r
然后就可以使用我們的PizzaAuth來進行網絡請求:
>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth')) <Response [200]>
使用 Response.iter_lines() 你可以很方便地對流式 API (例如 Twitter 的流式 API ) 進行迭代。簡單地設置 stream 為 True 便可以使用 iter_lines 對相應進行迭代:
import json import requests r=requests.get('http://httpbin.org/stream/20', stream=True) for line in r.iter_lines(): # filter out keep-alive new lines if line: decoded_line=line.decode('utf-8') print(json.loads(decoded_line))
當使用 decode_unicode=True 在 Response.iter_lines() 或 Response.iter_content() 中時,你需要提供一個回退編碼方式,以防服務器沒有提供默認回退編碼,從而導致錯誤:
r=requests.get('http://httpbin.org/stream/20', stream=True) if r.encoding is None: r.encoding='utf-8' for line in r.iter_lines(decode_unicode=True): if line: print(json.loads(line))
警告:iter_lines 不保證重進入時的安全性。多次調用該方法會導致部分收到的數據丟失。如果你要在多處調用它,就應該使用生成的迭代器對象:
lines=r.iter_lines() # 保存第一行以供后面使用,或者直接跳過 first_line=next(lines) for line in lines: print(line)
如果需要使用代理,你可以通過為任意請求方法提供 proxies 參數來配置單個請求:
import requests proxies={ "http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080", } requests.get("http://example.org", proxies=proxies)
你也可以通過環境變量 HTTP_PROXY 和 HTTPS_PROXY 來配置代理。
$ export HTTP_PROXY="http://10.10.1.10:3128" $ export HTTPS_PROXY="http://10.10.1.10:1080" $ python >>> import requests >>> requests.get("http://example.org")
若你的代理需要使用HTTP Basic Auth,可以使用 http://user:password@host/ 語法:
proxies={ "http": "http://user:pass@10.10.1.10:3128/", }
要為某個特定的連接方式或者主機設置代理,使用 scheme://hostname 作為 key, 它會針對指定的主機和連接方式進行匹配。
proxies={'http://10.20.1.128': 'http://10.10.1.10:5323'}
注意,代理 URL 必須包含連接方式。
2.10.0 新版功能.
除了基本的 HTTP 代理,Request 還支持 SOCKS 協議的代理。這是一個可選功能,若要使用, 你需要安裝第三方庫。
你可以用 pip 獲取依賴:
$ pip install requests[socks]
安裝好依賴以后,使用 SOCKS 代理和使用 HTTP 代理一樣簡單:
proxies={ 'http': 'socks5://user:pass@host:port', 'https': 'socks5://user:pass@host:port' }
Requests 符合所有相關的規范和 RFC,這樣不會為用戶造成不必要的困難。但這種對規范的考慮導致一些行為對于不熟悉相關規范的人來說看似有點奇怪。
當你收到一個響應時,Requests 會猜測響應的編碼方式,用于在你調用 Response.text 方法時對響應進行解碼。Requests 首先在 HTTP 頭部檢測是否存在指定的編碼方式,如果不存在,則會使用 charade 來嘗試猜測編碼方式。
只有當 HTTP 頭部不存在明確指定的字符集,并且 Content-Type 頭部字段包含 text 值之時, Requests 才不去猜測編碼方式。在這種情況下, RFC 2616 指定默認字符集必須是 ISO-8859-1 。Requests 遵從這一規范。如果你需要一種不同的編碼方式,你可以手動設置 Response.encoding 屬性,或使用原始的 Response.content。
Requests 提供了幾乎所有HTTP動詞的功能:GET、OPTIONS、HEAD、POST、PUT、PATCH、DELETE。以下內容為使用 Requests 中的這些動詞以及 Github API 提供了詳細示例。
我將從最常使用的動詞 GET 開始。HTTP GET 是一個冪等方法,從給定的 URL 返回一個資源。因而,當你試圖從一個 web 位置獲取數據之時,你應該使用這個動詞。一個使用示例是嘗試從 Github 上獲取關于一個特定 commit 的信息。假設我們想獲取 Requests 的 commit a050faf 的信息。我們可以這樣去做:
>>> import requests >>> r=requests.get('https://api.github.com/repos/requests/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
我們應該確認 GitHub 是否正確響應。如果正確響應,我們想弄清響應內容是什么類型的。像這樣去做:
>>> if (r.status_code==requests.codes.ok): ... print r.headers['content-type'] ... application/json; charset=utf-8
可見,GitHub 返回了 JSON 數據,非常好,這樣就可以使用 r.json 方法把這個返回的數據解析成 Python 對象。
>>> commit_data=r.json() >>> print commit_data.keys() [u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message'] >>> print commit_data[u'committer'] {u'date': u'2012-05-10T11:10:50-07:00', u'email': u'me@kennethreitz.com', u'name': u'Kenneth Reitz'} >>> print commit_data[u'message'] makin' history
到目前為止,一切都非常簡單。嗯,我們來研究一下 GitHub 的 API。我們可以去看看文檔,但如果使用 Requests 來研究也許會更有意思一點。我們可以借助 Requests 的 OPTIONS 動詞來看看我們剛使用過的 url 支持哪些 HTTP 方法。
>>> verbs=requests.options(r.url) >>> verbs.status_code 500
額,這是怎么回事?毫無幫助嘛!原來 GitHub,與許多 API 提供方一樣,實際上并未實現 OPTIONS 方法。這是一個惱人的疏忽,但沒關系,那我們可以使用枯燥的文檔。然而,如果 GitHub 正確實現了 OPTIONS,那么服務器應該在響應頭中返回允許用戶使用的 HTTP 方法,例如:
>>> verbs=requests.options('http://a-good-website.com/api/cats') >>> print verbs.headers['allow'] GET,HEAD,POST,OPTIONS
轉而去查看文檔,我們看到對于提交信息,另一個允許的方法是 POST,它會創建一個新的提交。由于我們正在使用 Requests 代碼庫,我們應盡可能避免對它發送笨拙的 POST。作為替代,我們來玩玩 GitHub 的 Issue 特性。
本篇文檔是回應 Issue #482 而添加的。鑒于該問題已經存在,我們就以它為例。先獲取它。
>>> r=requests.get('https://api.github.com/requests/kennethreitz/requests/issues/482') >>> r.status_code 200 >>> issue=json.loads(r.text) >>> print(issue[u'title']) Feature any http verb in docs >>> print(issue[u'comments']) 3
Cool,有 3 個評論。我們來看一下最后一個評論。
>>> r=requests.get(r.url + u'/comments') >>> r.status_code 200 >>> comments=r.json() >>> print comments[0].keys() [u'body', u'url', u'created_at', u'updated_at', u'user', u'id'] >>> print comments[2][u'body'] Probably in the "advanced" section
嗯,那看起來似乎是個愚蠢之處。我們發表個評論來告訴這個評論者他自己的愚蠢。那么,這個評論者是誰呢?
>>> print comments[2][u'user'][u'login'] kennethreitz
好,我們來告訴這個叫 Kenneth 的家伙,這個例子應該放在快速上手指南中。根據 GitHub API 文檔,其方法是 POST 到該話題。我們來試試看。
>>> body=json.dumps({u"body": u"Sounds great! I'll get right on it!"}) >>> url=u"https://api.github.com/repos/requests/requests/issues/482/comments" >>> r=requests.post(url=url, data=body) >>> r.status_code 404
額,這有點古怪哈??赡芪覀冃枰炞C身份。那就有點糾結了,對吧?不對。Requests 簡化了多種身份驗證形式的使用,包括非常常見的 Basic Auth。
>>> from requests.auth import HTTPBasicAuth >>> auth=HTTPBasicAuth('fake@example.com', 'not_a_real_password') >>> r=requests.post(url=url, data=body, auth=auth) >>> r.status_code 201 >>> content=r.json() >>> print(content[u'body']) Sounds great! I'll get right on it.
太棒了!噢,不!我原本是想說等我一會,因為我得去喂我的貓。如果我能夠編輯這條評論那就好了!幸運的是,GitHub 允許我們使用另一個 HTTP 動詞 PATCH 來編輯評論。我們來試試。
>>> print(content[u"id"]) 5804413 >>> body=json.dumps({u"body": u"Sounds great! I'll get right on it once I feed my cat."}) >>> url=u"https://api.github.com/repos/requests/requests/issues/comments/5804413" >>> r=requests.patch(url=url, data=body, auth=auth) >>> r.status_code 200
非常好?,F在,我們來折磨一下這個叫 Kenneth 的家伙,我決定要讓他急得團團轉,也不告訴他是我在搗蛋。這意味著我想刪除這條評論。GitHub 允許我們使用完全名副其實的 DELETE 方法來刪除評論。我們來清除該評論。
>>> r=requests.delete(url=url, auth=auth) >>> r.status_code 204 >>> r.headers['status'] '204 No Content'
很好。不見了。最后一件我想知道的事情是我已經使用了多少限額(ratelimit)。查查看,GitHub 在響應頭部發送這個信息,因此不必下載整個網頁,我將使用一個 HEAD 請求來獲取響應頭。
>>> r=requests.head(url=url, auth=auth) >>> print r.headers ... 'x-ratelimit-remaining': '4995' 'x-ratelimit-limit': '5000' ...
很好。是時候寫個 Python 程序以各種刺激的方式濫用 GitHub 的 API,還可以使用 4995 次呢。
有時候你會碰到一些服務器,處于某些原因,它們允許或者要求用戶使用上述 HTTP 動詞之外的定制動詞。比如說 WEBDAV 服務器會要求你使用 MKCOL 方法。別擔心,Requests 一樣可以搞定它們。你可以使用內建的 .request 方法,例如:
>>> r=requests.request('MKCOL', url, data=data) >>> r.status_code 200 # Assuming your call was correct
這樣你就可以使用服務器要求的任意方法動詞了。
許多 HTTP API 都有響應頭鏈接字段的特性,它們使得 API 能夠更好地自我描述和自我顯露。
GitHub 在 API 中為 分頁 使用這些特性,例如:
>>> url='https://api.github.com/users/kennethreitz/repos?page=1&per_page=10' >>> r=requests.head(url=url) >>> r.headers['link'] '<https://api.github.com/users/kennethreitz/repos?page=2&per_page=10>; rel="next", <https://api.github.com/users/kennethreitz/repos?page=6&per_page=10>; rel="last"'
Requests 會自動解析這些響應頭鏈接字段,并使得它們非常易于使用:
>>> r.links["next"] {'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'} >>> r.links["last"] {'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}
從 v1.0.0 以后,Requests 的內部采用了模塊化設計。部分原因是為了實現傳輸適配器(Transport Adapter),你可以看看關于它的最早描述。傳輸適配器提供了一個機制,讓你可以為 HTTP 服務定義交互方法。尤其是它允許你應用服務前的配置。
Requests 自帶了一個傳輸適配器,也就是 HTTPAdapter。 這個適配器使用了強大的 urllib3,為 Requests 提供了默認的 HTTP 和 HTTPS 交互。每當 Session 被初始化,就會有適配器附著在 Session 上,其中一個供 HTTP 使用,另一個供 HTTPS 使用。
Request 允許用戶創建和使用他們自己的傳輸適配器,實現他們需要的特殊功能。創建好以后,傳輸適配器可以被加載到一個會話對象上,附帶著一個說明,告訴會話適配器應該應用在哪個 web 服務上。
>>> s=requests.Session() >>> s.mount('http://www.github.com', MyAdapter())
這個 mount 調用會注冊一個傳輸適配器的特定實例到一個前綴上面。加載以后,任何使用該會話的 HTTP 請求,只要其 URL 是以給定的前綴開頭,該傳輸適配器就會被使用到。
傳輸適配器的眾多實現細節不在本文檔的覆蓋范圍內,不過你可以看看接下來這個簡單的 SSL 用例。更多的用法,你也許該考慮為 BaseAdapter 創建子類。
Requests 開發團隊刻意指定了內部庫(urllib3)的默認 SSL 版本。一般情況下這樣做沒有問題,不過是不是你可能會需要連接到一個服務節點,而該節點使用了和默認不同的 SSL 版本。
你可以使用傳輸適配器解決這個問題,通過利用 HTTPAdapter 現有的大部分實現,再加上一個 ssl_version 參數并將它傳遞到 urllib3 中。我們會創建一個傳輸適配器,用來告訴 urllib3 讓它使用 SSLv3:
import ssl from requests.adapters import HTTPAdapter from requests.packages.urllib3.poolmanager import PoolManager class Ssl3HttpAdapter(HTTPAdapter): """"Transport adapter" that allows us to use SSLv3.""" def init_poolmanager(self, connections, maxsize, block=False): self.poolmanager=PoolManager(num_pools=connections, maxsize=maxsize, block=block, ssl_version=ssl.PROTOCOL_SSLv3)
使用默認的傳輸適配器,Requests 不提供任何形式的非阻塞 IO。 Response.content 屬性會阻塞,直到整個響應下載完成。如果你需要更多精細控制,該庫的數據流功能(見 流式請求) 允許你每次接受少量的一部分響應,不過這些調用依然是阻塞式的。
如果你對于阻塞式 IO 有所顧慮,還有很多項目可以供你使用,它們結合了 Requests 和 Python 的某個異步框架。典型的優秀例子是 grequests 和 requests-futures。
在某些特殊情況下你也許需要按照次序來提供 header,如果你向 headers 關鍵字參數傳入一個 OrderedDict,就可以向提供一個帶排序的 header。然而,Requests 使用的默認 header 的次序會被優先選擇,這意味著如果你在 headers 關鍵字參數中覆蓋了默認 header,和關鍵字參數中別的 header 相比,它們也許看上去會是次序錯誤的。
如果這個對你來說是個問題,那么用戶應該考慮在 Session 對象上面設置默認 header,只要將 Session 設為一個定制的 OrderedDict 即可。這樣就會讓它成為優選的次序。
為防止服務器不能及時響應,大部分發至外部服務器的請求都應該帶著 timeout 參數。在默認情況下,除非顯式指定了 timeout 值,requests 是不會自動進行超時處理的。如果沒有 timeout,你的代碼可能會掛起若干分鐘甚至更長時間。
連接超時指的是在你的客戶端實現到遠端機器端口的連接時(對應的是`connect()`_),Request 會等待的秒數。一個很好的實踐方法是把連接超時設為比 3 的倍數略大的一個數值,因為 TCP 數據包重傳窗口 (TCP packet retransmission window) 的默認大小是 3。
一旦你的客戶端連接到了服務器并且發送了 HTTP 請求,讀取超時指的就是客戶端等待服務器發送請求的時間。(特定地,它指的是客戶端要等待服務器發送字節之間的時間。在 99.9% 的情況下這指的是服務器發送第一個字節之前的時間)。
如果你制訂了一個單一的值作為 timeout,如下所示:
r=requests.get('https://github.com', timeout=5)
這一 timeout 值將會用作 connect 和 read 二者的 timeout。如果要分別制定,就傳入一個元組:
r=requests.get('https://github.com', timeout=(3.05, 27))
如果遠端服務器很慢,你可以讓 Request 永遠等待,傳入一個 None 作為 timeout 值,然后就沖咖啡去吧。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。