python爬蟲之urllib庫(一)

python爬蟲之urllib庫(一)

  urllib庫

  urllib庫是python提供的一種用於操做URL的模塊,python2中是urllib和urllib2兩個庫文件,python3中整合在了urllib一個庫中。即在Python中導入和調用方法也發生了改變。html

python2和python3中urllib庫變化對比
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庫須要導入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

  讀取內容的三種方式及其用法:瀏覽器

  1. file.read()讀取文件的所有內容,read()方法讀取的內容賦值給一個字符串變量。
  2. file.readlines()讀取文件的所有內容,readlines()方法讀取的內容賦值給一個列表變量。
  3. file.readline()讀取文件的一行內容。
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類型數據。測試

  str類型與bytes類型之間的數據轉換方式:

  1. str類型數據轉化爲bytes類型數據:編碼,str.encode('utf-8'),其中utf-8爲統一碼,是一種編碼格式。
  2. bytes類型數據轉化爲str類型數據:解碼,bytes.decode('utf-8')。
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)

  使用headers屬性模擬瀏覽器

   有時,使用爬蟲爬取一些網頁的時候,會返回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())
View Code

  考慮:實例屬性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信息了。

相關文章
相關標籤/搜索