返回Scrapy中的复杂项目(webcrawler)

时间:2013-11-01 01:18:25

标签: python web-crawler scrapy

我正在尝试使用scrapy制作一个特定目标的webcrawler,它会返回我的结果对象。我陷入困境,可能会完全倒退。

更具体地说,对于TheScienceForum.com(数学,物理等)的每个子论坛,我想得到每个子论坛中所有线程的标题,最后得到一个具有论坛名称和论坛中所有主题标题的列表。

最终目标是对主题标题进行文本分析,以确定与每个论坛相关的最常见术语/术语。最后我也想对线程本身进行分析。

我有一个类定义如下:

from scrapy.item import Item, Field

class ProjectItem(Item):
    name = Field() #the forum name
    titles = Field() #the titles

我可能误解了项目的工作原理,但我希望最终为每个子论坛提供一个项目,并在同一项目的列表中包含该子论坛中的所有主题标题。

我写的爬虫看起来像这样,但没有按预期运行:

    from scrapy.contrib.spiders import CrawlSpider, Rule
    from scrapy.selector import HtmlXPathSelector
    from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor

    from individualProject.items import ProjectItem

    class TheScienceForum(CrawlSpider):
        name = "TheScienceForum.com"
        allowed_domains = ["theScienceForum.com"]
        start_urls = ["http://www.thescienceforum.com"]
        rules = [Rule(SgmlLinkExtractor(restrict_xpaths=['//h2[@class="forumtitle"]/a']), 'parse_one'),Rule(SgmlLinkExtractor(restrict_xpaths=['//div[@class="threadpagenav"]']), 'parse_two')]

        def parse_one(self, response):
            Sel = HtmlXPathSelector(response)
            forumNames = Sel.select('//h2[@class="forumtitle"]/a/text()').extract()

            items = []
        for forumName in forumNames:
            item = projectItem()
            item['name'] = forumName
            items.append(item)
        yield items

        def parse_two(self, response):
            Sel = HtmlXPathSelector(response)
            threadNames = Sel.select('////h3[@class="threadtitle"]/a/text()').extract()
            for item in items:
                for title in titles:
                    if Sel.select('//h1/span[@class="forumtitle"]/text()').extract()==item.name:
                        item['titles'] += Sel.select('//h3[@class="threadtitle"]/a/text()').extract()
            return items

我们的想法是从所有子论坛名称所在的网站的主页面开始。第一条规则只允许链接到第一个子论坛页面和与之关联的解析函数,它意味着为每个子论坛创建一个项目,在“名称”属性的论坛名称中加入。

对于以下请求,使用第二个规则,蜘蛛仅限于导航包含子论坛的所有线程(分页链接)的页面。第二个解析方法是将线程标题添加到与当前子论坛的名称相对应的项目(在第一个解析方法中创建)(Sel.select('// h1 / span [@ class =“forumtitle”] /文本()')。提取())

蜘蛛正在爬到所有主要的论坛页面,但对于每一个我都收到以下错误:

2013-11-01 13:05:37-0400 [TheScienceForum.com] ERROR: Spider must return Request, BaseItem or None, got 'list' in <GET http://www.thescienceforum.com/mathematics/>

非常感谢任何帮助或建议。谢谢!

2 个答案:

答案 0 :(得分:2)

我找到了解决我遇到的抓取问题的解决方案。以下代码在论坛主页上启动蜘蛛,为每个子论坛创建一个新项目。然后蜘蛛跟随链接,前往子论坛的每个页面,沿途收集线程标题(将它们添加到相关项目,所有这些都与下一个请求一起传递)。代码如下:

from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.http import Request

from individualProject.items import ProjectItem

class TheScienceForum(BaseSpider):
    name = "TheScienceForum.com"
    allowed_domains = ["www.thescienceforum.com"]
    start_urls = ["http://www.thescienceforum.com"]
    #rules = [Rule(SgmlLinkExtractor(restrict_xpaths=['//h2[@class="forumtitle"]/a']), 'parse_one'),Rule(SgmlLinkExtractor(restrict_xpaths=['//div[@class="threadpagenav"]']), 'parse_two')]

    def parse(self, response):
        Sel = HtmlXPathSelector(response)
        forumNames = Sel.select('//h2[@class="forumtitle"]/a/text()').extract()
        items = []
        for forumName in forumNames:
            item = ProjectItem()
            item['name'] = forumName
            items.append(item)


        forums = Sel.select('//h2[@class="forumtitle"]/a/@href').extract()
        itemDict = {}
        itemDict['items'] = items
        for forum in forums:
            yield Request(url=forum,meta=itemDict,callback=self.addThreadNames)


    def addThreadNames(self, response):
        items = response.meta['items']
        Sel = HtmlXPathSelector(response)
        currentForum = Sel.select('//h1/span[@class="forumtitle"]/text()').extract()
        for item in items:
            if currentForum==item['name']:
                item['thread'] += Sel.select('//h3[@class="threadtitle"]/a/text()').extract()
        self.log(items)


        itemDict = {}
        itemDict['items'] = items
        threadPageNavs = Sel.select('//span[@class="prev_next"]/a[@rel="next"]/@href').extract()
        for threadPageNav in threadPageNavs:  
            yield Request(url=threadPageNav,meta=itemDict,callback=self.addThreadNames)

我现在遇到的问题是如何保存它要分类的数据(稍后进行分析)。在这方面,我又提出了另一个问题here

答案 1 :(得分:0)

Christian Temus建议您更好地描述您遇到的问题。查看代码我可以提出一些建议

  1. 您应该在for循环中执行“yield item”,而不是返回项目列表。

  2. 使用crawlspider

  3. 如果您使用crawlspider将'解析'方法重命名为其他内容,例如parse_titles。