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信息了。