2011-07-04 2 views
13

Итак, моя проблема относительно проста. У меня есть один паук, который сканирует несколько сайтов, и мне нужно, чтобы они возвращали данные в том порядке, в котором я пишу его в своем коде. Он размещен ниже.Сканирование URL-адресов для сканирования в порядке

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 

Результаты возвращаются в случайном порядке, например, он возвращает 29, затем 28, затем 30-го. Я попытался изменить порядок планировщика из DFO в BFO, на случай, если это была проблема, но это ничего не изменило.

Заранее спасибо.

+0

Каан вы показать нам, как вы звоните ваш паук? –

+0

> У меня есть один паук, сканирующий несколько сайтов, Вы имеете в виду несколько стартовых URL-адресов? – warvariuc

ответ

15

start_urls определяет URL-адреса, которые используются в методе start_requests. Ваш метод parse вызывается с ответом для каждого начального URL-адреса при загрузке страницы. Но вы не можете контролировать время загрузки - первый URL-адрес запуска может быть последним до parse.

Решение - переопределить метод start_requests и добавить к сгенерированным запросам a meta с priority ключ. В parse выведите это значение priority и добавьте его в item. В трубопроводе сделайте что-то, основанное на этом значении.(Я не знаю, почему и где вам нужны эти URL-адреса для обработки в этом порядке).

Или сделайте это своего рода синхронным - храните эти стартовые URL-адреса. Поместите в start_urls первый из них. В parse обработайте первый ответ и введите товар (-ы), затем возьмите следующий url из своего хранилища и сделайте запрос на него с обратным вызовом для parse.

+0

Все отличные отзывы, спасибо всем за помощь. Этот был ближе всего к тому, что я хотел сделать. – Jeff

+0

У меня есть родственный вопрос. Предположим, я хочу указать список URL-адресов, чтобы первая была домашней страницей веб-сайта, а следующая - список веб-страниц. Как мне это сделать? –

+0

@PrakharMohanSrivastava, поместите их в ['start_urls'] (http://doc.scrapy.org/en/latest/topics/spiders.html#scrapy.spider.Spider.start_urls)? – warvariuc

0

Отказ от ответственности: не работали с Scrapy специально

Скребка может быть очереди и перенаправляя запросы, основанные на время ожидания и ошибках HTTP, это было бы намного проще, если вы можете получить на дату со страницы ответа?

I.e. добавьте еще один оператор hxs.select, который захватывает дату (просто посмотрел, это определенно в данных ответа), и добавьте это к элементу dict, сортируйте элементы на основе этого.

Это, вероятно, более надежный подход, а не полагаться на порядок передряги ...

0

Я считаю, что

hxs.select('...') 

вы делаете, будут царапать данные с сайта в том порядке, как кажется , Либо это, либо scrapy проходит через ваш start_urls в произвольном порядке. Для того, чтобы заставить его пройти через них в определенном порядке, и заметьте, это не будет работать, если вам нужно сканировать несколько сайтов, то вы можете попробовать это:

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)) 

затем написать parse2, что делает то же самое но добавляет запрос для url3.html с обратным вызовом = self.parse3. Это ужасный стиль кодирования, но я просто выбрасываю его, если вам нужен быстрый хак.

2

Я сомневаюсь, что можно добиться того, чего вы хотите, если не будете играть с внутренними органами. Есть несколько подобных обсуждений по группам google scrapy, например.

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

Одна вещь, которая может также помощь установка CONCURRENT_REQUESTS_PER_SPIDER 1, но он не будет полностью обеспечить порядок либо потому, что загрузчик имеет свою собственную локальную очередь по соображениям производительности, так что лучший вы можете сделать, это расставить приоритеты для запросов , но не гарантирует его точный порядок.

6

Обсуждение группы google предполагает использование атрибута priority в объекте Request. Scrapy гарантирует, что URL-адреса просканированы в DFO по умолчанию. Но это не гарантирует, что URL-адреса посещаются в том порядке, в котором они были получены в вашем обратном вызове разбора.

Вместо того чтобы давать объекты Request, вы хотите вернуть массив запросов, из которых будут выгружаться объекты до тех пор, пока они не будут пустыми.

Можете ли вы попробовать что-то подобное?

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

Конечно, вы можете управлять им. Совершенно секретно, как кормить жадный двигатель/планировщик. Требование - это всего лишь немного. Посмотрите, что я добавляю список с именем «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 

Если вы хотите, чтобы некоторые более сложный случай, пожалуйста, см мой проект: https://github.com/wuliang/TiebaPostGrabber

2

Решение является последовательным.
Это решение аналогично @wuliang

Я начал с методом @Alexis де Tréglodé но достиг проблемы:
Тот факт, что ваш метод start_requests() возвращает список URLS
return [ Request(url = start_url) for start_url in start_urls ]
вызывает выход быть (несинхронный)

Если возврат представляет собой один отклик, то путем создания альтернативы other_urls может выполнять требования. Кроме того, other_urls может использоваться для добавления URL-адресов, очищенных от других веб-страниц.

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 'Request' имеет атрибут приоритета. http://doc.scrapy.org/en/latest/topics/request-response.html#request-objects Если у вас есть много «Request» в функции и требуется обработать конкретный запрос первым, вы можете установить

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 будет обрабатывать один с приоритетом 1 в первую очередь.

0

Лично мне нравится реализация @ user1460015 после того, как мне удалось самостоятельно решить проблему.

Мое решение заключается в том, чтобы использовать подпроцесс Python для вызова URL-адреса scup url до тех пор, пока не будут учтены все URL-адреса.

В моем коде, если пользователь не указывает, что он/она хочет последовательно анализировать URL-адреса, мы можем запустить паук обычным способом.

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

Если пользователь указывает, что должно быть сделано последовательно, мы можем сделать это:

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

Обратите внимание, что: эта реализация не обрабатывает ошибки.

0

Существует гораздо более простой способ сделать SCRAPY следовать порядку starts_url: вы можете просто раскомментировать и изменить одновременных запросов в settings.py к 1.

Configure maximum concurrent requests performed by Scrapy (default: 16) 
CONCURRENT_REQUESTS = 1 
Смежные вопросы