Implementación de Web Crawler utilizando Abstract Factory Design Pattern en Python

En el patrón de diseño de Abstract Factory , cada producto tiene una interfaz de producto abstracta. Este enfoque facilita la creación de familias de objetos relacionados que es independiente de sus clases de fábrica. Como resultado, puede cambiar la fábrica en tiempo de ejecución para obtener un objeto diferente, lo que simplifica el reemplazo de las familias de productos.   

En este patrón de diseño, el cliente usa una interfaz de fábrica abstracta para acceder a los objetos. La interfaz abstracta separa la creación de objetos del cliente, lo que facilita la manipulación y aísla las clases concretas del cliente. Sin embargo, agregar nuevos productos a la fábrica existente es difícil porque necesita ampliar la interfaz de fábrica, lo que incluye cambiar la clase de interfaz de fábrica abstracta y todas sus subclases.  

Veamos la implementación del rastreador web en Python para una mejor comprensión. Como se muestra en el siguiente diagrama, tiene una clase de interfaz de fábrica abstracta, AbstractFactory , y dos clases de fábrica concretas, HTTPConcreteFactory y FTPConcreteFactory . Estas dos clases concretas se derivan de la clase AbstractFactory y tienen métodos para crear instancias de tres interfaces: ProtocolAbstractProduct , PortAbstractProduct y CrawlerAbstractProduct .

Dado que la clase AbstractFactory actúa como una interfaz para fábricas como HTTPConcreteFactory y FTPConcreteFactory , tiene tres métodos abstractos: create_protocol(), create_port(), create_crawler() . Estos métodos se redefinen en las clases de fábrica. Eso significa que la clase HTTPConcreteFactory crea su familia de objetos relacionados, como HTTPPort, HTTPSecurePort y HTTPSecureProtocol, mientras que la clase FTPConcreteFactory crea FTPPort, FTPProtocol y FTPCrawler.

Python3

import abc
import urllib
import urllib.error
import urllib.request
from bs4 import BeautifulSoup
 
class AbstractFactory(object, metaclass=abc.ABCMeta):
    """ Abstract Factory Interface """
     
    def __init__(self, is_secure):
        self.is_secure = is_secure
 
    @abc.abstractmethod
    def create_protocol(self):
        pass
 
    @abc.abstractmethod
    def create_port(self):
        pass
 
    @abc.abstractmethod
    def create_crawler(self):
        pass
 
class HTTPConcreteFactory(AbstractFactory):
    """ Concrete Factory for building HTTP connection. """
     
    def create_protocol(self):
        if self.is_secure:
            return HTTPSecureProtocol()
        return HTTPProtocol()
 
    def create_port(self):
        if self.is_secure:
            return HTTPSecurePort()
        return HTTPPort()
 
    def create_crawler(self):
        return HTTPCrawler()
 
class FTPConcreteFactory(AbstractFactory):
    """ Concrete Factory for building FTP connection """
     
    def create_protocol(self):
        return FTPProtocol()
 
    def create_port(self):
        return FTPPort()
 
    def create_crawler(self):
        return FTPCrawler()
 
class ProtocolAbstractProduct(object, metaclass=abc.ABCMeta):
    """ An abstract product, represents protocol to connect """
     
    @abc.abstractmethod
    def __str__(self):
        pass
     
class HTTPProtocol(ProtocolAbstractProduct):
    """ An concrete product, represents http protocol """
     
    def __str__(self):
        return 'http'
 
class HTTPSecureProtocol(ProtocolAbstractProduct):
    """ An concrete product, represents https protocol """
     
    def __str__(self):
        return 'https'
 
class FTPProtocol(ProtocolAbstractProduct):
    """ An concrete product, represents ftp protocol """
     
    def __str__(self):
        return 'ftp'
 
class PortAbstractProduct(object, metaclass=abc.ABCMeta):
    """ An abstract product, represents port to connect """
     
    @abc.abstractmethod
    def __str__(self):
        pass
 
class HTTPPort(PortAbstractProduct):
    """ A concrete product which represents http port. """
     
    def __str__(self):
        return '80'
 
class HTTPSecurePort(PortAbstractProduct):
    """ A concrete product which represents https port """
    def __str__(self):
        return '443'
 
class FTPPort(PortAbstractProduct):
    """ A concrete products which represents ftp port. """
     
    def __str__(self):
        return '21'
 
class CrawlerAbstractProduct(object, metaclass=abc.ABCMeta):
    """ An Abstract product, represents parser to parse web content """
     
    @abc.abstractmethod
    def __call__(self, content):
        pass
 
class HTTPCrawler(CrawlerAbstractProduct):
    def __call__(self, content):
        """ Parses web content """
         
        filenames = []
        soup = BeautifulSoup(content, "html.parser")
        links = soup.table.findAll('a')
 
        for link in links:
            filenames.append(link['href'])
             
        return '\n'.join(filenames)
 
class FTPCrawler(CrawlerAbstractProduct):
    def __call__(self, content):
       
        """ Parse Web Content """
        content = str(content, 'utf-8')
        lines = content.split('\n')
        filenames = []
         
        for line in lines:
            splitted_line = line.split(None, 8)
            if len(splitted_line) == 9:
                filenames.append(splitted_line[-1])
 
        return '\n'.join(filenames)
 
class Connector(object):
    """ A client """
     
    def __init__(self, abstractfactory):
        """ calling all attributes
of a connector according to abstractfactory class. """
         
        self.protocol = abstractfactory.create_protocol()
        self.port = abstractfactory.create_port()
        self.crawl = abstractfactory.create_crawler()
 
    def read(self, host, path):
        url = str(self.protocol) + '://' + host + ':' + str(self.port) + path
        print('Connecting to', url)
        return urllib.request.urlopen(url, timeout=10).read()
 
if __name__ == "__main__":
    con_domain = 'ftp.freebsd.org'
    con_path = '/pub/FreeBSD/'
 
    con_protocol = input('Choose the protocol \
                    (0-http, 1-ftp): ')
     
    if con_protocol == '0':
        is_secure = input('Use secure connection? (1-yes, 0-no):')
        if is_secure == '1':
            is_secure = True
        else:
            is_secure = False
        abstractfactory = HTTPConcreteFactory(is_secure)
    else:
        is_secure = False
        abstractfactory = FTPConcreteFactory(is_secure)
 
    connector = Connector(abstractfactory)
 
    try:
        data = connector.read(con_domain, con_path)
    except urllib.error.URLError as e:
        print('Cannot access resource with this method', e)
    else:
        print(connector.crawl(data))

Producción

Producción

El objetivo del programa es rastrear el sitio web utilizando el protocolo HTTP o el protocolo FTP. Aquí, debemos considerar tres escenarios al implementar el código. 

  1. Protocolo
  2. Puerto
  3. Tractor

Estos tres escenarios difieren en los modelos de acceso web HTTP y FTP. Entonces, aquí necesitamos crear dos fábricas, una para crear productos HTTP y otra para crear productos FTP: HTTPConcreteFactory y FTPConcreteFactory . Estas dos fábricas concretas se derivan de una fábrica abstracta: AbstractFactory

Se usa una interfaz abstracta porque los métodos de operación son los mismos para ambas clases de fábrica, solo la implementación es diferente y, por lo tanto, el código del cliente puede determinar qué fábrica usar durante el tiempo de ejecución. Analicemos los productos creados por cada fábrica. 

En el caso del producto de protocolo, la fábrica concreta de HTTP crea el protocolo http o https, mientras que la fábrica concreta de FTP crea el protocolo ftp. Para los productos de puerto, la fábrica concreta de HTTP genera 80 o 443 como producto de puerto, y la fábrica de FTP genera 21 como producto de puerto. Y finalmente, la implementación del rastreador difiere porque la estructura del sitio web es diferente para HTTP y FTP.  

Aquí, el objeto creado tiene la misma interfaz, mientras que los objetos concretos creados son diferentes para cada fábrica. Digamos, por ejemplo, que los productos de puerto como el puerto HTTP, el puerto seguro HTTP y el puerto FTP tienen la misma interfaz, pero los objetos concretos para ambas fábricas son diferentes. Lo mismo es aplicable para el protocolo y el rastreador también. 

Finalmente, la clase de conector acepta una fábrica y usa esta fábrica para inyectar todos los atributos del conector en función de la clase de fábrica.

Publicación traducida automáticamente

Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *