urllib.urlopen(url[,data[,proxies]])
: https://docs.python.org/2/library/urllib.html
python
中默認自帶的網絡請求的庫就是 urlllib
系列了,包括 urllib
urllib2
以及 urllib3
,大多數情況下三者都是互相配合一起合作.
當然也有不少優秀的第三方庫發送網絡請求,最爲大衆熟知的應該包括 requests
庫,不過本文打算從最基礎的 urllib
開始講起如何發送網絡請求,下面請讀者跟着雪之夢技術驛站的思路一起動手實踐吧!
Github
源碼地址: https://github.com/snowdreams1006/learn-python/edit/master/docs/url/urllib/teaching.mdGithub
在線地址: https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html
「雪之夢技術驛站」提醒您: 由於文章具有一定的時效性,很有可能讀者正在閱讀時,部分鏈接已失效,請按照文章相關說明親自實踐驗證,盡信書不如無書,不要直接複製粘貼代碼,一定要自己動手親自敲一遍!
文章目錄
環境搭建
本文使用的 python
環境是基於 virtualenv
實現的虛擬環境,只是爲了方便隔離不同環境,更好模擬真實用戶環境.
實際開發中,可以跟着本文一步一步操作練習搭建開發環境,也可以直接使用系統默認環境.
環境演示
演示環境相關信息如下:
(.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
!
- 步驟1. 安裝虛擬環境
virtualenv
sudo pip install virtualenv
安裝虛擬環境方便隔離不同 python 環境,也可以使用系統默認環境,所以這一步是可選的,同理下面的步驟也都是可選的.
- 步驟2. 準備虛擬環境目錄
.env
virtualenv .env
虛擬環境目錄設置成隱藏目錄的目的是防止誤操作,當然也可以設置成普通目錄那樣顯示出來.
- 步驟3. 激活虛擬環境
.env
source .env/bin/activate
一旦準備好虛擬環境目錄後就需要激活該虛擬環境,這一步可以重複操作,不會報錯!
- 步驟4. 查看當前正在使用的
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
而不是系統默認環境,如果未開啓虛擬環境則顯示的是系統目錄.
網絡請求 urllib 庫
如果讀者親測運行時發現網絡無法正常請求,可以將 http://httpbin.snowdreams1006.cn/ 替換成 http://httpbin.org/ 或者自行搭建本地測試環境.
下面提供兩種搭建本地測試環境的安裝方式,當然也可以訪問 http://httpbin.snowdreams1006.cn/ 或者 http://httpbin.org/ 等在線環境.
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()
可一次性讀取響應體內容.
代碼內容如下:
# -*- 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 protected]&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':'[email protected]',
'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':'[email protected]',
'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": "[email protected]",
"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://blog.snowdreams1006.cn&github=https://github.com/snowdreams1006/&wechat=snowdreams1006¶m2=world¶m1=hello&author=snowdreams1006&url=https://snowdreams1006.github.io/learn-python/url/urllib/teaching.html&[email protected]"
}
由此可見,不論是直接手動拼接查詢參數還是使用 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':'[email protected]',
'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": "[email protected]",
"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
,然後再運行上述命令啓動容器.
源碼方式安裝
- 步驟 1 : 下載源碼
git clone https://github.com/jhao104/proxy_pool.git
也可以直接下載安裝包: https://github.com/jhao104/proxy_pool/releases
- 步驟 2 : 安裝依賴
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
# 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 : 啓動項目
# 如果你的依賴已經安裝完成並且具備運行條件,可以在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/
獲取隨機代理 ip
# -*- 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/
命令查看結果.
設置代理ip訪問
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.
清除代理 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)
如需自行搭建該虛擬環境,可參考以下幾步開啓虛擬環境:
- 步驟1. 安裝虛擬環境
virtualenv
sudo pip install virtualenv
安裝虛擬環境方便隔離不同 python 環境,也可以使用系統默認環境,所以這一步是可選的,同理下面的步驟也都是可選的.
- 步驟2. 準備虛擬環境目錄
.env
virtualenv .env
虛擬環境目錄設置成隱藏目錄的目的是防止誤操作,當然也可以設置成普通目錄那樣顯示出來.
- 步驟3. 激活虛擬環境
.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
指定端口.
免費ip代理池 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
,然後再運行上述命令啓動容器.
-
源碼安裝方式
- 步驟 1 : 下載源碼
git clone https://github.com/jhao104/proxy_pool.git
當然也可以直接下載安裝包: https://github.com/jhao104/proxy_pool/releases
- 步驟 2 : 安裝依賴
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
# 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 : 啓動項目
# 如果你的依賴已經安裝完成並且具備運行條件,可以在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':'[email protected]',
'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':'[email protected]',
'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':'[email protected]',
'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}"
}
參考文檔
如果你覺得本文對你有所幫助,請隨手點個贊再走唄或者關注下公衆號「雪之夢技術驛站」定期更新優質文章喲!