zoukankan      html  css  js  c++  java
  • python爬虫之urllib库(二)

    python爬虫之urllib库(二)

      urllib库

      超时设置

       网页长时间无法响应的,系统会判断网页超时,无法打开网页。对于爬虫而言,我们作为网页的访问者,不能一直等着服务器给我们返回错误信息,耗费时间太久。因此,我们在爬取网页的时候可以设置超时异常的值。

    import urllib.request


    file=urllib.request.urlopen("http://yum.iqianyue.com",timeout=30) #timeout=30,表示30秒以后产生超时异常 data=file.read()

       HTTP协议请求

      HTTP请求即HTTP请求报文首行(协议方法,请求URL,协议版本)中的协议方法,HTTP请求方法主要有:

      GET请求:通过URL来传递请求信息,获取服务器资源。由于GET请求可以把要传递的请求信息添加在URL上,不安全性表现在可以通过地址栏URL信息看到传递的信息。

      POST请求:向服务器提交数据,如表单提交,通常使用Post请求。

      PUT请求:请求服务器存储一个资源,通常需要指定存储位置。

      DELETE请求:请求服务器删除一个资源。

      HEAD请求:请求获取响应的报头信息,对于响应的主体内容不需要。

      OPTIONS请求:获得请求URL所支持的HTTP方法

      四种HTTP请求方法区别:GET--查,POST--增,PUT--改,DELETE--删

      GET请求实现

      在百度首页输入关键词查询,不断更换关键词可以看到地址栏中URL的变化。分析可以得出:‘https://www.baidu.com/s?wd=’为URL主要部分,在关键词检索字段wd后添加关键词,即可实现百度搜索。

      构造get请求,实现自动爬取百度查询关键词为hello的结果。

    import urllib.request
    
    
    core_url = 'http://www.baidu.com/s?wd='
    keywords = 'hello'
    full_url = core_url + keywords
    req = urllib.request.Request(full_url)
    data = urllib.request.urlopen(req).read()
    
    with open('hello.html', 'wb') as f:
        f.write(data)

      上述关键词如果变成中文,会出现报错:UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-11: ordinal not in range(128),原因:python爬虫之urllib库(一)提到过URL编码,URL只会承认一部分ASCII码中字符,对于汉字等特殊符号是需要编码的。对于一个参数使用字符串结合request模块给URL传参:urllib.request.quote(str);对于多个参数使用字典结合parse模块给URL传参:urllib.parse.urlencode(dict)。

      一个参数

    import urllib.request
    
    
    core_url = 'http://www.baidu.com/s?wd='
    keywords = '您好'
    keywords_encode = urllib.request.quote(keywords)  # URL参数编码
    full_url = core_url + keywords_encode
    req = urllib.request.Request(full_url)
    data = urllib.request.urlopen(req).read()
    
    with open('hello.html', 'wb') as f:
        f.write(data)

      多个参数

    import urllib.request
    import urllib.parse
    
    
    core_url = 'http://www.baidu.com/s?'  # 关键词字段减掉
    keywords = {  # 多个参数
        'wd': '您好',
        'rsv_spt': 1,  
        'rsv_iqid': 0x8c77175600037633,
    }
    keywords_encode = urllib.parse.urlencode(keywords)  # 多个参数url编码
    full_url = core_url + keywords_encode
    req = urllib.request.Request(full_url)
    data = urllib.request.urlopen(req).read()
    
    with open('hello.html', 'wb') as f:
        f.write(data)

      POST请求实现

      POST请求多用于提交表单来实现注册登录。爬虫面对需要注册登录的网页一定是需要登录访问网页以后才可以对网页内容进行爬取的,可以构造POST请求实现自动登录爬取网页。

    import urllib.request
    import urllib.parse
    
    
    url = 'http://data.stats.gov.cn/login.htm'  # url必须是登录或者注册页面的url地址  国家数据统计局官网登录url
    form_data = {
        'username': '545859297@qq.com',  # 表单数据,登录时输入的信息,对应邮箱和密码。不再是url参数了,注意区分
        'keyp': 'bushizhenmima',  # 注意字典中的key需要使用页面中input输入框的name属性的属性值。别试我账号密码!!!
        # 浏览器打开上述网页,确实验证码输入,登录不会成功
    }
    form_data_deal = urllib.parse.urlencode(form_data).encode('utf-8')  # POST请求data属性需要传入bytes类型,而且字典需要通过urlencode连接
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'
    }
    req = urllib.request.Request(url, data=form_data_deal, headers=headers)
    data = urllib.request.urlopen(req).read()
    
    with open('country-data.html', 'wb') as f:
        f.write(data)

      代理服务器设置

      多次使用一个IP地址去爬取网页,服务器可以轻易察觉属于不正常访问行为,可能会对该IP地址设置拒绝访问(封IP),也可能会对用户账号处理(封账号)。使用代理服务器可以轻松换IP地址,即使用代理服务器的IP地址访问页面,而不使用我们真正的IP地址,所谓“偷梁换柱”。

      代理服务器可以分为:免费代理、付费代理。免费代理可以通过网页搜索“免费代理服务器”获得,付费代理可以通过购买获得。代理基本格式:IP:port(代理IP:端口号)

      免费代理添加需要先创建处理器handler,再由build_open()创建opener对象,调用opener中的open()方法爬取页面,不能再使用urlopen()发送请求爬取了。

      使用handle+opener发送请求爬取页面的方法:

    import urllib.request
    
    
    def handler_opener():
        url = 'https://www.baidu.com'
    
        handler = urllib.request.HTTPHandler()  # 常见HTTP处理器
        opener = urllib.request.build_opener(handler)  # 调用buile_open()创建opener对象
    
        response = opener.open(url)  # 调用open()方法发送HTTP请求
        response_str = response.read().decode('utf-8')
    
        return response_str
    
    
    result = handler_opener()
    with open('baidu.html', 'w', encoding='utf-8') as f:
        f.write(result)

      免费代理添加及使用方式:

    import urllib.request
    
    
    def free_proxy():
        url = 'http://www.baidu.com'
    
        proxy = {
            'http': 'http;//116.209.57.195:9999',  # 分为http和https两种协议版本,https是更加安全的http,在http基础上加入安全层SSL
            # 'https': 'https://118.182.33.7:42801'
        }
    
        proxy_handler = urllib.request.ProxyHandler(proxy)  # 创建代理处理器,使用ProxyHandle
        opener = urllib.request.build_opener(proxy_handler)  
    
        response = opener.open(url)
        response_str = response.read()  # 注意与上例不同
    
        return response_str
    
    
    result = free_proxy()
    with open('baidu-free.html', 'wb') as f:  # 注意与上例不同
        f.write(result)

      付费代理添加有两种方式:

      方式一

    import urllib.request
    
    
    def free_proxy():
        url = 'https://www.baidu.com'
    
        proxy = {
            'http': 'http;//222.139.245.130:58424',  # 分为http和https两种协议版本,https是更加安全的http,在http基础上加入安全层SSL
            # 'https': 'https://118.182.33.7:42801'
        }
    
        proxy_handler = urllib.request.ProxyHandler(proxy)  # 创建代理处理器
        opener = urllib.request.build_opener(proxy_handler, urllib.request.HTTPHandler)  # 这个可以缺省HTTPHandler,下面为源码解释
        '''
        The opener will use several default handlers, including support for HTTP, FTP and when applicable HTTPS.
        If any of the handlers passed as arguments are subclasses of the default handlers, the default handlers will not be used.
        '''
    
        response = opener.open(url)
        response_str = response.read()  # 注意与上例不同
    
        return response_str
    
    
    result = free_proxy()
    with open('baidu-free.html', 'wb') as f:  # 注意与上例不同
        f.write(result)

      方式二

    import urllib.request
    
    
    def fee_proxy():
        url = 'http://www.baidu.com'
    
        # 付费代理IP第二种方式
        user_name = 'admin'
        password = '123456'
        proxy_ip = 'http://121.61.1.222:9999'
        proxy_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()  # 创建密码管理器
        proxy_manager.add_password(None, proxy_ip, user_name, password)
    
        proxy_handler = urllib.request.ProxyBasicAuthHandler(proxy_manager)  # 代理IP验证处理器
        proxy_opener = urllib.request.build_opener(proxy_handler)
    
        response = proxy_opener.open(url)
        response_str = response.read().decode('utf-8')
    
        return response_str
    
    
    data = fee_proxy()
    with open('baidu-fee.html', 'w', encoding='utf-8') as f:
        f.write(data)

       代理服务器地址是具有时效性的,尤其是免费代理IP,如果代理服务器地址失效或者填写错误,会返回URLError。通常使用代理服务器进行网页爬取出现URLError,要考虑是否是代理IP失效的原因。

      DebugLog

      调试日志是记录程序运行状态的记录,对于自动化爬虫而言,调试日志是不可或缺的。通过设置Debuglog,可以实现程序边运行边打印调试日志的需求。

      调试日志的配置方法:

    1. 分别使用urllib.request.HTTPHandler()和urllib.request.HTTPSHandler()设置参数debuglevel=1,开启并设置bug级别。
    2. 使用urllib.request.build_opener()创建自定义的opener对象,并设置1中的值作为参数。
    3. 使用urllib.request.install_opener()创建全局的opener对象,使得opener对象不仅可以调用open()方法,也可以使用urlopen()发送HTTP请求。
    4. 调用open()方法或者使用urllib.request.urlopen()发送HTTP请求。
    import urllib.request
    
    
    url = 'http://www.baidu.com'
    
    http_handler = urllib.request.HTTPHandler(debuglevel=1)
    https_handler = urllib.request.HTTPSHandler(debuglevel=1)
    opener = urllib.request.build_opener(http_handler, https_handler)
    urllib.request.install_opener(opener)
    
    response = urllib.request.urlopen(url)  # 请求方式一
    # response = opener.open(url)  # 请求方式二

      URLError

      程序在执行或者搭建过程中,不可避免的会出现错误或者异常,错误通常是指不合语言本身规则且不可控的使用方式,异常是指合乎语言规则且可控的使用方式。网络爬虫中,网页内容和结构的迭代更新以及网络环境等因素都会产生影响,甚至异常。因此,合理处理异常对于爬虫而言是很重要的。

      异常主要为URLError类以及其子类HTTP类,处理方法是使用urllib.error模块和try...except语句,产生URLError异常的原因有:

    1. 连接不上服务器
    2. 远程URL不存在
    3. 无网络
    4. 触发了HTTPError
    import urllib.request
    import urllib.error
    
    
    url = 'http://sad.blog.csdn.net'
    
    try:
        rep = urllib.request.urlopen(url)
    except urllib.error.URLError as e:
        print(e)
    else:
        print(rep)

      当触发HTTPError,可以直接使用HTTPError类,可以查看异常后的状态码以及原因短语。

    import urllib.request
    import urllib.error
    
    
    url = 'http://sad.blog.csdn.net'
    
    try:
        rep = urllib.request.urlopen(url)
    except urllib.error.HTTPError as e:
        print(e.code, e.reason)
    else:
        print(rep)

    常见的状态码以及原因短语有:

    状态码 原因短语(英文) 原因短语(中文)
    200 OK 正常
    301 Moved Permanently 重新定向新的URL,永久性
    302 Found 重新定向新的URL,非永久性
    304 Not Modified 请求资源未更新
    400 Bad Request 非法请求
    401 Unauthorized 请求未经授权
    403 Forbidden 禁止访问
    404 Not Found 没有找到页面
    500 Internal Server Error 服务器内部错误
    501 Not Implemented 服务器不支持实现请求功能

      URLError和HTTPError中的属性及源码error.py:

     1 """Exception classes raised by urllib.
     2 
     3 The base exception class is URLError, which inherits from OSError.  It
     4 doesn't define any behavior of its own, but is the base class for all
     5 exceptions defined in this package.
     6 
     7 HTTPError is an exception class that is also a valid HTTP response
     8 instance.  It behaves this way because HTTP protocol errors are valid
     9 responses, with a status code, headers, and a body.  In some contexts,
    10 an application may want to handle an exception like a regular
    11 response.
    12 """
    13 
    14 import urllib.response
    15 
    16 __all__ = ['URLError', 'HTTPError', 'ContentTooShortError']
    17 
    18 
    19 class URLError(OSError):
    20     # URLError is a sub-type of OSError, but it doesn't share any of
    21     # the implementation.  need to override __init__ and __str__.
    22     # It sets self.args for compatibility with other OSError
    23     # subclasses, but args doesn't have the typical format with errno in
    24     # slot 0 and strerror in slot 1.  This may be better than nothing.
    25     def __init__(self, reason, filename=None):
    26         self.args = reason,
    27         self.reason = reason
    28         if filename is not None:
    29             self.filename = filename
    30 
    31     def __str__(self):
    32         return '<urlopen error %s>' % self.reason
    33 
    34 
    35 class HTTPError(URLError, urllib.response.addinfourl):
    36     """Raised when HTTP error occurs, but also acts like non-error return"""
    37     __super_init = urllib.response.addinfourl.__init__
    38 
    39     def __init__(self, url, code, msg, hdrs, fp):
    40         self.code = code
    41         self.msg = msg
    42         self.hdrs = hdrs
    43         self.fp = fp
    44         self.filename = url
    45         # The addinfourl classes depend on fp being a valid file
    46         # object.  In some cases, the HTTPError may not have a valid
    47         # file object.  If this happens, the simplest workaround is to
    48         # not initialize the base classes.
    49         if fp is not None:
    50             self.__super_init(fp, hdrs, url, code)
    51 
    52     def __str__(self):
    53         return 'HTTP Error %s: %s' % (self.code, self.msg)
    54 
    55     def __repr__(self):
    56         return '<HTTPError %s: %r>' % (self.code, self.msg)
    57 
    58     # since URLError specifies a .reason attribute, HTTPError should also
    59     #  provide this attribute. See issue13211 for discussion.
    60     @property
    61     def reason(self):
    62         return self.msg
    63 
    64     @property
    65     def headers(self):
    66         return self.hdrs
    67 
    68     @headers.setter
    69     def headers(self, headers):
    70         self.hdrs = headers
    View Code

      源码中可以看到,URLError类中有reason属性,HTTPError类具有code属性,HTTPError可以继承父类URLError中的reason属性,而HTTPError是引起URLError的一个原因,即当触发HTTPError引起的URLError异常时,URLError是具有code和reason属性,而HTTPError一直具有code和reason属性。因此,可以使用hasattr()函数在使用前判断是否存在属性,进而通过状态码的存在判定异常URLError的原因与HTTPError是否有关。

    import urllib.request
    import urllib.error
    
    
    try:
        urllib.request.urlopen("http://blog.csdn.net")
    except urllib.error.URLError as e:
        if hasattr(e, "code"):
            print(e.code)
        if hasattr(e, "reason"):
            print(e.reason)
  • 相关阅读:
    LeetCode 3Sum Closest
    LeetCode Jump Game
    LeetCode Convert Sorted List to Binary Search Tree
    LeetCode Search for a Range
    LeetCode ZigZag Conversion
    sql server中主键列的插入问题
    php 联系电话验证(手机和固话)
    Update与Mysql、Sqlsever中的随机数
    Yii在nginx下多目录rewrite
    php后台判断ajax请求
  • 原文地址:https://www.cnblogs.com/snow-lanuage/p/10478217.html
Copyright © 2011-2022 走看看