在不明确定义每个字段的情况下抓取数据
我想用Python的Scrapy库抓取一页数据,但不想一个个定义页面上的每个字段。相反,我想根据元素的id
来动态生成字段名。
一开始我觉得最好的办法是有一个处理流程,先收集所有数据,然后等收集齐了再输出。
但后来我意识到,我需要把数据传递给这个处理流程,而传递数据时需要用到一个项目(item),可是我不知道这个项目需要哪些字段,没法定义它!
那么,我该如何解决这个问题呢?
4 个回答
3
这个在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
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版本可以使用了
17
更新:
之前的方法在使用项目加载器时不太好用,还让事情变得复杂。这里有个更好的方法来实现灵活的项目:
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!
旧方案:
好吧,我找到了一种解决方案。这有点像“黑科技”,但确实有效。
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
这样就可以了。
现在,当你往一个项目里添加数据时,如果这个项目没有定义那个字段,它会被添加进去,然后数据就会像往常一样添加。