Scrapy Crawl URL в порядке - PullRequest
       22

Scrapy Crawl URL в порядке

18 голосов
/ 04 июля 2011

Итак, моя проблема относительно проста.У меня есть один паук, сканирующий несколько сайтов, и мне нужно, чтобы он возвращал данные в том порядке, в котором я записал их в своем коде.Это опубликовано ниже.

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, на всякий случай, если это было проблемой, но это ничего не изменило.

Ответы [ 10 ]

19 голосов
/ 06 июля 2011

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

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

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

15 голосов
/ 23 октября 2014

Scrapy Request теперь имеет атрибут priority.

Если у вас много 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 будет обрабатывать тот, в котором сначала будет priority=1.

8 голосов
/ 07 февраля 2012

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

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

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

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
2 голосов
/ 28 июня 2012

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

Я начал с метода @Alexis de Tréglodé, но столкнулся с проблемой:
Тот факт, что ваш метод start_requests() возвращает список URL-адресов
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
2 голосов
/ 04 июля 2011

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

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

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

1 голос
/ 06 февраля 2018

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

Configure maximum concurrent requests performed by Scrapy (default: 16) 
CONCURRENT_REQUESTS = 1
1 голос
/ 18 апреля 2012

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

0 голосов
/ 22 декабря 2017

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

Мое решение состоит в том, чтобы использовать подпроцесс Python для вызова URL-адреса scrapy по 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 голосов
/ 04 июля 2011

Я полагаю, что

hxs.select('...')

, который вы сделаете, будет очищать данные с сайта в порядке их появления.Либо это, либо скрап проходит через ваш 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 с callback = self.parse3.Это ужасный стиль кодирования, но я просто выкидываю его на случай, если вам нужен быстрый взлом.

0 голосов
/ 04 июля 2011

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...