Scrapy: crawlspider не генерирует все ссылки во вложенных обратных вызовах - PullRequest
4 голосов
/ 14 марта 2012

Я написал scrawlspider для сканирования сайта со структурой, такой как страница категории> страница типа> страница списка> страница элемента.На странице категорий есть много категорий машин, у каждой из которых есть страница типа с большим количеством типов, у каждого из различных типов есть список элементов, и, наконец, у каждой машины есть страница с информацией о ней.

У моего паука есть правило для перехода с домашней страницы на страницу категории, где я определяю страницу обратного вызова parsecatpage, она генерирует элемент, захватывает категорию и выдает новый запрос для каждой категории на странице.Я передаю элемент и имя категории с помощью request.meta и указываю, что обратный вызов - это страница parsetype.

Parsetypepage получает элемент из response.meta, затем выдает запросы для каждого типа и передает элемент, а также объединяет категориюи введите вместе с ним в request.meta.Обратным вызовом является parsemachinelist.

Parsemachinelist получает элемент из response.meta, затем выдает запросы для каждого элемента в списке и передает элемент, категорию / тип, описание через request.meta окончательному обратному вызову parsemachine.Это получает мета-атрибуты и заполняет все поля в элементе, используя информацию на странице и информацию, которая была передана с предыдущих страниц, и, наконец, возвращает элемент.

Если я ограничу это одной категорией инаберите (например, contains[@href, "filter=c:Grinders"] и contains[@href, "filter=t:Disc+-+Horizontal%2C+Single+End"]), тогда это сработает, и на последней странице есть элемент машины для каждой машины.Проблема заключается в том, что, как только я позволяю пауку убирать все категории и все типы, он возвращает только те предметы, которые были убраны для машин, на первой из последних страниц, на которые он попадает, и после того, как он сделал это, паук закончил и неполучить другие категории и т. д.

Вот (анонимный) код

from scrapy.selector import HtmlXPathSelector
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.http import Request
from myspider.items import MachineItem
import urlparse


class MachineSpider(CrawlSpider):
    name = 'myspider'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/index.php']

    rules = (
        Rule(SgmlLinkExtractor(allow_domains=('example.com'),allow=('12\.html'),unique=True),callback='parsecatpage'),
        )

    def parsecatpage(self, response):
        hxs = HtmlXPathSelector(response)
#this works, next line doesn't   categories = hxs.select('//a[contains(@href, "filter=c:Grinders")]')  
        categories = hxs.select('//a[contains(@href, "filter=c:Grinders") or contains(@href, "filter=c:Lathes")]')
        for cat in categories:
            item = MachineItem()
            req = Request(urlparse.urljoin(response.url,''.join(cat.select("@href").extract()).strip()),callback=self.parsetypepage)
            req.meta['item'] = item
            req.meta['machinecategory'] = ''.join(cat.select("./text()").extract())
            yield req

    def parsetypepage(self, response):
        hxs = HtmlXPathSelector(response)
#this works, next line doesn't   types = hxs.select('//a[contains(@href, "filter=t:Disc+-+Horizontal%2C+Single+End")]')
        types = hxs.select('//a[contains(@href, "filter=t:Disc+-+Horizontal%2C+Single+End") or contains(@href, "filter=t:Lathe%2C+Production")]')
        for typ in types:
            item = response.meta['item']
            req = Request(urlparse.urljoin(response.url,''.join(typ.select("@href").extract()).strip()),callback=self.parsemachinelist)
            req.meta['item'] = item
            req.meta['machinecategory'] = ': '.join([response.meta['machinecategory'],''.join(typ.select("./text()").extract())])
            yield req

    def parsemachinelist(self, response):
        hxs = HtmlXPathSelector(response)
        for row in hxs.select('//tr[contains(td/a/@href, "action=searchdet")]'):
            item = response.meta['item']
            req = Request(urlparse.urljoin(response.url,''.join(row.select('./td/a[contains(@href,"action=searchdet")]/@href').extract()).strip()),callback=self.parsemachine)
            print urlparse.urljoin(response.url,''.join(row.select('./td/a[contains(@href,"action=searchdet")]/@href').extract()).strip())
            req.meta['item'] = item
            req.meta['descr'] = row.select('./td/div/text()').extract()
            req.meta['machinecategory'] = response.meta['machinecategory']
            yield req

    def parsemachine(self, response):
        hxs = HtmlXPathSelector(response)
        item = response.meta['item']
        item['machinecategory'] = response.meta['machinecategory']
        item['comp_name'] = 'Name'
        item['description'] = response.meta['descr']
        item['makemodel'] = ' '.join([''.join(hxs.select('//table/tr[contains(td/strong/text(), "Make")]/td/text()').extract()),''.join(hxs.select('//table/tr[contains(td/strong/text(), "Model")]/td/text()').extract())])
        item['capacity'] = hxs.select('//tr[contains(td/strong/text(), "Capacity")]/td/text()').extract()
        relative_image_url = hxs.select('//img[contains(@src, "custom/modules/images")]/@src')[0].extract()
        abs_image_url = urlparse.urljoin(response.url, relative_image_url.strip())
        item['image_urls'] = [abs_image_url]
        yield item

SPIDER = MachineSpider()

Так, например, паук найдет измельчители на странице категории и перейдет на страницу типа измельчителя, где он будетнайдите тип диска Горизонтальный одинарный конец, затем он перейдет на эту страницу, найдет список машин и перейдет на страницу каждой машины, и, наконец, будет элемент для каждой машины.Если вы попытаетесь перейти к шлифовальным станкам и токарным станкам, хотя он будет нормально проходить через шлифовальные станки, он будет сканировать страницы типа токарных и токарных станков и останавливаться на этом без генерации запросов на страницу со списком токарных станков и последние страницы станков.*Может кто-нибудь помочь с этим?Почему паук не попадает на вторую (или третью и т. Д.) Страницу со списком машин, если существует более одной категории машин?

Извините за эпический пост, просто пытаюсь объяснить проблему !!

Спасибо !!

Ответы [ 2 ]

2 голосов
/ 14 марта 2012

Вы должны напечатать URL-адрес запроса, чтобы убедиться, что все в порядке.Также вы можете попробовать эту версию:

def parsecatpage(self, response):
    hxs = HtmlXPathSelector(response)
    categories = hxs.select('//a[contains(@href, "filter=c:Grinders") or contains(@href, "filter=c:Lathes")]')
    for cat in categories:
        item = MachineItem()
        cat_url = urlparse.urljoin(response.url, cat.select("./@href").extract()[0])
        print 'url:', cat_url # to see what's there
        cat_name = cat.select("./text()").extract()[0]
        req = Request(cat_url, callback=self.parsetypepage, meta={'item': item, 'machinecategory': cat_name})
        yield req
0 голосов
/ 16 марта 2012

Проблема заключалась в том, что веб-сайт настроен таким образом, что переход от категории к типовой странице (и следующим страницам) происходит посредством фильтрации отображаемых результатов. Это означает, что если запросы выполняются от глубины до основания запроса, то это работает (то есть выберите категорию, затем получите все типы этой категории, затем получите все машины каждого типа, затем очистите страницу каждой машины) но если запрос на страницу следующего типа обрабатывается до того, как паук получил URL-адреса для каждой машины первого типа, тогда URL-адреса больше не являются правильными, и паук достигает неверной страницы и не может извлечь информацию для следующего шага.

Чтобы решить эту проблему, я определил обратный вызов установки категории, который вызывается только в первый раз и получает список всех категорий с именем categories, затем обратный вызов категории, который вызывается из установки категории, которая запускает сканирование с одной категорией. только используя categories.pop(). Как только паук достиг дна вложенных обратных вызовов и очистил все машины в списке, снова выполняется обратный вызов до обратного вызова категории (необходимо dont_follow=True в Request), где categories.pop() запускает процесс снова со следующей категорией в списке, пока они все не будут сделаны. Таким образом, каждая категория полностью обрабатывается до запуска следующей, и она работает.

Спасибо за ваш последний комментарий, который заставил меня задуматься о правильном и привел меня к решению!

...