将scrapy项目导出到不同的文件

问题描述 投票:0回答:2

我正在从 moocs 上抓取评论,喜欢这个 one

从那里我可以获取所有课程详细信息、5 个项目和每个评论本身的另外 6 个项目。

这是我的课程详细信息的代码:

def parse_reviews(self, response):
    l = ItemLoader(item=MoocsItem(), response=response)
    l.add_xpath('course_title', '//*[@class="course-header-ng__main-info__name__title"]//text()')
    l.add_xpath('course_description', '//*[@class="course-info__description"]//p/text()')
    l.add_xpath('course_instructors', '//*[@class="course-info__instructors__names"]//text()')
    l.add_xpath('course_key_concepts', '//*[@class="key-concepts__labels"]//text()')
    l.add_value('course_link', response.url)
    return l.load_item()

现在我想包含评论详细信息,每个评论另外 5 个项目。 由于课程数据对于所有评论都是通用的,因此我想将其存储在不同的文件中,然后使用课程名称/ID 来关联数据。

这是我的评论项目的代码:

for review in response.xpath('//*[@class="review-body"]'):
    review_body = review.xpath('.//div[@class="review-body__content"]//text()').extract()
    course_stage =  review.xpath('.//*[@class="review-body-info__course-stage--completed"]//text()').extract()
    user_name =  review.xpath('.//*[@class="review-body__username"]//text()').extract()
    review_date =  review.xpath('.//*[@itemprop="datePublished"]/@datetime').extract()
    score =  review.xpath('.//*[@class="sr-only"]//text()').extract()

我尝试使用临时解决方案,返回每个案例的所有项目,但也不起作用:

def parse_reviews(self, response):
    #print response.body
    l = ItemLoader(item=MoocsItem(), response=response)
    #l = MyItemLoader(selector=response)
    l.add_xpath('course_title', '//*[@class="course-header-ng__main-info__name__title"]//text()')
    l.add_xpath('course_description', '//*[@class="course-info__description"]//p/text()')
    l.add_xpath('course_instructors', '//*[@class="course-info__instructors__names"]//text()')
    l.add_xpath('course_key_concepts', '//*[@class="key-concepts__labels"]//text()')
    l.add_value('course_link', response.url)

    for review in response.xpath('//*[@class="review-body"]'):
        l.add_xpath('review_body', './/div[@class="review-body__content"]//text()')
        l.add_xpath('course_stage', './/*[@class="review-body-info__course-stage--completed"]//text()')
        l.add_xpath('user_name', './/*[@class="review-body__username"]//text()')
        l.add_xpath('review_date', './/*[@itemprop="datePublished"]/@datetime')
        l.add_xpath('score', './/*[@class="sr-only"]//text()')

        yield l.load_item()

该脚本的输出文件已损坏,单元格被替换并且字段的大小不正确。

编辑: 我想在输出中有两个文件:

第一个包含:

course_title,course_description,course_instructors,course_key_concepts,course_link

第二个是:

course_title,review_body,course_stage,user_name,review_date,score
python scrapy scrapy-pipeline
2个回答
7
投票

问题是您将所有内容混合到一个项目中,这不是正确的方法。您应该创建两个项目:

MoocsItem
MoocsReviewItem

然后更新如下代码

def parse_reviews(self, response):
    #print response.body
    l = ItemLoader(item=MoocsItem(), response=response)
    l.add_xpath('course_title', '//*[@class="course-header-ng__main-info__name__title"]//text()')
    l.add_xpath('course_description', '//*[@class="course-info__description"]//p/text()')
    l.add_xpath('course_instructors', '//*[@class="course-info__instructors__names"]//text()')
    l.add_xpath('course_key_concepts', '//*[@class="key-concepts__labels"]//text()')
    l.add_value('course_link', response.url)

    item = l.load_item()

    for review in response.xpath('//*[@class="review-body"]'):
        r = ItemLoader(item=MoocsReviewItem(), response=response, selector=review)
        r.add_value('course_title', item['course_title'])
        r.add_xpath('review_body', './/div[@class="review-body__content"]//text()')
        r.add_xpath('course_stage', './/*[@class="review-body-info__course-stage--completed"]//text()')
        r.add_xpath('user_name', './/*[@class="review-body__username"]//text()')
        r.add_xpath('review_date', './/*[@itemprop="datePublished"]/@datetime')
        r.add_xpath('score', './/*[@class="sr-only"]//text()')

        yield r.load_item()

    yield item

现在您想要的是不同的项目类型放入不同的 csv 文件中,这就是下面的 SO 线程的答案:

scrapy如何将项目导出为每个项目单独的csv文件

我还没有测试以下内容,但代码看起来像这样:

from scrapy.exporters import CsvItemExporter
from scrapy import signals
from scrapy.xlib.pydispatch import dispatcher


def item_type(item):
    return type(item).__name__.replace('Item','').lower()  # TeamItem => team

class MultiCSVItemPipeline(object):
    SaveTypes = ['moocs','moocsreview']
    
    def __init__(self):
        dispatcher.connect(self.spider_opened, signal=signals.spider_opened)
        dispatcher.connect(self.spider_closed, signal=signals.spider_closed)

    def spider_opened(self, spider):
        self.files = dict([ (name, open(CSVDir+name+'.csv','w+b')) for name in self.SaveTypes ])
        self.exporters = dict([ (name,CsvItemExporter(self.files[name])) for name in self.SaveTypes])
        [e.start_exporting() for e in self.exporters.values()]

    def spider_closed(self, spider):
        [e.finish_exporting() for e in self.exporters.values()]
        [f.close() for f in self.files.values()]

    def process_item(self, item, spider):
        what = item_type(item)
        if what in set(self.SaveTypes):
            self.exporters[what].export_item(item)
        return item

您需要确保

ITEM_PIPELINES
已更新才能使用此
MultiCSVItemPipeline

ITEM_PIPELINES = {
    'mybot.pipelines.MultiCSVItemPipeline': 300,
}

-1
投票

现在有一个简单的方法可以实现这一点。我们可以在

settings.py
文件中配置 feed 导出,如下所示 -

FEED = {
    'team.csv': {
        'format': 'csv',
        'encoding': 'utf8',
        'store_empty': False,
        'item_classes': [MyItemClass1, 'myproject.items.TeamItem'],
        'fields': None,
        'indent': 4,
        'item_export_kwargs': {
           'export_empty_fields': True,
        },
    },
{
    'club.csv': {
        'format': 'csv',
        'encoding': 'utf8',
        'store_empty': False,
        'item_classes': ['myproject.items.ClubItem'],
        'fields': None,
        'indent': 4,
        'item_export_kwargs': {
           'export_empty_fields': True,
        },
    },

这将生成两个不同的

.csv
文件,并按照上面的
item_classes
键重定向相应的项目。

文档不太清楚,但这是我推断的链接 - Feed Exports

© www.soinside.com 2019 - 2024. All rights reserved.