为scrapy中的每个类别制作单独的输出文件

Posted

技术标签:

【中文标题】为scrapy中的每个类别制作单独的输出文件【英文标题】:Making separate output files for every category in scrapy 【发布时间】:2020-06-22 20:12:33 【问题描述】:

我尝试根据黄页的类别抓取黄页。所以我从一个文本文件加载类别并将其提供给 start_urls。我在这里面临的问题是为每个类别分别保存输出。以下是我尝试实现的代码:

CATEGORIES = []
with open('Catergories.txt', 'r') as f:
    data = f.readlines()

    for category in data:
        CATEGORIES.append(category.strip())

在 settings.py 中打开文件并在蜘蛛中创建一个访问列表。

蜘蛛:

# -*- coding: utf-8 -*-
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from ..items import YellowItem
from scrapy.utils.project import get_project_settings

settings = get_project_settings()


class YpSpider(CrawlSpider):
    categories = settings.get('CATEGORIES')

    name = 'yp'
    allowed_domains = ['yellowpages.com']

    start_urls = ['https://www.yellowpages.com/search?search_terms=0&geo_location_terms=New%20York'
                      '%2C '
                      '%20NY'.format(*categories)]
    rules = (

        Rule(LinkExtractor(restrict_xpaths='//a[@class="business-name"]', allow=''), callback='parse_item',
             follow=True),

        Rule(LinkExtractor(restrict_xpaths='//a[@class="next ajax-page"]', allow=''),
             follow=True),
    )

    def parse_item(self, response):
        categories = settings.get('CATEGORIES')
        print(categories)
        item = YellowItem()
        # for data in response.xpath('//section[@class="info"]'):
        item['title'] = response.xpath('//h1/text()').extract_first()
        item['phone'] = response.xpath('//p[@class="phone"]/text()').extract_first()
        item['street_address'] = response.xpath('//h2[@class="address"]/text()').extract_first()
        email = response.xpath('//a[@class="email-business"]/@href').extract_first()
        try:
            item['email'] = email.replace("mailto:", '')
        except AttributeError:
            pass
        item['website'] = response.xpath('//a[@class="primary-btn website-link"]/@href').extract_first()
        item['Description'] = response.xpath('//dd[@class="general-info"]/text()').extract_first()
        item['Hours'] = response.xpath('//div[@class="open-details"]/descendant-or-self::*/text()[not(ancestor::*['
                                       '@class="hour-category"])]').extract()
        item['Other_info'] = response.xpath(
            '//dd[@class="other-information"]/descendant-or-self::*/text()').extract()
        category_ha = response.xpath('//dd[@class="categories"]/descendant-or-self::*/text()').extract()
        item['Categories'] = " ".join(category_ha)
        item['Years_in_business'] = response.xpath('//div[@class="number"]/text()').extract_first()
        neighborhood = response.xpath('//dd[@class="neighborhoods"]/descendant-or-self::*/text()').extract()
        item['neighborhoods'] = ' '.join(neighborhood)
        item['other_links'] = response.xpath('//dd[@class="weblinks"]/descendant-or-self::*/text()').extract()

        item['category'] = '0'.format(*categories)

        return item

       

这里是 pipelines.py 文件:

from scrapy import signals
from scrapy.exporters import CsvItemExporter
from scrapy.utils.project import get_project_settings

settings = get_project_settings()


class YellowPipeline(object):
    @classmethod
    def from_crawler(cls, crawler):
        pipeline = cls()
        crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
        crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
        return pipeline

    def spider_opened(self, spider):
        self.exporters = 
        categories = settings.get('CATEGORIES')

        file = open('0.csv'.format(*categories), 'w+b')

        exporter = CsvItemExporter(file, encoding='cp1252')
        exporter.fields_to_export = ['title', 'phone', 'street_address', 'website', 'email', 'Description',
                                     'Hours', 'Other_info', 'Categories', 'Years_in_business', 'neighborhoods',
                                     'other_links']
        exporter.start_exporting()
        for category in categories:
            self.exporters[category] = exporter

    def spider_closed(self, spider):

        for exporter in iter(self.exporters.items()):
            exporter.finish_exporting()

    def process_item(self, item, spider):

        self.exporters[item['category']].export_item(item)
        return item

运行代码后出现以下错误:

exporter.finish_exporting()
AttributeError: 'tuple' object has no attribute 'finish_exporting'

每个类别我都需要单独的 csv 文件。任何帮助将不胜感激。

【问题讨论】:

为什么iter()iter(self.exporters.items()) 中? 遍历字典项 在这种情况下,您不需要iter() 然后抛出未解决的引用错误.... 当唯一的改变是删除 iter() 时? 【参考方案1】:

我会在后期处理中这样做。将所有项目导出到一个带有类别字段的 .csv 文件。我认为你没有以正确的方式思考这个问题,并且过于复杂化了。不确定这是否可行,但值得一试:)

with open('parent.csv', 'r') as file:
    reader = csv.reader(file)
    for row in reader:
        with open('.csv'.format(row[category]), 'a') as f:
            writer = csv.writer(f)
            writer.writerow(row)

您也可以使用蜘蛛关闭信号应用此代码。

https://docs.scrapy.org/en/latest/topics/signals.html#scrapy.signals.spider_closed

【讨论】:

嘿,我试过了,问题是它可以完成这项工作,但我面临一些问题。它复制了数据,并且每一行后面都有一个空格。此外,我还想使用原始 csv 中使用的相同标题。如果你能帮忙,我将不胜感激 请有人编辑以使用 DictReader 并编写头文件。我没有时间atm 我已经解决了另一个问题。现在唯一的问题是标题。我无法弄清楚如何编写它们,因为 for 循环它们在打开多个 csv 文件时出现在每一行之后 use enumerate on reader... for idx, row in enumerate(reader): 然后你可以得到列表的索引并且只为索引0写标题 很高兴它终于成功了。如果您想要一个更永久的解决方案,您可以查看scrapy docs并创建自己的爬虫运行并将此代码附加到spider.finished信号,但是使用一次甚至几次我都会使用这个简单的脚本而不是而不是费心制作一个修改过的跑步者。【参考方案2】:

dict.items() 返回可迭代的,其中每个项目看起来像 tuple (key, value) 要消除此错误,您需要删除 iter 并解压缩像 for category, exporter in self.exporter.items(): 这样的项目

【讨论】:

以上是关于为scrapy中的每个类别制作单独的输出文件的主要内容,如果未能解决你的问题,请参考以下文章

Scrapy - 输出到多个 JSON 文件

在R中为一个工作表制作多个类别的单独直方图

遍历列中的每个类别并将另一列中的值添加为单独的 df

Scrapy python csv输出每行之间有空行

Scrapy 框架 - 着色日志记录

获取Scrapy爬虫输出/结果脚本文件功能