2012-05-15 16 views
6

Estoy usando Scrapy, en particular la clase CrawlSpider de Scrapy para raspar enlaces web que contienen ciertas palabras clave. Tengo una larga lista start_urls que obtiene sus entradas de una base de datos SQLite que está conectada a un proyecto de Django. Quiero guardar los enlaces web raspados en esta base de datos.¿Cómo acceder a una start_url específica en un Scrapy CrawlSpider?

Tengo dos modelos de Django, uno para las URL de inicio como http://example.com y uno de los enlaces a las webs raspadas como http://example.com/website1, http://example.com/website2 etc. Todos los enlaces a las webs son raspadas subsitios de una de las URL de inicio en la lista start_urls.

El modelo de enlaces web tiene una relación muchos a uno con el modelo de inicio de URL, es decir, el modelo de enlaces web tiene una clave externa para el modelo de inicio de URL. Para guardar correctamente mis enlaces web raspados a la base de datos, necesito decir el CrawlSpider del método parse_item() que inicia el URL al que pertenece el enlace web raspado. ¿Cómo puedo hacer eso? La clase DjangoItem de Scrapy no ayuda a este respecto, ya que todavía tengo que definir explícitamente la URL de inicio utilizada.

En otras palabras, ¿cómo puedo pasar la url de inicio actualmente utilizada al método parse_item(), de modo que pueda guardarla junto con los enlaces web raspados apropiados a la base de datos? ¿Algunas ideas? ¡Gracias por adelantado!

+0

¿Puede tener el campo start_url en la misma tabla que la tabla de enlaces web (como en el DjangoItem que está utilizando)?Claro, creará desnormalización redundante, pero si desea evitar llamadas explícitas, esto podría ayudar. – zubinmehta

Respuesta

8

De manera predeterminada, no puede acceder a la URL de inicio original.

Pero puede anular el método make_requests_from_url y poner la url de inicio en meta. Luego, en un análisis, puede extraerlo de allí (si cede en ese método de análisis las solicitudes subsiguientes, no olvide reenviar esa url de inicio en ellas).


que no han trabajado con CrawlSpider y tal vez lo que sugiere Maxim va a trabajar para usted, pero tenga en cuenta que response.url tiene la URL después de posibles cambios de dirección.

Aquí es un ejemplo de la forma en que lo haría, pero es sólo un ejemplo (tomado de la clase particular scrapy) y no se puso a prueba:

class MySpider(CrawlSpider): 
    name = 'example.com' 
    allowed_domains = ['example.com'] 
    start_urls = ['http://www.example.com'] 

    rules = (
     # Extract links matching 'category.php' (but not matching 'subsection.php') 
     # and follow links from them (since no callback means follow=True by default). 
     Rule(SgmlLinkExtractor(allow=('category\.php',), deny=('subsection\.php',))), 

     # Extract links matching 'item.php' and parse them with the spider's method parse_item 
     Rule(SgmlLinkExtractor(allow=('item\.php',)), callback='parse_item'), 
    ) 

    def parse(self, response): # When writing crawl spider rules, avoid using parse as callback, since the CrawlSpider uses the parse method itself to implement its logic. So if you override the parse method, the crawl spider will no longer work. 
     for request_or_item in CrawlSpider.parse(self, response): 
      if isinstance(request_or_item, Request): 
       request_or_item = request_or_item.replace(meta = {'start_url': response.meta['start_url']}) 
      yield request_or_item 

    def make_requests_from_url(self, url): 
     """A method that receives a URL and returns a Request object (or a list of Request objects) to scrape. 
     This method is used to construct the initial requests in the start_requests() method, 
     and is typically used to convert urls to requests. 
     """ 
     return Request(url, dont_filter=True, meta = {'start_url': url}) 

    def parse_item(self, response): 
     self.log('Hi, this is an item page! %s' % response.url) 

     hxs = HtmlXPathSelector(response) 
     item = Item() 
     item['id'] = hxs.select('//td[@id="item_id"]/text()').re(r'ID: (\d+)') 
     item['name'] = hxs.select('//td[@id="item_name"]/text()').extract() 
     item['description'] = hxs.select('//td[@id="item_description"]/text()').extract() 
     item['start_url'] = response.meta['start_url'] 
     return item 

preguntará si usted tiene alguna pregunta. Por cierto, utilizando la función 'Ir a la definición' de PyDev puede ver las fuentes de scrapy y comprender qué parámetros Request, make_requests_from_url y otras clases y métodos esperan. Entrar en el código ayuda y le ahorra tiempo, aunque puede parecer difícil al principio.

+0

Creo que entiendo más o menos lo que quiere decir, pero realmente no sé cómo implementarlo. ¿Puede proporcionar un ejemplo breve que demuestre cómo se usa el método 'make_requests_from_url' y cómo se relaciona con el método' parse'? Sigo siendo un principiante. ¡Gracias! – pemistahl

+1

Gracias Warwaruk, esto funciona muy bien. Sin embargo, descubrí que solo el método 'make_requests_from_url' y la configuración de la etiqueta' meta' de la solicitud es necesaria para que funcione. No quiero guardar la url de inicio al final, así que no tiene sentido que la ponga en un ítem. De todos modos, muchas gracias de nuevo! :) – pemistahl

1

Si entiendo correctamente el problema, puede obtener la URL de response.url y luego escribir en item['url'].

En Spider: item['url'] = response.url

Y en la tubería: url = item['url'].

O ponga response.url en meta como warvariuc escribió.

1

Parece que la respuesta de warvariuc requiere una ligera modificación a partir de Scrapy 1.3.3: debe sobrescribir _parse_response en lugar de. Anulación make_requests_from_url ya no es necesario.

Cuestiones relacionadas