元类理解与元类编程 《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__的一般写法:

  

元类理解与元类编程 《Python3网络爬虫开发》中第九章代理的使用代码Crawler中代码的理解
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()
元类理解与元类编程 《Python3网络爬虫开发》中第九章代理的使用代码Crawler中代码的理解

通过元类创建类

#!/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。实现了动态地设计类。

  

上一篇:AngularJS 指令中的 replace:true


下一篇:使用User Agent和代理IP隐藏身份