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

      

      

  • 相关阅读:
    单元测试——破除依赖
    单元测试——基础概念
    2015年9月书单推荐
    菜鸟vimer成长记——第4.2章、编程插件
    克服弱点,愈发完美-自我篇——《人性的弱点》读后感
    菜鸟vimer成长记——第4.0章、Vim插件管理利器-Vundle
    菜鸟vimer成长记——第3章、文件
    阿里IPO法律咨询费达1580万美元 为Facebook六倍
    新移民漫画家 以幻想构筑奇妙世界
    请教Amazon FBA里面Label Service, Stickerless, Commingled Inventory是什么意思?
  • 原文地址:https://www.cnblogs.com/snow-lanuage/p/10396895.html
Copyright © 2020-2023  润新知