zoukankan      html  css  js  c++  java
  • 元类理解与元类编程 《Python3网络爬虫开发》中第九章代理的使用代码Crawler中代码的理解

    __new__与__init__的理解

      __new__()方法是在创建实例之前被调用的,它的作用是创建一个实例,然后返回该实例对象,它是一个静态方法。

      __init__() 当实例被创建完成之后被调用的,然后设置对象属性的一些初始值,是一个实例方法。

    也即:__new__先被调用,__init__后被调用,__new__方法中的返回值将实例传递给__init__方法中的第一个参数。然后__init__给这个实例设置一些初始参数。

    注意:

    1、继承自object的新式类才有__new__

    2、__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别

    3、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例

    4、__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

    5、如果__new__创建的是当前类的实例,会自动调用__init__函数,通过return语句里面调用的__new__函数的第一个参数是 cls 来保证是当前类实例,如果是其他类的类名;那么实际创建返回的就是其他类的实例,其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数。

    6、在定义子类时没有重新定义__new__()时,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。

    10、将类比作制造商,__new__方法就是前期的原材料购买环节,__init__方法就是在有原材料的基础上,加工,初始化商品环节

    11、__new__的一般写法:

      

    复制代码
    class A(object): # -> don t forget the object specified as base
    
      def __new__(cls):
        print ("A.__new__ called")
        return super(A, cls).__new__(cls)
      #return super().__new__(cls) # 这样子写也可以,注意要与类的继承简写区别一下,后面的cls不可以省略。
    def __init__(self): print("A.__init__ called") A()
    复制代码

    通过元类创建类

    #!/usr/bin/env python 
    # -*- coding:utf-8 -*-
    import json
    import re
    from .utils import get_page
    from pyquery import PyQuery as pq
    
    
    class ProxyMetaclass(type):
        def __new__(cls, name, bases, attrs):
            count = 0
            attrs['__CrawlFunc__'] = []
            for k, v in attrs.items():
                if 'crawl_' in k:
                    attrs['__CrawlFunc__'].append(k)
                    count += 1
            attrs['__CrawlFuncCount__'] = count
            return type.__new__(cls, name, bases, attrs)
    
    
    class Crawler(object, metaclass=ProxyMetaclass):
        def get_proxies(self, callback):
            proxies = []
            for proxy in eval("self.{}()".format(callback)):
                print('成功获取到代理', proxy)
                proxies.append(proxy)
            return proxies
    
        def crawl_daili66(self, page_count=4):
            """
            获取代理66
            :param page_count: 页码
            :return: 代理
            """
            start_url = 'http://www.66ip.cn/{}.html'
            urls = [start_url.format(page) for page in range(1, page_count + 1)]
            for url in urls:
                print('Crawling', url)
                html = get_page(url)
                if html:
                    doc = pq(html)
                    trs = doc('.containerbox table tr:gt(0)').items()
                    for tr in trs:
                        ip = tr.find('td:nth-child(1)').text()
                        port = tr.find('td:nth-child(2)').text()
                        yield ':'.join([ip, port])
    
        def crawl_kuaidaili(self):
            for i in range(1, 4):
                start_url = 'http://www.kuaidaili.com/free/inha/{}/'.format(i)
                html = get_page(start_url)
                if html:
                    ip_address = re.compile('<td data-title="IP">(.*?)</td>')
                    re_ip_address = ip_address.findall(html)
                    port = re.compile('<td data-title="PORT">(.*?)</td>')
                    re_port = port.findall(html)
                    for address, port in zip(re_ip_address, re_port):
                        address_port = address + ':' + port
                        yield address_port.replace(' ', '')
    
        def crawl_xicidaili(self):
            for i in range(1, 3):
                start_url = 'http://www.xicidaili.com/nn/{}'.format(i)
                headers = {
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                    'Cookie': '_free_proxy_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWRjYzc5MmM1MTBiMDMzYTUzNTZjNzA4NjBhNWRjZjliBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMUp6S2tXT3g5a0FCT01ndzlmWWZqRVJNek1WanRuUDBCbTJUN21GMTBKd3M9BjsARg%3D%3D--2a69429cb2115c6a0cc9a86e0ebe2800c0d471b3',
                    'Host': 'www.xicidaili.com',
                    'Referer': 'http://www.xicidaili.com/nn/3',
                    'Upgrade-Insecure-Requests': '1',
                }
                html = get_page(start_url, options=headers)
                if html:
                    find_trs = re.compile('<tr class.*?>(.*?)</tr>', re.S)
                    trs = find_trs.findall(html)
                    for tr in trs:
                        find_ip = re.compile('<td>(d+.d+.d+.d+)</td>')
                        re_ip_address = find_ip.findall(tr)
                        find_port = re.compile('<td>(d+)</td>')
                        re_port = find_port.findall(tr)
                        for address, port in zip(re_ip_address, re_port):
                            address_port = address + ':' + port
                            yield address_port.replace(' ', '')

    根据网络资料以及个人理解,此段代码用到元类的理解如下:

      这里借助了元类来实现所有ProxyMetaclass类中以crawl__开头的方法,具体实现过程如下:

    1.首先定义ProxyMetaclass类作为Crawler类的元类,元类中实现__new__()方法,这个方法有固定的几个参数,其中参数含义如下:

      name:当前类的名字。

      bases:当前类继承的父类,以元组形式提交。

      attrs:当前类中所有的属性(包括方法也可以看作特殊的属性)

    2.遍历attrs参数即可获取类的所有方法信息,然后判断方法是否以crawl__开头,如果是则将其添加到__CrawlFunc__属性中。这样就将所有的以crawl__开头的方法定义成了一个属性,动态获取到所有以crawl__开头的方法。

     过程理解:

      当Crawler中启用元类(即实现metaclass=ProxyMetaclass)时,意味着Crawler中的__new__()方法继承自ProxyMetaclass的__new__()方法,该方法的第一个参数cls就代表创建的Crawler类的实例,name为其名字,bases为其父类方法,然后attrs为其所有类方法(其中的key即方法名,value为方法返回值),最后metaclass又通过returnreturn type.__new__(cls, name, bases, attrs)方法,将构建实例的工作又交给了type去完成。只是在传递参数attrs中做了一些改变,于是前面过程2中的方法实现之后使得创建的Crawler类的实例具有了属性__CrawlFunc__ = [crawl_daili66, crawl_kuaidaili, , crawl_xicidaili,  crawl_ip3366, crawl_iphai, crawl_data5u] 和属性__CrawlFuncCount__ = 6。实现了动态地设计类。

      

  • 相关阅读:
    jquery 异常
    easyui datagrid 单击行不选择行
    java 简单实现上传 | commons-fileupload
    abator ibatis 构建过程
    mysql 杂笔
    eclipse杂笔
    maven 构建项目
    ubuntu随笔
    powershell:convert-path
    maven学习笔记1
  • 原文地址:https://www.cnblogs.com/yc3110/p/10809607.html
Copyright © 2011-2022 走看看