我想刮掉一页数据(使用Python Scrapy库),而不必在页面上定义每个字段。相反,我想使用元素的id
作为字段名称来动态生成字段。
起初我认为最好的方法是拥有一个收集所有数据的管道,并在它拥有所有数据后输出。
然后我意识到我需要将数据传递给项目中的管道,但我无法定义项目,因为我不知道它需要哪些字段!
解决这个问题的最佳方式是什么?
答案 0 :(得分:16)
旧方法不能与item loaders一起使用,并且不必要地使事情复杂化。这是实现灵活项目的更好方式:
from scrapy.item import BaseItem
from scrapy.contrib.loader import ItemLoader
class FlexibleItem(dict, BaseItem):
pass
if __name__ == '__main__':
item = FlexibleItem()
loader = ItemLoader(item)
loader.add_value('foo', 'bar')
loader.add_value('baz', 123)
loader.add_value('baz', 'test')
loader.add_value(None, {'abc': 'xyz', 'foo': 555})
print loader.load_item()
if 'meow' not in item:
print "it's not a cat!"
<强>结果:强>
{'foo': ['bar', 555], 'baz': [123, 'test'], 'abc': ['xyz']} it's not a cat!
好的,我找到了解决方案。它有点&#34; hack&#34;但它有效..
Scrapy项将字段名称存储在名为fields
的字典中。向项目添加数据时,它会检查该字段是否存在,以及它是否存在抛出错误:
def __setitem__(self, key, value):
if key in self.fields:
self._values[key] = value
else:
raise KeyError("%s does not support field: %s" %\
(self.__class__.__name__, key))
您可以做的是覆盖此__setitem__
函数,使其不那么严格:
class FlexItem(Item):
def __setitem__(self, key, value):
if key not in self.fields:
self.fields[key] = Field()
self._values[key] = value
然后你去。
现在,当您向项目添加数据时,如果项目没有定义该字段,则会添加该字段,然后正常添加数据。
答案 1 :(得分:4)
此解决方案适用于导出程序(scrapy crawl -t json -o output.json
):
import scrapy
class FlexibleItem(scrapy.Item):
def __setitem__(self, key, value):
if key not in self.fields:
self.fields[key] = scrapy.Field()
super(FlexibleItem, self).__setitem__(key, value)
编辑更新以使用最新的Scrapy
答案 2 :(得分:2)
这适用于版本0.24,并且还允许项目使用项目加载器:
import scrapy
from collections import defaultdict
class FlexibleItem(scrapy.Item):
fields = defaultdict(scrapy.Field)
def __setitem__(self, key, value):
# all keys are supported
self._values[key] = value
答案 3 :(得分:1)
我知道我的答案很晚,但对于那些仍然需要使用Scrapy的动态项目(当前版本为1)的人,我在Github上创建了一个包含示例的存储库。
你去吧