python爬蟲之urllib庫(一)


python爬蟲之urllib庫(一)

  urllib庫

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

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庫中對應的模塊。

  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響應對象,讀取對象中的內容需要其他方式。

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

  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)  # 讀取下一行

  成功爬取了一個網頁以后,將網頁保存在本地需要使用文件讀寫操作。文件讀寫的具有兩種寫法:

  法一:

fhandle = open('D:/Spider/test/baidu.html', 'wb')
fhandle.write(data)
fhandle.close()

  法二:

  with open('D:/Spider/test/baidu.html', 'wb') as fhandle:
      fhandle.write(data)

  兩種寫法都是先使用open()方法按照文件目錄D:/Spider/test/ 找到並打開名為baidu.html的文件,文件操作模式為'wb',表示bytes類型數據寫模式,然后使用write()方法寫入。區別在於with方法在數據寫入以后會自動關閉文件,而法一需要調用close()方法關閉文件。

  注意一個問題: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信息了。

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM