urllib.urlopen(url[,data[,proxies]])
:
https://docs.python.org/2/library/urllib.html
python
中默認自帶的網絡請求的庫就是 urlllib
系列了,包括 urllib
urllib2
以及 urllib3
,大多數狀況下三者都是互相配合一塊兒合做.html
固然也有很多優秀的第三方庫發送網絡請求,最爲大衆熟知的應該包括 requests
庫,不過本文打算從最基礎的 urllib
開始講起如何發送網絡請求,下面請讀者跟着雪之夢技術驛站的思路一塊兒動手實踐吧!python
Github
源碼地址: https://github.com/snowdreams1006/learn-python/edit/master/docs/url/urllib/teaching.md Github
在線地址: https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html 「雪之夢技術驛站」提醒您: 因爲文章具備必定的時效性,頗有可能讀者正在閱讀時,部分連接已失效,請按照文章相關說明親自實踐驗證, 盡信書不如無書,不要直接複製粘貼代碼, 必定要本身動手親自敲一遍!
<!-- toc -->nginx
本文使用的 python
環境是基於 virtualenv
實現的虛擬環境,只是爲了方便隔離不一樣環境,更好模擬真實用戶環境.git
實際開發中,能夠跟着本文一步一步操做練習搭建開發環境,也能夠直接使用系統默認環境.github
演示環境相關信息以下:web
(.env)$ python --version Python 2.7.16 (.env) $ pip --version pip 19.3.1 from ~/python/src/url/urllib/.env/lib/python2.7/site-packages/pip (python 2.7)
如下代碼在該環境運行正常,可是並不保證其餘環境與演示結果一致,因此一切仍是以實際運行結果爲準.
若是不須要虛擬環境的話,能夠忽略環境安裝這一部份內容,直接使用默認環境便可,只須要保證測試時使用的 python
版本是 python2
而非 python3
!docker
virtualenv
sudo pip install virtualenv
安裝虛擬環境方便隔離不一樣 python 環境,也可使用 系統默認環境,因此這一步是可選的,同理下面的步驟也都是可選的.
.env
virtualenv .env
虛擬環境目錄設置成隱藏目錄的目的是防止誤操做,固然也能夠設置成普通目錄那樣顯示出來.
.env
source .env/bin/activate
一旦準備好虛擬環境目錄後就須要激活該虛擬環境,這一步能夠重複操做,不會報錯!
python
與 pip
版本(.env) $ which python ~/python/src/url/urllib/.env/bin/python (.env) snowdreams1006s-MacBook-Pro:urllib snowdreams1006$ which pip ~/python/src/url/urllib/.env/bin/pip
激活虛擬環境後會自動下載相關的python
依賴,所以python
和pip
文件位置正是當前目錄.env
而不是系統默認環境,若是未開啓虛擬環境則顯示的是系統目錄.
若是讀者親測運行時發現網絡沒法正常請求,能夠將 http://httpbin.snowdreams1006.cn/ 替換成 http://httpbin.org/ 或者自行搭建本地測試環境.數據庫
下面提供兩種搭建本地測試環境的安裝方式,固然也能夠訪問 http://httpbin.snowdreams1006.cn/ 或者 http://httpbin.org/ 等在線環境.json
docker
安裝方式docker run -p 8000:80 kennethreitz/httpbin
首次運行會先將鏡像下載到本地再啓動容器,非首次運行會直接啓動容器,訪問地址: http://127.0.0.1:8000/
python
安裝方式pip install gunicorn httpbin && gunicorn httpbin:app
默認監聽端口是8000
,若是遇到端口衝突提示已被佔用,可運行gunicorn httpbin:app -b :9898
指定端口.
urllib2.urlopen(url)
: 發送最簡單的網絡請求,直接返回響應體文本數據.
新建 python
文件名爲 urllib_demo.py
,核心代碼包括先導入 urllib2
包,而後使用 urllib2.urlopen()
便可發送最簡單的 GET
請求,最後利用 response.read()
可一次性讀取響應體內容.segmentfault
代碼內容以下:
# -*- coding: utf-8 -*- import urllib import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.read() if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
假如該文件名爲urllib_demo.py
,則在終端命令行內運行python urllib_demo.py
便可查看輸出結果.
print type(response)
: 獲取對象 類型,配合基本類型可大體猜想出有哪些方法和屬性可供外部調用.
print dir(response)
: 獲取對象方法和屬性 枚舉值,無文檔猜想方法和屬性.
經過 urllib2.urlopen(url)
已經能夠發送最簡單的網絡請求了, 不管是 GET
請求仍是 POST
請求,獲取請求後的響應體無疑是很是重要的,但實際開發中一樣不可忽略的是還有其餘方法和屬性.
所以,除了掌握 response.read()
一次性所有讀取響應體內容以外,還須要知道 response
有哪些屬性和方法.
經過 type(response)
獲取對象類型再配合 dir(response)
獲取屬性枚舉值便可無文檔大體猜想對象有哪些可供調用的屬性和方法.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print type(response) print dir(response) if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
下面是 print type(response)
和 print dir(response)
的輸出內容,接下來將挑選出經常使用的屬性和方法慢慢講解.
# print type(response) <type 'instance'> # print dir(response) ['__doc__', '__init__', '__iter__', '__module__', '__repr__', 'close', 'code', 'fileno', 'fp', 'getcode', 'geturl', 'headers', 'info', 'msg', 'next', 'read', 'readline', 'readlines', 'url']
response.code
: 獲取響應對象的狀態碼,正常狀況下是200
表示請求成功,而500
是典型的系統錯誤.
經過 dir(response)
獲取屬性枚舉值,結合 type(response)
不難發現 response.code
是用來獲取響應狀態碼的,具體調用方式是 response.code
仍是 response.code()
可運行 print type(response.code)
大體推斷.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print type(response.read) print type(response.code) if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
這裏不妨結合print type(response.read)
是方法來驗證輸出結果究竟是屬性仍是方法,能夠看出response.read
是<type 'instancemethod'>
方法類型,而response.code
是<type 'int'>
基本類型,所以response.code
是屬性調用方式.
type(response.code)
的輸出結果是 <type 'int'>
並非 <type 'instancemethod'>
,所以獲取狀態碼的方式是屬性調用.
詳細代碼以下:
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.code if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.getcode()
: 獲取響應對象的狀態碼,正常狀況下是200
表示請求成功,而500
是典型的系統錯誤.
一樣地,從 print dir(response)
可知 getcode
字段可供調用,但不知道是屬性調用仍是方法調用?再次使用 `
print type(response.getcode) 獲得
<type 'instancemethod'>` 於是斷定爲方法調用形式.
詳情代碼以下:
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.getcode() if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.msg
: 獲取響應對象的狀態描述信息,例如狀態碼200
對於OK
,而500
對於INTERNAL SERVER ERROR
.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應狀態碼 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/status/200') print response.code print response.msg response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/status/500') print response.code print response.msg if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
正常請求狀態是200 OK
,而請求發生異常極可能是500 INTERNAL SERVER ERROR
,一旦出現異常如若異常處理則會報錯,程序終止運行.
response.url
: 獲取請求連接.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.url if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.geturl()
: 獲取請求連接.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.geturl() if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.headers.dict
: 獲取請求頭信息並以字典形式顯示.
在某些狀況下發送請求時必須攜帶特定的請求頭方可成功,所以須要清楚默認不設置請求頭時服務端接收到的請求頭是什麼樣子的,一樣地,可以使用 print type(response.headers)
結合 print dir(response.headers)
自行探索可供調用的屬性和方法.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.headers.dict if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.info()
: 獲取請求頭信息並以逐行顯示
和上一個 response.headers.dict
獲取請求頭信息相似,只不過 response.info()
適合肉眼顯示而非程序使用.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取請求方信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.info() if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.read()
: 一次性讀取響應體,適合響應體數據量比較小的狀況,一次性所有讀取到內存方便操做.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print response.read() if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.read()
返回的是字符串,所以能夠很方便用變量接收做後續處理,例如 result = response.read()
:
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') result = response.read() print result if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.readline()
: 逐行讀取響應體,適用於數據體比較大的狀況,循環讀取直到最終無數據可讀取爲止.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') line = response.readline() while line: print line line = response.readline() if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
response.readline()
只能逐行讀取,所以想要獲取完成的響應體須要進行手動拼接,例如:
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') result = '' line = response.readline() result = result + str(line) while line: line = response.readline() result = result + str(line) print result if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
str(line)
是爲了保證響應體字符串必定是字符串類型,其實應該沒必要如此,response.readline()
自己已是字符串類型了.
response.readlines()
: 遍歷讀取響應體,循環讀取且保存到列表對象中,適合須要逐行處理狀況.
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') for line in response.readlines(): print line if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
一樣地,若是須要針對 response.readlines()
方式獲取完整響應體結果,能夠以下進行拼接,示例以下:
# -*- coding: utf-8 -*- import urllib2 def use_simple_urllib2(): ''' 獲取響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') result = '' for line in response.readlines(): result = result + str(line) print result if __name__ == '__main__': print '>>>Use simple urllib2:' use_simple_urllib2()
上述多行代碼還能夠進一步轉換成一行代碼:
result = ''.join([line for line in response.readlines()])
GET
請求urllib2.urlopen(url)) : 只須要一個目標URL
便可發送GET
請求.
最簡單的請求方式也就是 GET
方式,不設置其餘參數的狀況下,只須要填寫 URL
便可發送請求,例如 urllib2.urlopen('http://httpbin.snowdreams1006.cn/get')
,示例代碼以下:
# -*- coding: utf-8 -*- import urllib import urllib2 def use_simple_urllib2(): ''' 獲取響應頭和響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Use simple urllib2<<<' use_simple_urllib2()
假如上述代碼文件名爲 urllib_demo.py
,在終端命令行內運行 python urllib_demo.py
文件,輸出結果以下所示:
(.env) $ python urllib_demo.py >>>Use simple urllib2<<< >>>Response Headers: Server: nginx/1.17.6 Date: Thu, 16 Jan 2020 13:38:27 GMT Content-Type: application/json Content-Length: 263 Connection: close Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true >>>Response Body: { "args": {}, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Host": "httpbin.snowdreams1006.cn", "User-Agent": "Python-urllib/2.7" }, "origin": "218.205.55.192", "url": "http://httpbin.snowdreams1006.cn/get" }
其中響應頭 Connection: close
代表鏈接是自動關閉的,而響應體 args
是空字典代表沒有查詢參數.
實際開發過程當中,不多有 GET
請求不須要攜帶參數的,對於有參數查詢的 GET
請求,原生 urllib
也是支持的,最簡單的作法是將查詢參數拼接到目標 URL
上獲得帶有查詢參數的 URL
.
# -*- coding: utf-8 -*- import urllib import urllib2 def use_params_urllib2(): ''' 獲取響應頭和響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get?param1=hello¶m2=world') print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Use params urllib2<<<' use_params_urllib2()
一樣地,假如上述代碼文件名爲 urllib_demo.py
,在終端命令行內運行 python urllib_demo.py
文件,輸出結果以下所示:
(.env) $ python urllib_demo.py >>>Use params urllib2<<< >>>Response Headers: Server: nginx/1.17.6 Date: Thu, 16 Jan 2020 13:59:23 GMT Content-Type: application/json Content-Length: 338 Connection: close Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true >>>Response Body: { "args": { "param1": "hello", "param2": "world" }, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Host": "httpbin.snowdreams1006.cn", "User-Agent": "Python-urllib/2.7" }, "origin": "218.205.55.192", "url": "http://httpbin.snowdreams1006.cn/get?param1=hello¶m2=world" }
其中響應頭 Connection: close
代表鏈接是自動關閉的,而響應體 args
再也不是空字典而是剛纔傳遞的查詢參數代表服務端確實接收到發送的查詢參數了,因此這種方式也是可行的.
若是查詢參數很是多,直接在請求連接 URL
基礎上拼接造成新的 URL
將會顯示很是繁瑣,並且必須遵照 ?param1=hello¶m2=world
這種格式,因此這種繁瑣的拼接工做就交給程序去完成吧!
# -*- coding: utf-8 -*- import urllib import urllib2 def use_params_urllib2(): ''' 獲取響應頭和響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get?param1=hello¶m2=world&author=snowdreams1006&website=http://blog.snowdreams1006.cn&url=snowdreams1006.github.io/learn-python/url/urllib/teaching.html&wechat=snowdreams1006&email=snowdreams1006@163.com&github=https://github.com/snowdreams1006/') print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Use params urllib2<<<' use_params_urllib2()
上述繁瑣不只體如今拼接成新的 URL
時長度過長容器出錯,還會遇到動態查詢參數替換的問題,因此自動拼接查詢參數功能真的是及時雨!
params = urllib.urlencode({ 'param1': 'hello', 'param2': 'world', 'author':'snowdreams1006', 'website':'http://blog.snowdreams1006.cn', 'url':'https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html', 'wechat':'snowdreams1006', 'email':'snowdreams1006@163.com', 'github':'https://github.com/snowdreams1006/' }) print params
urllib.urlencode()
能夠將字典類型的查詢參數轉碼拼接成 &
鏈接的查詢參數,以後再手動拼接到請求 URL?params
便可獲得帶參數的 URL
.
# -*- coding: utf-8 -*- import urllib import urllib2 def use_params_urllib2(): params = urllib.urlencode({ 'param1': 'hello', 'param2': 'world', 'author':'snowdreams1006', 'website':'http://blog.snowdreams1006.cn', 'url':'https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html', 'wechat':'snowdreams1006', 'email':'snowdreams1006@163.com', 'github':'https://github.com/snowdreams1006/' }) response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get?%s' % params) print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Use params urllib2<<<' use_params_urllib2()
假如上述代碼文件名爲 urllib_demo.py
,在終端命令行內運行 python urllib_demo.py
文件,輸出結果以下所示:
$ python urllib_demo.py >>>Use params urllib2<<< >>>Response Headers: Server: nginx/1.17.6 Date: Thu, 16 Jan 2020 14:27:21 GMT Content-Type: application/json Content-Length: 892 Connection: close Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true >>>Response Body: { "args": { "author": "snowdreams1006", "email": "snowdreams1006@163.com", "github": "https://github.com/snowdreams1006/", "param1": "hello", "param2": "world", "url": "https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html", "website": "http://blog.snowdreams1006.cn", "wechat": "snowdreams1006" }, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Host": "httpbin.snowdreams1006.cn", "User-Agent": "Python-urllib/2.7" }, "origin": "218.205.55.192", "url": "http://httpbin.snowdreams1006.cn/get?website=http%3A%2F%2Fblog.snowdreams1006.cn&github=https%3A%2F%2Fgithub.com%2Fsnowdreams1006%2F&wechat=snowdreams1006¶m2=world¶m1=hello&author=snowdreams1006&url=https%3A%2F%2Fsnowdreams1006.github.io%2Flearn-python%2Furl%2Furllib%2Fteaching.html&email=snowdreams1006%40163.com" }
因而可知,不管是直接手動拼接查詢參數仍是使用 urllib.urlencode(query)
半手動拼接查詢參數,本質上都是同樣的,依然是使用 urllib2.urlopen(url)
發送 GET
請求.
POST
請求若是請求連接 URL
僅僅支持 POST
請求,這時上述拼接地址實現的 GET
請求就再也不知足要求,有意思的是,居然只須要一步就能夠將 GET
請求轉換成 POST
請求.
若是是 GET
請求,發送請求時是這樣: urllib2.urlopen('http://httpbin.snowdreams1006.cn/post?%s' % params)
;
若是是 POST
請求,發送請求時是這樣: urllib2.urlopen('http://httpbin.snowdreams1006.cn/post',params)
;
def post_params_urllib2(): ''' 獲取響應頭和響應體信息 ''' params = urllib.urlencode({ 'param1': 'hello', 'param2': 'world', 'author':'snowdreams1006', 'website':'http://blog.snowdreams1006.cn', 'url':'https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html', 'wechat':'snowdreams1006', 'email':'snowdreams1006@163.com', 'github':'https://github.com/snowdreams1006/' }) response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/post',params) print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Post params urllib2<<<' post_params_urllib2()
因爲 GET
請求和 POST
請求方式實在太像了,所以須要留意發送請求時 urllib2.urlopen(url)
中連接 URL
究竟是怎麼拼接的?
不過更加直觀的方法就是發送請求直接驗證,示例以下:
(.env) $ python urllib_demo.py >>>Post params urllib2<<< >>>Response Headers: Server: nginx/1.17.6 Date: Thu, 16 Jan 2020 14:45:43 GMT Content-Type: application/json Content-Length: 758 Connection: close Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true >>>Response Body: { "args": {}, "data": "", "files": {}, "form": { "author": "snowdreams1006", "email": "snowdreams1006@163.com", "github": "https://github.com/snowdreams1006/", "param1": "hello", "param2": "world", "url": "https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html", "website": "http://blog.snowdreams1006.cn", "wechat": "snowdreams1006" }, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Content-Length": "285", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.snowdreams1006.cn", "User-Agent": "Python-urllib/2.7" }, "json": null, "origin": "218.205.55.192", "url": "http://httpbin.snowdreams1006.cn/post" }
值得注意的是,上述 POST
請求提交的參數存放在 form
屬性而不是 GET
請求時的 args
屬性.
若是 http://proxyip.snowdreams1006.cn/ 沒法訪問,能夠訪問https://github.com/jhao104/proxy_pool項目自行構建代理池.
{ "delete?proxy=127.0.0.1:8080": "delete an unable proxy", "get": "get an useful proxy", "get_all": "get all proxy from proxy pool", "get_status": "proxy number" }
單機勿壓,惡意訪問會關小黑屋喲,推薦你們自行搭建本地環境,謝謝支持.
本代理池是基於 jhao104/proxy_pool項目提供兩種安裝方式,分爲 docker
安裝方式和源碼安裝方式.
docker
方式安裝docker run --env db_type=REDIS --env db_host=127.0.0.1 --env db_port=6379 --env db_password='' -p 5010:5010 jhao104/proxy_pool
固然也能夠提早下載鏡像:
docker pull jhao104/proxy_pool
,而後再運行上述命令啓動容器.
git clone https://github.com/jhao104/proxy_pool.git
也能夠直接下載安裝包: https://github.com/jhao104/proxy_pool/releases
pip install -r requirements.txt
安裝項目依賴時須要切換到項目根目錄,例如cd proxy_pool
,而後會自動從默認安裝源進行下載,也能夠是使用pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
加速安裝.
Config/setting.py
# Config/setting.py 爲項目配置文件 # 配置DB DATABASES = { "default": { "TYPE": "REDIS", # 目前支持SSDB或REDIS數據庫 "HOST": "127.0.0.1", # db host "PORT": 6379, # db port,例如SSDB一般使用8888,REDIS一般默認使用6379 "NAME": "proxy", # 默認配置 "PASSWORD": "" # db password } } # 配置 API服務 SERVER_API = { "HOST": "0.0.0.0", # 監聽ip, 0.0.0.0 監聽全部IP "PORT": 5010 # 監聽端口 } # 上面配置啓動後,代理池訪問地址爲 http://127.0.0.1:5010
# 若是你的依賴已經安裝完成而且具有運行條件,能夠在cli目錄下經過ProxyPool.py啓動. # 程序分爲: schedule 調度程序 和 webserver Api服務 # 首先啓動調度程序 python proxyPool.py schedule # 而後啓動webApi服務 python proxyPool.py webserver
該命令要求當前環境處於cli
目錄,若是是其餘目錄請自行調整proxyPool.py
的路徑(cd cli
便可切換到cli
目錄)
若是以上步驟均正常,項目啓動會後自動抓取互聯網免費代理 ip,能夠經過訪問 http://127.0.0.1:5010 查看.
建議首先利用瀏覽器直接訪問http://proxyip.snowdreams1006.cn/get/查看是否能獲取隨機代理 ip,而後再利用 python
程序獲取,保證代碼運行正確,方便後續開發測試.
{ "proxy": "183.91.33.41:8086", "fail_count": 0, "region": "", "type": "", "source": "freeProxy09", "check_count": 59, "last_status": 1, "last_time": "2020-01-18 13:14:32" }
上述是請求/get/
獲取的隨機 ip 示例,完整請求地址:http://proxyip.snowdreams1006.cn/get/
# -*- coding: utf-8 -*- import urllib2 import json def get_proxy(): ''' 獲取隨機代理 ''' response = urllib2.urlopen('http://proxyip.snowdreams1006.cn/get/') result = response.read() return json.loads(result) if __name__ == '__main__': print '>>>Get proxy urllib<<<' get_proxy_urllib()
若是有瀏覽器環境,能夠直接訪問
http://proxyip.snowdreams1006.cn/get/驗證是否能獲取隨機代理 ip,或者在終端命令行運行
curl http://proxyip.snowdreams1006.cn/get/
命令查看結果.
urllib.FancyURLopener(proxy)
: 設置代理 ip 信息實現間接訪問
經過 urllib.FancyURLopener(proxy)
可設置代理,用於向服務端隱藏客戶端的真實信息,可是服務端到底可否區分代理請求和普通請求是和代理 ip 有關的.
若是是高匿代理的話,是最爲理想的一種狀況,可以達到真正代理的做用.
相反,若是是透明代理的話,是最沒用的代理,服務端不只知道你正在使用代理還知道你真實 ip,有一種掩耳盜鈴的錯覺.
如何驗證設置的代理 ip 是否能被服務端識別,能夠訪問http://httpbin.snowdreams1006.cn/ip獲取服務端讀取到的客戶端 ip.
$ curl http://httpbin.snowdreams1006.cn/ip { "origin": "115.217.104.191" }
若是終端命令行沒
curl
命令,能夠百度一下自行安裝或者直接打開瀏覽器訪問
http://httpbin.snowdreams1006.cn/ip
若是服務器讀取到的請求 ip 和設置的代理 ip 一致,恭喜你,設置代理成功並且是高匿代理,不然的話,那就是掩耳盜鈴罷了.
# -*- coding: utf-8 -*- import urllib import urllib2 import json def get_proxy(): ''' 獲取隨機代理 ''' response = urllib2.urlopen('http://proxyip.snowdreams1006.cn/get/') result = response.read() return json.loads(result) def get_proxy_urllib(): ''' 經過代理髮送請求 ''' # 隨機代理 ip ip = get_proxy().get('proxy') print('>>>Get Proxy:') print(ip) proxy = { 'http': 'http://{}'.format(ip), 'https': 'https://{}'.format(ip) } opener = urllib.FancyURLopener(proxy) response = opener.open() print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Get proxy urllib<<<' get_proxy_urllib()
上述示例只是演示如何設置代理 ip 發送請求,並無驗證代理 ip 是否設置成功,即服務端讀取到請求 ip 和剛剛設置的代理 ip是否相同,而且也沒有考慮代理 ip 不可用或者鏈接超時等異常狀況.
下面提供一個簡單示例判斷代理 ip 是否設置成功:
{ "proxy": "121.225.199.78:3128", "fail_count": 0, "region": "", "type": "", "source": "freeProxy09", "check_count": 15, "last_status": 1, "last_time": "2020-01-17 12:03:29" }
獲取隨機代理 ip 的通常格式,提取出隨機 ip 的通常值爲
121.225.199.78:3128
針對隨機獲取代理ip 的通常格式是帶有端口號,而訪問 http://httpbin.snowdreams1006.cn/ip 獲取到來源 ip 並不包括端口號,所以最簡單的思路是去掉隨機 ip 的端口號,而後再和訪問結果做比較.
'121.225.199.78:3128'.split(':')[0]
首先以:
分割成兩部分,而後只取第一部分,即獲取不帶端口號的 ip 地址:121.225.199.78
接下來因爲 response.read()
獲取到的響應體數據是字符串類型,不方便提取出其中 origin
對應的值,而響應體數據明顯又是 json
格式,所以使用 json.loads(result)
可方便轉換成 python
的字典類型.
result = response.read() result = json.loads(result) proxyip = result.get('origin')
針對字典類型的取值方式不只僅能夠result.get('origin')
也能夠result['origin']
,只不過當鍵名不存在時二者的表現不一致, 建議使用方法取值.
如今最簡單驗證代理 ip 是否設置成功的完整示例以下:
# -*- coding: utf-8 -*- import urllib import urllib2 import json def get_proxy(): ''' 獲取隨機代理 ''' response = urllib2.urlopen('http://proxyip.snowdreams1006.cn/get/') result = response.read() return json.loads(result) def get_proxy_urllib(): ''' 經過代理髮送請求 ''' # 隨機代理 ip ip = get_proxy().get('proxy') print('>>>Get Proxy:') print(ip) proxy = { 'http': 'http://{}'.format(ip), 'https': 'https://{}'.format(ip) } opener = urllib.FancyURLopener(proxy) response = opener.open() result = response.read() result = json.loads(result) response_ip = result.get('origin') proxy_ip = ip.split(':')[0] if proxy_ip == response_ip: print 'Proxy Success' else: print 'Proxy Fail' if __name__ == '__main__': print '>>>Get proxy urllib<<<' get_proxy_urllib()
若是隨機獲取的代理 ip 正常的話,通常不會拋出異常,要麼設置成功,要麼設置失敗.
(.env) $ python urllib_demo.py >>>Get proxy urllib<<< >>>Get Proxy: 52.80.58.248:3128 Proxy Fail (.env) $ python urllib_demo.py >>>Get proxy urllib<<< >>>Get Proxy: 117.88.176.152:3000 Proxy Success
免費代理 ip 質量通常般而已,所以也不要抱有過高的心理預期,實際開發過程當中仍是應該選擇 付費代理 ip.
urllib.FancyURLopener({})
: 清除代理 ip 信息實現直接訪問
設置代理 ip 時須要傳遞給 urllib.FancyURLopener(proxy)
一個代理字典,清除代理信息時只須要將原來的代理字典設置成空對象便可.
主要代碼和設置代理 ip 相差無二,再也不贅述,可參考如下代碼:
# -*- coding: utf-8 -*- import urllib import urllib2 import json def clear_proxy_urllib(): ''' 清除代理後發送請求 ''' # 隨機代理 ip ip = get_proxy().get('proxy') print('>>>Get Proxy:') print(ip) proxy = { 'http': 'http://{}'.format(ip), 'https': 'https://{}'.format(ip) } opener = urllib.FancyURLopener(proxy) response = opener.open("http://httpbin.snowdreams1006.cn/ip") print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') result = response.read() print(result) result = json.loads(result) response_ip = result.get('origin') proxy_ip = ip.split(':')[0] if proxy_ip == response_ip: print 'Set proxy success' else: print 'Set proxy fail' opener = urllib.FancyURLopener({}) response = opener.open("http://httpbin.snowdreams1006.cn/ip") print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') result = response.read() print(result) result = json.loads(result) response_ip = result.get('origin') proxy_ip = ip.split(':')[0] if proxy_ip == response_ip: print 'Clear proxy fail' else: print 'Clear proxy success' if __name__ == '__main__': print '>>>Get proxy urllib<<<' get_proxy_urllib()
除了上述方法使用 urllib.FancyURLopener()
設置或清除代理 ip,其實也可使用 urllib.urlopen()
實現相似需求.
# Use http://www.someproxy.com:3128 for HTTP proxying proxies = {'http': 'http://www.someproxy.com:3128'} filehandle = urllib.urlopen(some_url, proxies=proxies) # Don't use any proxies filehandle = urllib.urlopen(some_url, proxies={}) # Use proxies from environment - both versions are equivalent filehandle = urllib.urlopen(some_url, proxies=None) filehandle = urllib.urlopen(some_url)
其中關於環境變量的設置示例,以下:
% http_proxy="http://www.someproxy.com:3128" % export http_proxy % python ...
本文主要介紹了 python
中原生的 urllib
如何發送網絡請求以及一些基本環境的搭建過程,其中附帶大量可直接操做的現成代碼,文檔和源碼均已開源,感興趣的小夥伴可自行翻閱瀏覽.
如今簡要回顧一下本文主要涉及到的重要知識點,以供後來者學習時快速翻閱查詢.
virtualenv
虛擬環境安裝並激活成功後,python
和 pip
的版本信息以下:
(.env)$ python --version Python 2.7.16 (.env) $ pip --version pip 19.3.1 from ~/python/src/url/urllib/.env/lib/python2.7/site-packages/pip (python 2.7)
如需自行搭建該虛擬環境,可參考如下幾步開啓虛擬環境:
virtualenv
sudo pip install virtualenv
安裝虛擬環境方便隔離不一樣 python 環境,也可使用 系統默認環境,因此這一步是可選的,同理下面的步驟也都是可選的.
.env
virtualenv .env
虛擬環境目錄設置成隱藏目錄的目的是防止誤操做,固然也能夠設置成普通目錄那樣顯示出來.
.env
source .env/bin/activate
激活虛擬環境後能夠運行
pip --version
查看當前版本信息,由此驗證虛擬環境是否開啓成功.
httpbin
默認本地訪問地址: http://127.0.0.1:8000/,線上訪問地址: http://httpbin.snowdreams1006.cn/ 或者 http://httpbin.org/
若是採用 docker
安裝 httpbin
運行成功後,訪問接口地址,實際預覽以下:
若是使用 python
啓動 httpbin
庫,運行成功後效果和 docker
方式有所不一樣,效果以下:
如需自行搭建本地服務,請讀者根據自身須要自行決定安裝方式,下面提供兩種方式開啓 httpbin
服務.
docker
安裝方式docker run -p 8000:80 kennethreitz/httpbin
首次運行會先將鏡像下載到本地再啓動容器,非首次運行會直接啓動容器,訪問地址: http://127.0.0.1:8000/
python
安裝方式pip install gunicorn httpbin && gunicorn httpbin:app
默認監聽端口是8000
,若是遇到端口衝突提示已被佔用,可運行gunicorn httpbin:app -b :9898
指定端口.
proxyip
默認本地訪問地址: http://127.0.0.1:5010/,線上訪問地址: http://proxyip.snowdreams1006.cn/ 或者 http://118.24.52.95/
{ "delete?proxy=127.0.0.1:8080": "delete an unable proxy", "get": "get an useful proxy", "get_all": "get all proxy from proxy pool", "get_status": "proxy number" }
如需自行搭建本地服務,請讀者根據自身須要自行決定安裝方式,下面提供兩種方式開啓 proxyip
服務.
docker
安裝方式docker run --env db_type=REDIS --env db_host=127.0.0.1 --env db_port=6379 --env db_password='' -p 5010:5010 jhao104/proxy_pool
固然也能夠提早下載鏡像:
docker pull jhao104/proxy_pool
,而後再運行上述命令啓動容器.
源碼安裝方式
git clone https://github.com/jhao104/proxy_pool.git
> 固然也能夠直接下載安裝包: [https://github.com/jhao104/proxy_pool/releases](https://github.com/jhao104/proxy_pool/releases) - 步驟 2 : 安裝依賴 ```bash pip install -r requirements.txt ``` > 注意: 安裝項目依賴時須要提早切換到項目根目錄(`cd proxy_pool`),若是嫌棄下載速度慢可使用清華大學鏡像 `pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt` 加速下載安裝過程. - 步驟 3 : 配置 `Config/setting.py` ```python # Config/setting.py 爲項目配置文件 # 配置DB DATABASES = { "default": { "TYPE": "REDIS", # 目前支持SSDB或REDIS數據庫 "HOST": "127.0.0.1", # db host "PORT": 6379, # db port,例如SSDB一般使用8888,REDIS一般默認使用6379 "NAME": "proxy", # 默認配置 "PASSWORD": "" # db password } } # 配置 API服務 SERVER_API = { "HOST": "0.0.0.0", # 監聽ip, 0.0.0.0 監聽全部IP "PORT": 5010 # 監聽端口 } # 上面配置啓動後,代理池訪問地址爲 http://127.0.0.1:5010 ``` > 關於配置更多詳情,請直接參考項目官方介紹,以上配置信息基本夠用了. - 步驟 5 : 啓動項目 ``` bash # 若是你的依賴已經安裝完成而且具有運行條件,能夠在cli目錄下經過ProxyPool.py啓動. # 程序分爲: schedule 調度程序 和 webserver Api服務 # 首先啓動調度程序 python proxyPool.py schedule # 而後啓動webApi服務 python proxyPool.py webserver ``` > 該命令要求當前環境處於 `cli` 目錄,若是是其餘目錄請自行調整 `proxyPool.py` 的路徑(`cd cli`)
urllib
urllib.urlopen(url[,data[,proxies]])
:
https://docs.python.org/2/library/urllib.html
GET
請求若是查詢參數比較簡單的話,能夠直接構建請求URL
,同時能夠配合urllib.urlencode(dict)
序列化查詢參數字典.
當查詢參數不太複雜時,尤爲是不須要查詢參數時,能夠直接 urllib2.urlopen(url)
發送請求,以下:
# -*- coding: utf-8 -*- import urllib import urllib2 def use_simple_urllib2(): ''' 獲取響應頭和響應體信息 ''' response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get') print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Use simple urllib2<<<' use_simple_urllib2()
當查詢參數比較多或者須要動態拼接時,推薦使用 urllib.urlencode(dict)
序列化查詢參數,而後再拼接到請求 URL
後,最終造成完成的請求 URL
.
# -*- coding: utf-8 -*- import urllib import urllib2 def use_params_urllib2(): params = urllib.urlencode({ 'param1': 'hello', 'param2': 'world', 'author':'snowdreams1006', 'website':'http://blog.snowdreams1006.cn', 'url':'https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html', 'wechat':'snowdreams1006', 'email':'snowdreams1006@163.com', 'github':'https://github.com/snowdreams1006/' }) response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/get?%s' % params) print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Use params urllib2<<<' use_params_urllib2()
urllib2.urlopen('http://httpbin.snowdreams1006.cn/get')
POST
請求相比於默認的GET
請求方式,只須要將查詢參數再也不拼接到請求連接URL
而是做爲可選參數傳遞給參數data
,形如urllib.urlopen(url,data)
的請求方式是POST
請求.
def post_params_urllib2(): ''' 獲取響應頭和響應體信息 ''' params = urllib.urlencode({ 'param1': 'hello', 'param2': 'world', 'author':'snowdreams1006', 'website':'http://blog.snowdreams1006.cn', 'url':'https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html', 'wechat':'snowdreams1006', 'email':'snowdreams1006@163.com', 'github':'https://github.com/snowdreams1006/' }) response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/post',params) print('>>>Response Headers:') print(response.info()) print('>>>Response Body:') print(response.read()) if __name__ == '__main__': print '>>>Post params urllib2<<<' post_params_urllib2()
當代理對象有效時
urllib.FancyURLopener(proxy)
可發送代理請求,若代理對象是空字典時則是清除代理設置.
# -*- coding: utf-8 -*- import urllib import urllib2 import json def get_proxy(): ''' 獲取隨機代理 ''' response = urllib2.urlopen('http://proxyip.snowdreams1006.cn/get/') result = response.read() return json.loads(result) def get_proxy_urllib(): ''' 經過代理髮送請求 ''' # 隨機代理 ip ip = get_proxy().get('proxy') print('>>>Get Proxy:') print(ip) proxy = { 'http': 'http://{}'.format(ip), 'https': 'https://{}'.format(ip) } opener = urllib.FancyURLopener(proxy) response = opener.open('http://httpbin.snowdreams1006.cn/ip') result = response.read() result = json.loads(result) response_ip = result.get('origin') proxy_ip = ip.split(':')[0] if proxy_ip == response_ip: print 'Proxy Success' else: print 'Proxy Fail' if __name__ == '__main__': print '>>>Get proxy urllib<<<' get_proxy_urllib()
除了使用 urllib.FancyURLopener(proxy)
設置代理請求外,還可使用 urllib2.urlopen(url,data,proxies)
發送 GET
或 POST
請求的代理請求.
# -*- coding: utf-8 -*- import urllib import urllib2 import json def get_proxy(): ''' 獲取隨機代理 ''' response = urllib2.urlopen('http://proxyip.snowdreams1006.cn/get/') result = response.read() return json.loads(result) def post_proxy_urllib(): ''' 經過代理獲取響應頭和響應體信息 ''' data = urllib.urlencode({ 'param1': 'hello', 'param2': 'world', 'author':'snowdreams1006', 'website':'http://blog.snowdreams1006.cn', 'url':'https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html', 'wechat':'snowdreams1006', 'email':'snowdreams1006@163.com', 'github':'https://github.com/snowdreams1006/' }) ip = get_proxy().get('proxy') print('>>>Get Proxy:') print(ip) proxies = { 'http': 'http://{}'.format(ip), 'https': 'https://{}'.format(ip) } response = urllib2.urlopen('http://httpbin.snowdreams1006.cn/post',data=data,proxies=proxies) result = response.read() result = json.loads(result) response_ip = result.get('origin') proxy_ip = ip.split(':')[0] if proxy_ip == response_ip: print 'Proxy Success' else: print 'Proxy Fail' if __name__ == '__main__': print '>>>Get proxy urllib<<<' post_proxy_urllib()
python2
的 urllib.urlopen(url[,data[,proxies]])
的相關演示基本上所有覆蓋完畢,推薦讀者實際練習一下.
下節預告:
訪問https://api.github.com/請求感興趣的接口,親測公開數據.
{ "current_user_url": "https://api.github.com/user", "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}", "authorizations_url": "https://api.github.com/authorizations", "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}", "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}", "emails_url": "https://api.github.com/user/emails", "emojis_url": "https://api.github.com/emojis", "events_url": "https://api.github.com/events", "feeds_url": "https://api.github.com/feeds", "followers_url": "https://api.github.com/user/followers", "following_url": "https://api.github.com/user/following{/target}", "gists_url": "https://api.github.com/gists{/gist_id}", "hub_url": "https://api.github.com/hub", "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}", "issues_url": "https://api.github.com/issues", "keys_url": "https://api.github.com/user/keys", "label_search_url": "https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}", "notifications_url": "https://api.github.com/notifications", "organization_url": "https://api.github.com/orgs/{org}", "organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}", "organization_teams_url": "https://api.github.com/orgs/{org}/teams", "public_gists_url": "https://api.github.com/gists/public", "rate_limit_url": "https://api.github.com/rate_limit", "repository_url": "https://api.github.com/repos/{owner}/{repo}", "repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}", "current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}", "starred_url": "https://api.github.com/user/starred{/owner}{/repo}", "starred_gists_url": "https://api.github.com/gists/starred", "user_url": "https://api.github.com/users/{user}", "user_organizations_url": "https://api.github.com/user/orgs", "user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}", "user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}" }
若是本文對你有所幫助,不用讚揚,也沒必要轉發,直接點贊留言告訴鼓勵一下就能夠啦!