2012-03-22 17 views
16

Mi pregunta es realmente cómo hacer lo mismo que una pregunta anterior, pero en Scrapy 0.14.Creando una araña scrapy genérica

Using one Scrapy spider for several websites

Básicamente, tengo interfaz gráfica de usuario que tiene parámetros como el dominio, palabras clave, nombres de las etiquetas, etc., y quiero crear una araña genérico para rastrear esos dominios para las palabras clave en las etiquetas. He leído cosas conflictivas, usando versiones anteriores de scrapy, ya sea anulando la clase de administrador de araña o creando dinámicamente una araña. ¿Qué método es el preferido y cómo implemento e invoco la solución adecuada? Gracias por adelantado.

Aquí está el código que quiero hacer genérico. También usa BeautifulSoup. Lo emparejé, así que afortunadamente no eliminé nada crucial para entenderlo.

class MySpider(CrawlSpider): 

name = 'MySpider' 
allowed_domains = ['somedomain.com', 'sub.somedomain.com'] 
start_urls = ['http://www.somedomain.com'] 

rules = (
    Rule(SgmlLinkExtractor(allow=('/pages/',), deny=('',))), 

    Rule(SgmlLinkExtractor(allow=('/2012/03/')), callback='parse_item'), 
) 

def parse_item(self, response): 
    contentTags = [] 

    soup = BeautifulSoup(response.body) 

    contentTags = soup.findAll('p', itemprop="myProp") 

    for contentTag in contentTags: 
     matchedResult = re.search('Keyword1|Keyword2', contentTag.text) 
     if matchedResult: 
      print('URL Found: ' + response.url) 

    pass 
+0

¿Podría mostrar el código que utiliza para valores fijos de dominio, palabras clave, etiquetas? – jfs

+0

Código agregado. Utiliza BeautifulSoup para analizar el html. – user1284717

+0

hey, no seas demasiado flojo mi amigo. –

Respuesta

0

No estoy seguro de qué manera se prefiere, pero yo le dirá lo que he hecho en el pasado. De ninguna manera estoy seguro de que esta sea la mejor (o correcta) forma de hacerlo y me interesaría saber qué piensan los demás.

por lo general sólo sustituir la clase de padre (CrawlSpider) y, o bien pase en argumentos y luego inicializar la clase padre a través de super(MySpider, self).__init__() desde dentro de mi propio init-función o saco en que los datos de una base de datos donde he ahorrado lista de enlaces que se anexarán al start_urls anteriormente.

1

En lugar de tener las variables name, allowed_domains, start_urls y rules unidos a la clase, usted debe escribir una MySpider.__init__, llame CrawlSpider.__init__ de que la aprobación de los argumentos necesarios, y el establecimiento de name, etc. allowed_domains por objeto. MyProp y las palabras clave también se deben establecer dentro de su __init__. Así que al final deberías tener algo como a continuación. Usted no tiene que añadir name a los argumentos, como name se establece por sí mismo de BaseSpiderkwargs:

class MySpider(CrawlSpider): 

    def __init__(self, allowed_domains=[], start_urls=[], 
      rules=[], findtag='', finditemprop='', keywords='', **kwargs): 
     CrawlSpider.__init__(self, **kwargs) 
     self.allowed_domains = allowed_domains 
     self.start_urls = start_urls 
     self.rules = rules 
     self.findtag = findtag 
     self.finditemprop = finditemprop 
     self.keywords = keywords 

    def parse_item(self, response): 
     contentTags = [] 

     soup = BeautifulSoup(response.body) 

     contentTags = soup.findAll(self.findtag, itemprop=self.finditemprop) 

     for contentTag in contentTags: 
      matchedResult = re.search(self.keywords, contentTag.text) 
      if matchedResult: 
       print('URL Found: ' + response.url) 
2

Se puede crear una araña de tiempo de ejecución que es evaluado por el intérprete. Esta pieza de código se pudo evaluar en tiempo de ejecución, así:

a = open("test.py") 
from compiler import compile 
d = compile(a.read(), 'spider.py', 'exec') 
eval(d) 

MySpider 
<class '__main__.MySpider'> 
print MySpider.start_urls 
['http://www.somedomain.com'] 
2

uso el enfoque Scrapy Extensions a extender la clase araña a una clase llamada Masterspider que incluye un analizador genérico.

A continuación se muestra la muy "corta" versión de mi analizador ampliado genérico. Tenga en cuenta que deberá implementar un representador con un motor Javascript (como Selenium o BeautifulSoup) a tan pronto como comience a trabajar en las páginas con AJAX. Y una gran cantidad de código adicional para administrar las diferencias entre los sitios (chatarra basada en el título de la columna, manejar URL relativa a larga, administrar diferentes tipos de contenedores de datos, etc.).

Lo que está interesado en el enfoque de Extensión de Scrapy es que aún puede anular el método de analizador genérico si algo no encaja pero nunca tuve que hacerlo. La clase Masterspider comprueba si se han creado algunos métodos (por ejemplo, parser_start, next_url_parser ...) en la clase de araña específica del sitio para permitir la gestión de especificidades: enviar un formulario, construir la solicitud next_url de elementos en la página, etc.

Como estoy rastreando sitios muy diferentes, siempre hay especificidades para administrar. Es por eso que prefiero mantener una clase para cada sitio raspado para que pueda escribir algunos métodos específicos para manejarlo (procesamiento previo/posterior, excepto PipeLines, generadores de solicitud ...).

masterspider/SiteSpider/settings.py

EXTENSIONS = { 
    'masterspider.masterspider.MasterSpider': 500 
} 

masterspider/masterspdier/masterspider.py

# -*- coding: utf8 -*- 
from scrapy.spider import Spider 
from scrapy.selector import Selector 
from scrapy.http import Request 
from sitespider.items import genspiderItem 

class MasterSpider(Spider): 

    def start_requests(self): 
     if hasattr(self,'parse_start'): # First page requiring a specific parser 
      fcallback = self.parse_start 
     else: 
      fcallback = self.parse 
     return [ Request(self.spd['start_url'], 
        callback=fcallback, 
        meta={'itemfields': {}}) ] 

    def parse(self, response): 
     sel = Selector(response) 
     lines = sel.xpath(self.spd['xlines']) 
     # ... 
     for line in lines: 
      item = genspiderItem(response.meta['itemfields'])    
      # ... 
      # Get request_url of detailed page and scrap basic item info 
      # ... 
      yield Request(request_url, 
        callback=self.parse_item, 
        meta={'item':item, 'itemfields':response.meta['itemfields']}) 

     for next_url in sel.xpath(self.spd['xnext_url']).extract(): 
      if hasattr(self,'next_url_parser'): # Need to process the next page URL before? 
       yield self.next_url_parser(next_url, response) 
      else: 
       yield Request(
        request_url, 
        callback=self.parse, 
        meta=response.meta) 

    def parse_item(self, response): 
     sel = Selector(response) 
     item = response.meta['item'] 
     for itemname, xitemname in self.spd['x_ondetailpage'].iteritems(): 
      item[itemname] = "\n".join(sel.xpath(xitemname).extract()) 
     return item 

masterspider/SiteSpider/arañas/somesite_spider.py

# -*- coding: utf8 -*- 
from scrapy.spider import Spider 
from scrapy.selector import Selector 
from scrapy.http import Request 
from sitespider.items import genspiderItem 
from masterspider.masterspider import MasterSpider 

class targetsiteSpider(MasterSpider): 
    name = "targetsite" 
    allowed_domains = ["www.targetsite.com"] 
    spd = { 
     'start_url' : "http://www.targetsite.com/startpage", # Start page 
     'xlines' : "//td[something...]", 
     'xnext_url' : "//a[contains(@href,'something?page=')]/@href", # Next pages 
     'x_ondetailpage' : { 
      "itemprop123" :  u"id('someid')//text()" 
      } 
    } 

#  def next_url_parser(self, next_url, response): # OPTIONAL next_url regexp pre-processor 
#   ... 
0

Por lo ya que los dominios específicos de rastreo pasaron como argumentos, simplemente anulo Spider.__init__:

class MySpider(scrapy.Spider): 
    """ 
    This spider will try to crawl whatever is passed in `start_urls` which 
    should be a comma-separated string of fully qualified URIs. 

    Example: start_urls=http://localhost,http://example.com 
    """ 
    def __init__(self, name=None, **kwargs): 
     if 'start_urls' in kwargs: 
      self.start_urls = kwargs.pop('start_urls').split(',') 
     super(Spider, self).__init__(name, **kwargs) 
Cuestiones relacionadas