我有一个蜘蛛,可以抓取无法保存在一个项目类中的数据。
为了说明这一点,我有一个个人资料项目,每个个人资料项目可能有未知数量的评论。这就是为什么我想实现个人资料项和评论项。我知道我可以简单地使用 Yield 将它们传递到我的管道。
但是,我不知道具有一个 parse_item 函数的管道如何处理两个不同的项目类?
或者是否可以使用不同的parse_item函数?
或者我必须使用多个管道吗?
或者是否可以将迭代器写入Scrapy Item Field?
comments_list=[]
comments=response.xpath(somexpath)
for x in comments.extract():
comments_list.append(x)
ScrapyItem['comments'] =comments_list
默认情况下,每个项目都会经过每个管道。
例如,如果您生成
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(self, item, spider):
# Handle profile here, return item
def handle_comment(self, item, spider):
# Handle Comment here, return item
或者,您可以使其变得更加复杂,并开发一个类型委托系统来加载类并调用默认处理程序方法,类似于 Scrapy 处理中间件/管道的方式。这实际上取决于您需要它的复杂程度以及您想要做什么。
定义多个项目,当您导出数据时,如果它们具有关系(例如,配置文件 1 - N 条评论),并且您必须将它们一起导出,因为每个项目都由管道在不同时间处理,那么定义多个项目是一件棘手的事情。此场景的另一种方法是定义自定义 Scrapy 字段,例如:
class CommentItem(scrapy.Item):
profile = ProfileField()
class ProfileField(scrapy.item.Field):
# your business here
但是考虑到您必须有 2 个项目的情况,强烈建议对每种类型的项目使用不同的管道以及不同的导出器实例,以便您在不同的文件中获取此信息(如果您使用文件) :
设置.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
@Rejected 答案是解决方案,但它需要一些调整才能对我起作用,所以在这里分享。这是我的 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
最直接的方法是让解析器包含两个子解析器,每个解析器对应一种数据类型。主解析器确定输入的类型并将字符串传递给适当的子例程。
第二种方法是按顺序包含解析器:一个解析配置文件并忽略其他所有内容;第二个解析 Comments 并忽略所有其他内容(与上面的原理相同)。
这会推动你前进吗?
我想出了这个解决方案。
ITEMS = {
'project.items.Item1': {
'filename': 'item1',
},
'project.items.Item2': {
'filename': 'item2',
},
}
from scrapy.utils.project import get_project_settings
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()
for settings_key in self.settings.keys():
self.settings[settings_key]['exporter'].finish_exporting()
self.settings[settings_key]['file'].close()
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
来自
python>=3.10
https://www.python.org/dev/peps/pep-0622/
基于结构模式匹配来实现路由器(@mdkb回答)可能会很方便
!项目也是遗留创建的类,因为从
python>=3.7
开始就有数据类
我建议在
ProfileItem
中添加评论。这样您就可以在一个人的个人资料中添加多个评论。其次,处理此类数据会更容易。