urllib庫是python提供的一種用於操做URL的模塊,python2中是urllib和urllib2兩個庫文件,python3中整合在了urllib一個庫中。即在Python中導入和調用方法也發生了改變。html
python2 | python3 |
import urllib2 | import urllib.request,urllib.request |
import urllib | import urllib.reqest,urllib.error,urllib.parse |
import parse | import urllib.parse |
urllib2.urlopen | urllib.request.urlopen |
urllib.urlencode | urllib.parse.urlencode |
urllib.quote | urllib.request.quote |
cookielib.CookieJar | http.CookieJar |
urllib2.Request | urllib.request.Request |
使用urllib庫須要導入urllib庫中對應的模塊。python
import urllib.request
導入對應模塊之後,使用模塊中的urlopen()方法打開並爬取網頁。例如,爬取百度首頁(http://www.baidu.com),並保存在變量file中。編程
file = urllib.request.urlopen(‘http://www.baidu.com’)
使用print(file)查看一下變量file中保存的內容,查看結果爲:<http.client.HTTPResponse object at 0x0000000002DCBB38>。由此看出,urlopen()方法爬取網頁返回的結果是一個HTTP響應對象,讀取對象中的內容須要其餘方式。api
讀取內容的三種方式及其用法:瀏覽器
data = file.read() # 全部內容賦值給字符串 print(data) data_lines = file.readlines() # 全部內容賦值給列表 print(data_lines) data_line = file.readline() # 單行內容 print(data_line) data_line_next = file.readline() print(data_line_next) # 讀取下一行
成功爬取了一個網頁之後,將網頁保存在本地須要使用文件讀寫操做。文件讀寫的具備兩種寫法:緩存
法一:cookie
fhandle = open('D:/Spider/test/baidu.html', 'wb') fhandle.write(data) fhandle.close()
法二:python爬蟲
with open('D:/Spider/test/baidu.html', 'wb') as fhandle: fhandle.write(data)
兩種寫法都是先使用open()方法按照文件目錄D:/Spider/test/ 找到並打開名爲baidu.html的文件,文件操做模式爲'wb',表示bytes類型數據寫模式,而後使用write()方法寫入。區別在於with方法在數據寫入之後會自動關閉文件,而法一須要調用close()方法關閉文件。ide
注意一個問題:urlopen()返回的HTTP響應對象經過head()讀取之後,能夠看到b‘ ’形式的字符串,此類型數據爲bytes類型,對應文件寫入的中'wb',‘rb’等。咱們知道python中,bytes類型數據是適合用於數據的傳輸和存儲,而若是bytes類型數據須要處理,則須要轉化爲str類型數據。post
str類型與bytes類型之間的數據轉換方式:
import urllib.request file = urllib.request.urlopen('http://www.baidu.com') data = file.read().decode() # decode()轉bytes爲str with open('D:/Spider/test/baidu.html', 'w') as fhandle: # 以str類型寫入文件 fhandle.write(data)
按照文件目錄找到baidu.html文件,使用瀏覽器打開,能夠看到本地版百度首頁。只是圖片暫時沒有爬取過來。
此外,可使用getcode()方法查看爬取網頁時的狀態碼,geturl()方法獲取當時爬取的url地址。
import urllib.request file = urllib.request.urlopen('http://www.baidu.com') code = file.getcode() url = file.geturl() print(code) print(url)
除了上面使用的方法,還可使用urllib.request中urlretrieve()方法直接將對應信息寫入本地文件,格式:urllib.request.urlretrieve(url, filename = '本地文件地址')。
import urllib.request url = 'http://www.baidu.com' filename = urllib.request.urlretrieve(url, filename='D:/Spider/test/baidu-2.html')
按照文件目錄找到baidu-2.html文件,使用瀏覽器打開,能夠看到本地版百度首頁。此外,使用print(filename)查看,得出('D:/Spider/test/baidu-2.html', <http.client.HTTPMessage object at 0x0000000002DEC2E8>),filename是以元組形式存儲了本地文件地址和HTTP響應消息對象。
使用urllib.request.retrieve()方法爬取網頁本地化保存會產生一些緩存,可使用urlcleanup()方法清除緩存。
urllib.request.urlcleanup()
URL編碼:通常來講,URL標準只容許一部分ASCII字符在url中使用,像漢字、「:」、「&」等字符是不符合URL標準的,須要進行URL編碼。根據傳參形式不一樣,URL編碼又分爲兩種方式。
方式一:使用urllib.request.quote()對單一參數進行編碼。
url = 'http://www.baidu.com/s?wd=' search = '編程' # 單一參數 search_url = url + urllib.request.quote(search) # 參數編碼,合併url print(search_url)
方式二:使用urllib.parse.urlencode()對多個參數以字典形式傳入進行編碼。
import urllib.parse url = 'http://www.baidu.com/s?' params = { 'wd': '中文', 'key': '張', 'value': '三' } str_params = urllib.parse.urlencode(params) # 字典傳參拼接方式,urlencode()和quote()類似,urlencode對多個參數轉義 print(str_params) # wd=%E4%B8%AD%E6%96%87&key=%25E5%25BC%25A0&value=%E4%B8%89 search_url = url + str_params print(search_url)
有時,使用爬蟲爬取一些網頁的時候,會返回403錯誤,即禁止訪問的錯誤。是由於網頁爲了防止惡意採集信息的行爲設置了一些反爬措施。對於這部分網頁,咱們能夠嘗試設置一些headers信息,模擬成瀏覽器去訪問這些網頁。因爲urlopen()不支持一些HTTP的高級特性,要添加可使用opener對象或者Request類來進行。
三種添加headers信息的方式,參閱python爬蟲之User-Agent用戶信息。
方法一:使用build_opener()修改報頭
import urllib.request url= "http://blog.csdn.net/weiwei_pig/article/details/51178226" headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0") opener = urllib.request.build_opener() opener.addheaders = [headers] data=opener.open(url).read()
經過build_opener()方法建立opener對象,而opener對象是由OpenerDirector類實例化來的。
OpenerDirector類的實例屬性self.addheaders默認初始值爲[('User-agent', client_version)](列表元素爲元組型嵌套),外部調用賦值修改opener.addheaders屬性。
後調用OpenerDirector類的實例方法open()發送HTTP請求。
方法二:使用Request類實例化靜態添加報頭
import urllib.request url= "http://www.baidu.com" headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0") req=urllib.request.Request(url, headers= headers) data=urllib.request.urlopen(req).read()
方法三:使用Request類的實例方法add_headers()動態添加報頭(注意源碼中add_headers()方法的首字母大寫的key才能夠取value)
import urllib.request url= "http://www.baidu.com" req=urllib.request.Request(url) req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0') data=urllib.request.urlopen(req).read()
方法二和方法三都是對Request類的操做,方法二是經過對類的初始化添加headers,方法三是調用類中的實例方法add_header()添加headers,Request類源碼:
1 class Request: 2 3 def __init__(self, url, data=None, headers={}, 4 origin_req_host=None, unverifiable=False, 5 method=None): 6 self.full_url = url 7 self.headers = {} 8 self.unredirected_hdrs = {} 9 self._data = None 10 self.data = data 11 self._tunnel_host = None 12 for key, value in headers.items(): 13 self.add_header(key, value) 14 if origin_req_host is None: 15 origin_req_host = request_host(self) 16 self.origin_req_host = origin_req_host 17 self.unverifiable = unverifiable 18 if method: 19 self.method = method 20 21 @property 22 def full_url(self): 23 if self.fragment: 24 return '{}#{}'.format(self._full_url, self.fragment) 25 return self._full_url 26 27 @full_url.setter 28 def full_url(self, url): 29 # unwrap('<URL:type://host/path>') --> 'type://host/path' 30 self._full_url = unwrap(url) 31 self._full_url, self.fragment = splittag(self._full_url) 32 self._parse() 33 34 @full_url.deleter 35 def full_url(self): 36 self._full_url = None 37 self.fragment = None 38 self.selector = '' 39 40 @property 41 def data(self): 42 return self._data 43 44 @data.setter 45 def data(self, data): 46 if data != self._data: 47 self._data = data 48 # issue 16464 49 # if we change data we need to remove content-length header 50 # (cause it's most probably calculated for previous value) 51 if self.has_header("Content-length"): 52 self.remove_header("Content-length") 53 54 @data.deleter 55 def data(self): 56 self.data = None 57 58 def _parse(self): 59 self.type, rest = splittype(self._full_url) 60 if self.type is None: 61 raise ValueError("unknown url type: %r" % self.full_url) 62 self.host, self.selector = splithost(rest) 63 if self.host: 64 self.host = unquote(self.host) 65 66 def get_method(self): 67 """Return a string indicating the HTTP request method.""" 68 default_method = "POST" if self.data is not None else "GET" 69 return getattr(self, 'method', default_method) 70 71 def get_full_url(self): 72 return self.full_url 73 74 def set_proxy(self, host, type): 75 if self.type == 'https' and not self._tunnel_host: 76 self._tunnel_host = self.host 77 else: 78 self.type= type 79 self.selector = self.full_url 80 self.host = host 81 82 def has_proxy(self): 83 return self.selector == self.full_url 84 85 def add_header(self, key, val): 86 # useful for something like authentication 87 self.headers[key.capitalize()] = val 88 89 def add_unredirected_header(self, key, val): 90 # will not be added to a redirected request 91 self.unredirected_hdrs[key.capitalize()] = val 92 93 def has_header(self, header_name): 94 return (header_name in self.headers or 95 header_name in self.unredirected_hdrs) 96 97 def get_header(self, header_name, default=None): 98 return self.headers.get( 99 header_name, 100 self.unredirected_hdrs.get(header_name, default)) 101 102 def remove_header(self, header_name): 103 self.headers.pop(header_name, None) 104 self.unredirected_hdrs.pop(header_name, None) 105 106 def header_items(self): 107 hdrs = self.unredirected_hdrs.copy() 108 hdrs.update(self.headers) 109 return list(hdrs.items())
考慮:實例屬性self.headers初始值爲空字典{},可否像方法一,在外部調用時賦值修改實例屬性。
import urllib.request url = "http://www.baidu.com" headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0"} req = urllib.request.Request(url) req.headers = headers data = urllib.request.urlopen(req).read() with open('t.html', 'wb') as fhandle: fhandle.write(data)
測試驗證方法,找到t.html文件使用瀏覽器打開,按F12切換到DevTools-Network選項,刷新頁面,出現
點擊黑色箭頭所指列表項的任意一下,右側出現
下拉找到Request Headers,展開找到User-Agent項,對照上述代碼User-Agent信息驗證可使用外部調用實例屬性修改。
上述過程反過來,則爲手動抓包,獲取User-Agent信息了。