2017-08-30 3 views
0

Je raccroche un site d'actualité avec Scrapy et enregistre des éléments récupérés dans une base de données avec sqlalchemy. Le travail d'analyse s'exécute périodiquement et je souhaite ignorer les URL qui n'ont pas changé depuis la dernière exploration.Scrapy subclassing LinkExtractor soulève TypeError: MyLinkExtractor() a un argument de mot clé inattendu 'allow'

J'essaie de sous-classer LinkExtractor et retourner une liste vide dans le cas où le fichier response.url a été analysé plus récemment que mis à jour.

Mais quand je lance 'scrapy crawl spider_name' Je reçois:

TypeError: MyLinkExtractor() got an unexpected keyword argument 'allow'

Le code:

def MyLinkExtractor(LinkExtractor): 
    '''This class should redefine the method extract_links to 
    filter out all links from pages which were not modified since 
    the last crawling''' 
    def __init__(self, *args, **kwargs): 
     """ 
     Initializes database connection and sessionmaker. 
     """ 
     engine = db_connect() 
     self.Session = sessionmaker(bind=engine) 
     super(MyLinkExtractor, self).__init__(*args, **kwargs) 

    def extract_links(self, response): 
     all_links = super(MyLinkExtractor, self).extract_links(response) 

     # Return empty list if current url was recently crawled 
     session = self.Session() 
     url_in_db = session.query(Page).filter(Page.url==response.url).all() 
     if url_in_db and url_in_db[0].last_crawled.replace(tzinfo=pytz.UTC) > item['header_last_modified']: 
      return [] 

     return all_links 

...

class MySpider(CrawlSpider): 

    def __init__(self, *args, **kwargs): 
     """ 
     Initializes database connection and sessionmaker. 
     """ 
     engine = db_connect() 
     self.Session = sessionmaker(bind=engine) 
     super(MySpider, self).__init__(*args, **kwargs) 

    ... 

    # Define list of regex of links that should be followed 
    links_regex_to_follow = [ 
     r'some_url_pattern', 
     ] 

    rules = (Rule(MyLinkExtractor(allow=links_regex_to_follow), 
        callback='handle_news', 
        follow=True),  
      ) 

    def handle_news(self, response): 

     item = MyItem() 
     item['url'] = response.url 
     session = self.Session() 

     # ... Process the item and extract meaningful info 

     # Register when the item was crawled 
     item['last_crawled'] = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC) 

     # Register when the page was last-modified 
     date_string = response.headers.get('Last-Modified', None).decode('utf-8') 
     item['header_last_modified'] = get_datetime_from_http_str(date_string) 

     yield item 

La chose la plus étrange est que , si je remplace MyLinkExtractor pour LinkE xtractor dans la règle définition, il s'exécute.

Mais si je laisse MyLinkExtractor dans la définition Règle et redéfinissent MyLinkExtractor à:

def MyLinkExtractor(LinkExtractor): 
    '''This class should redefine the method extract_links to 
    filter out all links from pages which were not modified since 
    the last crawling''' 
    pass 

je reçois la même erreur.

Répondre

2

Votre MyLinkExtractor n'est pas un class, mais la fonction puisque vous avez déclaré avec def au lieu de class. C'est difficile à repérer, car Python permet de déclarer des fonctions à l'intérieur d'autres fonctions et aucun des noms n'est vraiment réservé. Quoi qu'il en soit, je crois que stack-trace serait un peu différent au cas où la classe ne serait pas correctement instanciée - vous verriez le nom de la dernière fonction qui a été erronée (__init__ de MyLinkExtractor).

+0

...: | Je savais que c'était quelque chose de simple que je manquais simplement ... mais rien de si stupide. Eh bien, j'espère que cette question peut aider n'importe qui sous-classant LinkExtractor. Merci @erhesto – pedrovgp