Scrapy,Python:一个管道中有多个项目类?

vatpfxk5  于 2023-04-21  发布在  Python
关注(0)|答案(7)|浏览(173)

我有一个蜘蛛,刮数据,不能保存在一个项目类。
为了说明,我有一个Profile Item,每个Profile Item可能有未知数量的Comments。这就是为什么我想实现Profile Item和Comment Item。我知道我可以通过使用yield将它们传递到我的管道。
1.但是,我不知道一个带有一个parse_item函数的管道如何处理两个不同的item类?
1.或者是否可以使用不同的parse_item函数?
1.或者我必须使用多个管道?
1.或者是否可以将迭代器写入Scrapy Item字段?

comments_list=[]
comments=response.xpath(somexpath)
for x in comments.extract():
        comments_list.append(x)
    ScrapyItem['comments'] =comments_list
vc6uscn9

vc6uscn91#

默认情况下,每个项目都经过每个管道。
例如,如果你生成一个ProfileItem和一个CommentItem,它们都将通过所有管道。如果你有一个管道设置来跟踪项目类型,那么你的process_item方法可能看起来像这样:

def process_item(self, item, spider):
    self.stats.inc_value('typecount/%s' % type(item).__name__)
    return item

ProfileItem通过时,'typecount/ProfileItem'递增。当CommentItem通过时,'typecount/CommentItem'递增。
您可以让一个管道只处理一种类型的项目请求,但是,如果处理该项目类型是唯一的,请在继续之前检查项目类型:

def process_item(self, item, spider):
    if not isinstance(item, ProfileItem):
        return item
    # Handle your Profile Item here.

如果你在不同的管道中设置了上面的两个process_item方法,项目将通过这两个方法,被跟踪和处理(或者在第二个管道中被忽略)。
此外,您可以使用一个管道设置来处理所有“相关”项目:

def process_item(self, item, spider):
    if isinstance(item, ProfileItem):
        return self.handle_profile(item, spider)
    if isinstance(item, CommentItem):
        return self.handle_comment(item, spider)

def handle_profile(item, spider):
    # Handle profile here, return item

def handle_comment(item, spider):
    # Handle Comment here, return item

或者,你可以让它更复杂,开发一个类型委托系统,加载类并调用默认的处理程序方法,类似于Scrapy处理中间件/管道的方式。这真的取决于你需要它的复杂程度,以及你想做什么。

v1l68za4

v1l68za42#

定义多个项目在导出数据时,如果它们有关系(例如,配置文件1 - N注解),并且必须一起导出它们,这是一件棘手的事情,因为管道在不同的时间处理每个项目。这种情况的替代方法是定义自定义Scrapy字段,例如:

class CommentItem(scrapy.Item):
    profile = ProfileField()

class ProfileField(scrapy.item.Field):
   # your business here

但是,考虑到您必须有2个项目的情况,强烈建议为每种类型的项目使用不同的管道以及不同的导出器示例,以便您在不同的文件中获得此信息(如果您使用的是文件):

settings.py

ITEM_PIPELINES = {
    'pipelines.CommentsPipeline': 1,
    'pipelines.ProfilePipeline': 1,
}

管道.py

class CommentsPipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, CommentItem):
           # Your business here

class ProfilePipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, ProfileItem):
           # Your business here
cqoc49vn

cqoc49vn3#

@Rejected answer是解决方案,但它需要一些调整才能为我工作,所以在这里分享。pipeline.py:

from .items import MyFirstItem, MySecondItem # needed import of Items

    def process_item(self, item, spider):
        if isinstance(item, MyFirstItem):
            return self.handlefirstitem(item, spider) 
        if isinstance(item, MySecondItem):
            return self.handleseconditem(item, spider)

    def handlefirstitem(self, item, spider): # needed self added
        self.storemyfirst_db(item) # function to pipe it to database table
        return item

    def handleseconditem(self, item, spider): # needed self added
        self.storemysecond_db(item) # function to pipe it to database table
        return item
fiei3ece

fiei3ece4#

最直接的方法是让解析器包含两个子解析器,每个子解析器对应一种数据类型。主解析器从输入中确定类型,并将字符串传递给相应的子例程。
第二种方法是按顺序包含解析器:一个解析配置文件并忽略所有其他;第二个解析Comments并忽略所有其他内容(与上面相同的原则)。
这能让你前进吗?

a64a0gku

a64a0gku5#

我想出了这个解决方案。
1.我在www.example.com文件中创建了ITEMsetting.py

ITEMS = {
    'project.items.Item1': {
        'filename': 'item1',
    },
    'project.items.Item2': {
        'filename': 'item2',
    },
}

1.在www.example.com文件中导入的设置pipeline.py

from scrapy.utils.project import get_project_settings

1.在open_spider方法中,从设置创建文件和附加导出器中为每个项目

for settings_key in self.settings.keys():
    filename = os.path.join(f"output/{self.settings[settings_key]['filename']}_{self.dt}.csv")
    self.settings[settings_key]['file'] = open(filename, 'wb')
    self.settings[settings_key]['exporter'] = CsvItemExporter(
        self.settings[settings_key]['file'], 
        encoding='utf-8', 
        delimiter=';', 
        quoting=csv.QUOTE_NONNUMERIC
    )
    self.settings[settings_key]['exporter'].start_exporting()

1.在close_spider方法中停止所有导出器并关闭所有文件

for settings_key in self.settings.keys():
    self.settings[settings_key]['exporter'].finish_exporting()
    self.settings[settings_key]['file'].close()

1.在process_item方法中,只需选择具有适当导出器的项目并将其导出

item_class = f"{type(item).__module__}.{type(item).__name__}"
settings_item = self.settings.get(item_class)
if settings_item:
    settings_item['exporter'].export_item(item)
return item
ndasle7k

ndasle7k6#

python>=3.10https://www.python.org/dev/peps/pep-0622/
也许基于结构模式匹配实现路由器(@mdkb答案)会很方便
!item也是遗留创建的类,因为从python>=3.7有数据类

xfyts7mz

xfyts7mz7#

我建议在ProfileItem中添加评论。这样你就可以在一个人的个人资料中添加多个评论。其次,处理这种类型的数据会更容易。

相关问题