標(biāo)準(zhǔn)庫(6)

2018-02-24 15:48 更新

urllib

urllib模塊用于讀取來自網(wǎng)上(服務(wù)器上)的數(shù)據(jù),比如不少人用python做爬蟲程序,就可以使用這個(gè)模塊。先看一個(gè)簡(jiǎn)單例子:

>>> import urllib
>>> itdiffer =  urllib.urlopen("http://www.itdiffer.com")

這樣就已經(jīng)把我的網(wǎng)站www.itdiffer.com首頁的內(nèi)容拿過來了,得到了一個(gè)類似文件的對(duì)象。接下來的操作跟操作一個(gè)文件一樣(如果忘記了文件怎么操作,可以參考:《文件(1)

>>> print itdiffer.read()
<!DOCTYPE HTML>
<html>
    <head>
        <title>I am Qiwsir</title>
....//因?yàn)閮?nèi)容太多,下面就省略了

就這么簡(jiǎn)單,完成了對(duì)一個(gè)網(wǎng)頁的抓取。當(dāng)然,如果你真的要做爬蟲程序,還不是僅僅如此。這里不介紹爬蟲程序如何編寫,僅說明urllib模塊的常用屬性和方法。

>>> dir(urllib)
['ContentTooShortError', 'FancyURLopener', 'MAXFTPCACHE', 'URLopener', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__version__', '_asciire', '_ftperrors', '_have_ssl', '_hexdig', '_hextochr', '_hostprog', '_is_unicode', '_localhost', '_noheaders', '_nportprog', '_passwdprog', '_portprog', '_queryprog', '_safe_map', '_safe_quoters', '_tagprog', '_thishost', '_typeprog', '_urlopener', '_userprog', '_valueprog', 'addbase', 'addclosehook', 'addinfo', 'addinfourl', 'always_safe', 'base64', 'basejoin', 'c', 'ftpcache', 'ftperrors', 'ftpwrapper', 'getproxies', 'getproxies_environment', 'i', 'localhost', 'noheaders', 'os', 'pathname2url', 'proxy_bypass', 'proxy_bypass_environment', 'quote', 'quote_plus', 're', 'reporthook', 'socket', 'splitattr', 'splithost', 'splitnport', 'splitpasswd', 'splitport', 'splitquery', 'splittag', 'splittype', 'splituser', 'splitvalue', 'ssl', 'string', 'sys', 'test1', 'thishost', 'time', 'toBytes', 'unquote', 'unquote_plus', 'unwrap', 'url2pathname', 'urlcleanup', 'urlencode', 'urlopen', 'urlretrieve']

選幾個(gè)常用的介紹,其它的如果讀者用到,可以通過查看文檔了解。

urlopen()

urlopen()主要用于打開url文件,然后就獲得指定url的數(shù)據(jù),接下來就如同在本地操作文件那樣來操作。

Help on function urlopen in module urllib:

urlopen(url, data=None, proxies=None) Create a file-like object for the specified URL to read from.

得到的對(duì)象被叫做類文件。從名字中也可以理解后面的操作了。先對(duì)參數(shù)說明一下:

  • url:遠(yuǎn)程數(shù)據(jù)的路徑,常常是網(wǎng)址
  • data:如果使用post方式,這里就是所提交的數(shù)據(jù)
  • proxies:設(shè)置代理

關(guān)于參數(shù)的詳細(xì)說明,還可以參考python的官方文檔,這里僅演示最常用的,如前面的例子那樣。

當(dāng)?shù)玫搅祟愇募?duì)象之后,就可以對(duì)它進(jìn)行操作。變量itdiffer引用了得到的類文件對(duì)象,通過它查看:

>>> dir(itdiffer)
['__doc__', '__init__', '__iter__', '__module__', '__repr__', 'close', 'code', 'fileno', 'fp', 'getcode', 'geturl', 'headers', 'info', 'next', 'read', 'readline', 'readlines', 'url']

讀者從這個(gè)結(jié)果中也可以看出,這個(gè)類文件對(duì)象也是可迭代的。常用的方法:

  • read(),readline(),readlines(),fileno(),close():都與文件操作一樣,這里不再贅述??梢詤⒖记懊嬗嘘P(guān)文件章節(jié)
  • info():返回頭信息
  • getcode():返回http狀態(tài)碼
  • geturl():返回url

簡(jiǎn)單舉例:

>>> itdiffer.info()
<httplib.HTTPMessage instance at 0xb6eb3f6c>
>>> itdiffer.getcode()
200
>>> itdiffer.geturl()
'http://www.itdiffer.com'

更多情況下,已經(jīng)建立了類文件對(duì)象,通過對(duì)文件操作方法,獲得想要的數(shù)據(jù)。

對(duì)url編碼、解碼

url對(duì)其中的字符有嚴(yán)格要求,不許可某些特殊字符,這就要對(duì)url進(jìn)行編碼和解碼了。這個(gè)在進(jìn)行web開發(fā)的時(shí)候特別要注意。urllib模塊提供這種功能。

  • quote(string[, safe]):對(duì)字符串進(jìn)行編碼。參數(shù)safe指定了不需要編碼的字符
  • urllib.unquote(string) :對(duì)字符串進(jìn)行解碼
  • quote_plus(string [ , safe ] ) :與urllib.quote類似,但這個(gè)方法用'+'來替換空格' ',而quote用'%20'來代替空格
  • unquote_plus(string ) :對(duì)字符串進(jìn)行解碼;
  • urllib.urlencode(query[, doseq]):將dict或者包含兩個(gè)元素的元組列表轉(zhuǎn)換成url參數(shù)。例如{'name': 'laoqi', 'age': 40}將被轉(zhuǎn)換為"name=laoqi&age=40"
  • pathname2url(path):將本地路徑轉(zhuǎn)換成url路徑
  • url2pathname(path):將url路徑轉(zhuǎn)換成本地路徑

看例子就更明白了:

>>> du = "http://www.itdiffer.com/name=python book"
>>> urllib.quote(du)
'http%3A//www.itdiffer.com/name%3Dpython%20book'
>>> urllib.quote_plus(du)
'http%3A%2F%2Fwww.itdiffer.com%2Fname%3Dpython+book'

注意看空格的變化,一個(gè)被編碼成%20,另外一個(gè)是+

再看解碼的,假如在google中搜索零基礎(chǔ) python,結(jié)果如下圖:

我的教程可是在這次搜索中排列第一個(gè)哦。

這不是重點(diǎn),重點(diǎn)是看url,它就是用+替代空格了。

>>> dup = urllib.quote_plus(du)
>>> urllib.unquote_plus(dup)
'http://www.itdiffer.com/name=python book'

從解碼效果來看,比較完美地逆過程。

>>> urllib.urlencode({"name":"qiwsir","web":"itdiffer.com"})
'web=itdiffer.com&name=qiwsir'

這個(gè)在編程中,也會(huì)用到,特別是開發(fā)網(wǎng)站時(shí)候。

urlretrieve()

雖然urlopen()能夠建立類文件對(duì)象,但是,那還不等于將遠(yuǎn)程文件保存在本地存儲(chǔ)器中,urlretrieve()就是滿足這個(gè)需要的。先看實(shí)例:

>>> import urllib
>>> urllib.urlretrieve("http://www.itdiffer.com/images/me.jpg","me.jpg")
('me.jpg', <httplib.HTTPMessage instance at 0xb6ecb6cc>)
>>> 

me.jpg是一張存在于服務(wù)器上的圖片,地址是:http://www.itdiffer.com/images/me.jpg,把它保存到本地存儲(chǔ)器中,并且仍舊命名為me.jpg。注意,如果只寫這個(gè)名字,表示存在啟動(dòng)python交互模式的那個(gè)目錄中,否則,可以指定存儲(chǔ)具體目錄和文件名。

urllib官方文檔中有一大段相關(guān)說明,讀者可以去認(rèn)真閱讀。這里僅簡(jiǎn)要介紹一下相關(guān)參數(shù)。

urllib.urlretrieve(url[, filename[, reporthook[, data]]])

  • url:文件所在的網(wǎng)址
  • filename:可選。將文件保存到本地的文件名,如果不指定,urllib會(huì)生成一個(gè)臨時(shí)文件來保存
  • reporthook:可選。是回調(diào)函數(shù),當(dāng)鏈接服務(wù)器和相應(yīng)數(shù)據(jù)傳輸完畢時(shí)觸發(fā)本函數(shù)
  • data:可選。如果用post方式所發(fā)出的數(shù)據(jù)

函數(shù)執(zhí)行完畢,返回的結(jié)果是一個(gè)元組(filename, headers),filename是保存到本地的文件名,headers是服務(wù)器響應(yīng)頭信息。

#!/usr/bin/env python
# coding=utf-8

import urllib

def go(a,b,c):
    per = 100.0 * a * b / c
    if per > 100:
        per = 100
    print "%.2f%%" % per

url = "http://youxi.66wz.com/uploads/1046/1321/11410192.90d133701b06f0cc2826c3e5ac34c620.jpg"
local = "/home/qw/Pictures/g.jpg"
urllib.urlretrieve(url, local, go)

這段程序就是要下載指定的圖片,并且保存為本地指定位置的文件,同時(shí)要顯示下載的進(jìn)度。上述文件保存之后,執(zhí)行,顯示如下效果:

$ python 22501.py 
0.00%
8.13%
16.26%
24.40%
32.53%
40.66%
48.79%
56.93%
65.06%
73.19%
81.32%
89.46%
97.59%
100.00%

到相應(yīng)目錄中查看,能看到與網(wǎng)上地址一樣的文件。我這里就不對(duì)結(jié)果截圖了,唯恐少部分讀者鼻子流血。

urllib2

urllib2是另外一個(gè)模塊,它跟urllib有相似的地方——都是對(duì)url相關(guān)的操作,也有不同的地方。關(guān)于這方面,有一篇文章講的不錯(cuò):Python: difference between urllib and urllib2

我選取一段,供大家參考:

urllib2 can accept a Request object to set the headers for a URL request, urllib accepts only a URL. That means, you cannot masquerade your User Agent string etc.

urllib provides the urlencode method which is used for the generation of GET query strings, urllib2 doesn't have such a function. This is one of the reasons why urllib is often used along with urllib2.

所以,有時(shí)候兩個(gè)要同時(shí)使用,urllib模塊和urllib2模塊有的方法可以相互替代,有的不能。看下面的屬性方法列表就知道了。

>>> dir(urllib2)
['AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'AbstractHTTPHandler', 'BaseHandler', 'CacheFTPHandler', 'FTPHandler', 'FileHandler', 'HTTPBasicAuthHandler', 'HTTPCookieProcessor', 'HTTPDefaultErrorHandler', 'HTTPDigestAuthHandler', 'HTTPError', 'HTTPErrorProcessor', 'HTTPHandler', 'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm', 'HTTPRedirectHandler', 'HTTPSHandler', 'OpenerDirector', 'ProxyBasicAuthHandler', 'ProxyDigestAuthHandler', 'ProxyHandler', 'Request', 'StringIO', 'URLError', 'UnknownHandler', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__version__', '_cut_port_re', '_opener', '_parse_proxy', '_safe_gethostbyname', 'addinfourl', 'base64', 'bisect', 'build_opener', 'ftpwrapper', 'getproxies', 'hashlib', 'httplib', 'install_opener', 'localhost', 'mimetools', 'os', 'parse_http_list', 'parse_keqv_list', 'posixpath', 'proxy_bypass', 'quote', 'random', 'randombytes', 're', 'request_host', 'socket', 'splitattr', 'splithost', 'splitpasswd', 'splitport', 'splittag', 'splittype', 'splituser', 'splitvalue', 'sys', 'time', 'toBytes', 'unquote', 'unwrap', 'url2pathname', 'urlopen', 'urlparse', 'warnings']

比較常用的比如urlopen()跟urllib.open()是完全類似的。

Request類

正如前面區(qū)別urllib和urllib2所講,利用urllib2模塊可以建立一個(gè)Request對(duì)象。方法就是:

>>> req = urllib2.Request("http://www.itdiffer.com")

建立了Request對(duì)象之后,它的最直接應(yīng)用就是可以作為urlopen()方法的參數(shù)

>>> response = urllib2.urlopen(req)
>>> page = response.read()
>>> print page

因?yàn)榕c前面的urllib.open("http://www.itdiffer.com")結(jié)果一樣,就不浪費(fèi)篇幅了。

但是,如果Request對(duì)象僅僅局限于此,似乎還沒有什么太大的優(yōu)勢(shì)。因?yàn)閯偛诺脑L問僅僅是滿足以get方式請(qǐng)求頁面,并建立類文件對(duì)象。如果是通過post向某地址提交數(shù)據(jù),也可以建立Request對(duì)象。

import urllib    
import urllib2    

url = 'http://www.itdiffer.com/register.py'    

values = {'name' : 'qiwsir',    
          'location' : 'China',    
          'language' : 'Python' }    

data = urllib.urlencode(values)     # 編碼  
req = urllib2.Request(url, data)    # 發(fā)送請(qǐng)求同時(shí)傳data表單  
response = urllib2.urlopen(req)     #接受反饋的信息  
the_page = response.read()          #讀取反饋的內(nèi)容

注意,讀者不能照抄上面的程序,然后運(yùn)行代碼。因?yàn)槟莻€(gè)url中沒有相應(yīng)的接受客戶端post上去的data的程序文件。上面的代碼只是以一個(gè)例子來顯示Request對(duì)象的另外一個(gè)用途,還有就是在這個(gè)例子中是以post方式提交數(shù)據(jù)。

在網(wǎng)站中,有的會(huì)通過User-Agent來判斷訪問者是瀏覽器還是別的程序,如果通過別的程序訪問,它有可能拒絕。這時(shí)候,我們編寫程序去訪問,就要設(shè)置headers了。設(shè)置方法是:

user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }

然后重新建立Request對(duì)象:

req = urllib2.Request(url, data, headers)    

再用urlopen()方法訪問:

response = urllib2.urlopen(req) 

除了上面演示之外,urllib2模塊的東西還很多,比如還可以:

  • 設(shè)置HTTP Proxy
  • 設(shè)置Timeout值
  • 自動(dòng)redirect
  • 處理cookie

等等。這些內(nèi)容不再一一介紹,當(dāng)需要用到的時(shí)候可以查看文檔或者google。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)