Scrapy:crawlspider在嵌套回调中不生成所有链接

时间:2012-03-14 15:44:06

标签: python scrapy yield

我已经编写了一个scrapy crawlspider来抓取一个网站,其结构类似于类别页面>打印页面>列表页面>项目页面。在类别页面上有许多类别的机器,每个机器都有一个包含许多类型的类型页面,每个不同类型都有一个项目列表,最后每台机器都有一个包含相关信息的页面。

我的蜘蛛有一条规则从主页到我定义回调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"]),那么它可以工作,并且最终页面上的每台计算机都有一个计算机项目。问题是,一旦我允许蜘蛛扫描所有类别和所有类型,它只返回它到达的最后一页上的机器的scrapy项目,一旦它完成了蜘蛛完成并且没有#39 ;得到其他类别等。

这是(匿名)代码

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

因此,例如蜘蛛会在类别页面上找到Grinders并转到Grinder类型页面,在那里它将找到Disc Horizo​​ntal Single End类型,然后它将转到该页面并查找机器列表并转到每个机器页面,最后每台机器都会有一个项目。如果您尝试转到磨床和车床,虽然它会完全通过磨床,然后它会抓住车床和车床类型页面并停在那里,而不会产生对车床列表页面和最终车床页面的请求。

任何人都可以帮忙吗?一旦有多个机器类别,为什么蜘蛛不会进入第二个(或第三个等)机器列表页面?

对不起史诗贴,只是想解释一下这个问题!!

谢谢!

2 个答案:

答案 0 :(得分:2)

您应该打印请求的网址,以确保没问题。您也可以尝试这个版本:

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

答案 1 :(得分:0)

问题在于网站已设置为通过过滤显示的结果,从类别页面(以及后续页面)移动。这意味着如果请求首先深入到查询的底部,那么它就可以工作(即选择一个类别,然后获取该类别的所有类型,然后获取每种类型的所有机器,然后刮掉每台机器的页面)但是如果在蜘蛛获得第一种类型中每台机器的网址之前处理了对下一个类型页面的请求,则网址不再正确并且蜘蛛到达不正确的页面并且无法提取下一步的信息。

为了解决这个问题,我定义了一个类别设置回调,它只是第一次被调用,并获得了一个名为categories的所有类别的列表,然后是一个从类别设置调用的类别回调,它开始爬行单一类别仅使用categories.pop()。一旦蜘蛛到达嵌套回调的底部并抓取列表中的所有计算机,就会再次回调到类别回调(dont_follow=True中需要Request){{1使用列表中的下一个类别再次启动该过程,直到它们全部完成。这样,每个类别在下一个类别开始之前都会被完全处理,并且可以正常工

感谢您的最终评论,这让我思考正确的方向,并引导我找到解决方案!