2011-07-04 14 views
13

Por lo tanto, mi problema es relativamente simple. Tengo una araña que rastrea varios sitios y necesito que devuelva los datos en el orden en que los escribo en mi código. Está publicado a continuación.URL de Scrapy Crawl en orden

from scrapy.spider import BaseSpider 
from scrapy.selector import HtmlXPathSelector 
from mlbodds.items import MlboddsItem 

class MLBoddsSpider(BaseSpider): 
    name = "sbrforum.com" 
    allowed_domains = ["sbrforum.com"] 
    start_urls = [ 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/" 
    ] 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     sites = hxs.select('//div[@id="col_3"]//div[@id="module3_1"]//div[@id="moduleData4952"]') 
     items = [] 
     for site in sites: 
      item = MlboddsItem() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract()# | /*//table[position()<2]//tr//th[@colspan="2"]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c4"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c6"]//text()').extract() 
      items.append(item) 
     return items 

Los resultados se devuelven en un orden aleatorio, por ejemplo, se devuelve el 29a, entonces la 28a, entonces la 30a. Intenté cambiar el orden del programador de DFO a BFO, por si ese era el problema, pero eso no cambió nada.

Gracias de antemano.

+0

Caan que nos muestran cómo se está llamando tu araña? –

+0

> Tengo una araña que rastrea varios sitios, ¿Quiere decir varias URL de inicio? – warvariuc

Respuesta

15

start_urls define las URL que se utilizan en el método start_requests. Se llama al método parse con una respuesta para cada URL de inicio cuando se descarga la página. Pero no puede controlar los tiempos de carga: la primera URL de inicio podría ser la última en parse.

Una solución - anula el método start_requests y agrega a las solicitudes generadas un meta con la clave priority. En parse extraiga este valor de priority y agréguelo al item. En la tubería, haga algo basado en este valor.(No sé por qué y dónde necesita que se procesen estas urls en este orden).

O hazlo de forma sincrónica: guarda estas URL de inicio en algún lugar. Ponga en start_urls el primero de ellos. En parse procese la primera respuesta y ceda el/los artículo/s, luego tome la siguiente url de su almacenamiento y solicite la devolución de llamada con parse.

+0

Todos los buenos comentarios, gracias a todos por la ayuda. Este se acercó más a lo que yo quería hacer. – Jeff

+0

Tengo una pregunta relacionada. Supongamos que quiero especificar una lista de URL de manera que la primera sea la página de inicio de un sitio web y la siguiente es una lista de páginas web. ¿Cómo lo hago? –

+0

@PrakharMohanSrivastava, póngalos en ['start_urls'] (http://doc.scrapy.org/en/latest/topics/spiders.html#scrapy.spider.Spider.start_urls)? – warvariuc

0

Negación: no han trabajado con scrapy específicamente

El rascador puede estar haciendo cola y requeueing solicitudes en base a los tiempos de espera y los errores HTTP, sería mucho más fácil si se puede obtener en la fecha de la página de respuesta?

I.e. agregue otra instrucción hxs.select que capte la fecha (simplemente eche un vistazo, definitivamente está en los datos de respuesta), y agréguela al ítem dict, ordene los ítems en función de eso.

Esto es probablemente un enfoque más sólido, en lugar de confiar en orden de rasguños ...

0

Creo que el

hxs.select('...') 

que realice raspar los datos desde el sitio en el orden en que aparece . O bien eso o scrapy está pasando por su start_urls en un orden arbitrario. Para obligarlo a pasar por ellos en un orden predefinido, y fíjate, esto no funcionará si tiene que arrastrarse más sitios, entonces usted puede probar esto:

start_urls = ["url1.html"] 

def parse1(self, response): 
    hxs = HtmlXPathSelector(response) 
    sites = hxs.select('blah') 
    items = [] 
    for site in sites: 
     item = MlboddsItem() 
     item['header'] = site.select('blah') 
     item['game1'] = site.select('blah') 
     items.append(item) 
    return items.append(Request('url2.html', callback=self.parse2)) 

luego escribir un parse2 que hace lo mismo pero agrega una Solicitud de url3.html con callback = self.parse3. Este es un estilo de codificación horrible, pero lo estoy tirando en caso de que necesites un truco rápido.

2

Dudo si es posible lograr lo que desea, a menos que juegue con el interior de scrapy. Existen discusiones similares sobre los grupos de google de scrapy, p.

http://groups.google.com/group/scrapy-users/browse_thread/thread/25da0a888ac19a9/1f72594b6db059f4?lnk=gst

Una cosa que puede también ayuda se poniendo CONCURRENT_REQUESTS_PER_SPIDER a 1, pero no va a garantizar por completo la orden ya sea porque el descargador tiene su propia cola local por razones de rendimiento, así que la mejor que puede hacer es priorizar las solicitudes pero no garantizar su orden exacto.

6

La discusión de grupo de google sugiere el uso de atributo de prioridad en el objeto Solicitud. Scrapy garantiza que las URL se rastrean en DFO de forma predeterminada. Pero no garantiza que las URL se visiten en el orden en que se produjeron en su devolución de llamada parse.

En lugar de ceder a los objetos de Solicitud, desea devolver una matriz de Solicitudes desde las cuales se mostrarán los objetos hasta que estén vacíos.

¿Puedes intentar algo como eso?

from scrapy.spider import BaseSpider 
from scrapy.http import Request 
from scrapy.selector import HtmlXPathSelector 
from mlbodds.items import MlboddsItem 

class MLBoddsSpider(BaseSpider): 
    name = "sbrforum.com" 
    allowed_domains = ["sbrforum.com"] 

    def start_requests(self): 
     start_urls = reversed([ 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/" 
     ]) 

     return [ Request(url = start_url) for start_url in start_urls ] 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     sites = hxs.select('//div[@id="col_3"]//div[@id="module3_1"]//div[@id="moduleData4952"]') 
     items = [] 
     for site in sites: 
      item = MlboddsItem() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract()# | /*//table[position()<2]//tr//th[@colspan="2"]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c4"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c6"]//text()').extract() 
      items.append(item) 
     return items 
1

Por supuesto, puede controlarlo. El secreto más importante es el método de cómo alimentar al codicioso Engine/Schedulor. Tu requerimiento es solo un pequeño. Por favor, veo que agrego una lista llamada "task_urls".

from scrapy.spider import BaseSpider 
from scrapy.selector import HtmlXPathSelector 
from scrapy.http.request import Request 
from dirbot.items import Website 

class DmozSpider(BaseSpider): 
    name = "dmoz" 
    allowed_domains = ["sbrforum.com"] 
    start_urls = [ 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
    ] 
    task_urls = [ 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/" 
    ] 
    def parse(self, response): 

     hxs = HtmlXPathSelector(response) 
     sites = hxs.select('//div[@id="col_3"]//div[@id="module3_1"]//div[@id="moduleData4952"]') 
     items = [] 
     for site in sites: 
      item = Website() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract()# | /*//table[position()<2]//tr//th[@colspan="2"]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c4"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c6"]//text()').extract() 
      items.append(item) 
     # Here we feed add new request 
     self.task_urls.remove(response.url) 
     if self.task_urls: 
      r = Request(url=self.task_urls[0], callback=self.parse) 
      items.append(r) 

     return items 

Si desea algún caso más complicado, por favor ver mi proyecto: https://github.com/wuliang/TiebaPostGrabber

2

La solución es secuencial.
Esta solución es similar a @wuliang

empecé con el método de @Alexis Tréglodé pero alcancé un problema:
El hecho de que su método de start_requests() devuelve una lista de URL
return [ Request(url = start_url) for start_url in start_urls ]
está causando que la salida sea no secuencial (asíncrono)

Si el resultado es una sola respuesta, al crear una alternativa, other_urls puede cumplir los requisitos. Además, other_urls se puede utilizar para agregar URLs extraídas de otras páginas web.

from scrapy import log 
from scrapy.spider import BaseSpider 
from scrapy.http import Request 
from scrapy.selector import HtmlXPathSelector 
from practice.items import MlboddsItem 

log.start() 

class PracticeSpider(BaseSpider): 
    name = "sbrforum.com" 
    allowed_domains = ["sbrforum.com"] 

    other_urls = [ 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/", 
      ] 

    def start_requests(self): 
     log.msg('Starting Crawl!', level=log.INFO) 
     start_urls = "http://www.sbrforum.com/mlb-baseball/odds-scores/20110327/" 
     return [Request(start_urls, meta={'items': []})] 

    def parse(self, response): 
     log.msg("Begin Parsing", level=log.INFO) 
     log.msg("Response from: %s" % response.url, level=log.INFO) 
     hxs = HtmlXPathSelector(response) 
     sites = hxs.select("//*[@id='moduleData8460']") 
     items = response.meta['items'] 
     for site in sites: 
      item = MlboddsItem() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text()').extract() 
      items.append(item) 

     # here we .pop(0) the next URL in line 
     if self.other_urls: 
      return Request(self.other_urls.pop(0), meta={'items': items}) 

     return items 
9

Scrapy 'Solicitud' tiene un atributo de prioridad ahora. http://doc.scrapy.org/en/latest/topics/request-response.html#request-objects Si tiene muchas 'Solicitud' en una función y desea procesar una solicitud particular, en primer lugar, se puede establecer

def parse(self,response): url = http://www.example.com/first yield Request(url=url,callback = self.parse_data,priority=1) url = http://www.example.com/second yield Request(url=url,callback = self.parse_data)

Scrapy procesará el que tiene la prioridad 1 en primer lugar.

0

Personalmente, me gusta la implementación de @ user1460015 después de que pude tener mi propia solución de trabajo.

Mi solución es utilizar el subproceso de Python para llamar a la URL de scrapy por URL hasta que se hayan solucionado todas las URL.

En mi código, si el usuario no especifica que quiere analizar las URL secuencialmente, podemos iniciar la araña de forma normal.

process = CrawlerProcess({'USER_AGENT': 'Mozilla/4.0 (compatible; \ 
    MSIE 7.0; Windows NT 5.1)'}) 
process.crawl(Spider, url = args.url) 
process.start() 

Si un usuario especifica que hay que hacer de forma secuencial, podemos hacer esto:

for url in urls: 
    process = subprocess.Popen('scrapy runspider scrapper.py -a url='\ 
     + url + ' -o ' + outputfile) 
    process.wait() 

Tenga en cuenta que: esta aplicación no se ocupa de los errores.

0

Hay una manera mucho más fácil hacer scrapy seguir el orden de starts_url: puede simplemente elimine el comentario y cambiar los concurrentes solicitudes en settings.py a 1.

Configure maximum concurrent requests performed by Scrapy (default: 16) 
CONCURRENT_REQUESTS = 1 
Cuestiones relacionadas