Scrapy 里的 item 可以是 dict、scrapy.Item、dataclass、attrs。它们在字段检查、元数据、导出、速度和内存上差在哪里?ItemAdapter 又怎样把它们统一起来?这篇逐项量。
类型 is_item field_names() asdict() get("price") get("zzz", "默认")
dict True ['name', 'price'] {'name': 'a', 'price': 1.5} 1.5 默认
scrapy.Item True ['name', 'price'] {'name': 'a', 'price': 1.5} 1.5 默认
dataclass True ['name', 'price', 'extra'] {'name': 'a', 'price': 1.5, 'extra': []} 1.5 默认
attrs True ['name', 'price'] {'name': 'a', 'price': 1.5} 1.5 默认
字段元数据 get_field_meta("price"):scrapy.Item={'serializer': <function SItem.<lambda> at 0x…>}; dataclass={}; attrs={'serializer': <class 'str'>}
不是 item 的东西:is_item(42)= False is_item("s")= False is_item([1])= False is_item(object())= FalseItemAdapter 给 dict、scrapy.Item、dataclass、attrs 提供相同的读写、asdict()、字段元数据接口,pipeline、导出器都通过它处理 item。
scrapy.Item 赋值未声明字段 → 抛 KeyError: 'SItem does not support field: zzz'
scrapy.Item item["zzz"] = 1 → 抛 KeyError: 'SItem does not support field: zzz'
scrapy.Item 读未赋值的字段 item["name"] → 抛 KeyError: 'name'
scrapy.Item item.get("name") → 通过,结果 None
dataclass 传未声明字段 DItem(zzz=1) → 抛 TypeError: DItem.__init__() got an unexpected keyword argument 'zzz'
dataclass 给 name 赋整数(类型标注 str) → 通过,结果 DItem(name=123)
dict 随便加字段 → 通过,结果 None
ItemAdapter(SItem())["zzz"] = 1 → 抛 KeyError: 'SItem does not support field: zzz'scrapy.Item 不允许未声明的字段(KeyError);dataclass 不允许未声明的构造参数但不检查类型;dict 随意。
o.jsonl:
{"name": "字典", "price": 1.5}
{"name": "Item", "price": "¥2.5"}
{"name": "dataclass", "price": 3.5, "extra": ["x"]}
{"name": "attrs", "price": 4.5}
o.csv:
name,price
字典,1.5
Item,¥2.5
dataclass,3.5
attrs,4.5
item_scraped_count=4(四个类型都被接受)
说明:csv 表头取自第一个 item(字典)的键 name,price;scrapy.Item 的 serializer 在 jsonlines 里生效(price 变成 “¥2.5”),dataclass 的 extra 字段在 csv 里被丢掉(列头里没有)FEEDS 对四种类型都能导出;scrapy.Item 的 Field(serializer=…) 会在导出时使用;csv 的列由第一个 item 决定。
dict 1.3 ms / 万次 1.0× dict
scrapy.Item 17.8 ms / 万次 13.5× dict
dataclass 3.0 ms / 万次 2.3× dict
dataclass(slots=True) 2.4 ms / 万次 1.8× dict
attrs 2.6 ms / 万次 2.0× dict
attrs(slots=True) 2.5 ms / 万次 1.9× dict
每个对象占的内存(tracemalloc,10 万个实例的平均字节):
dict 192 字节/个
scrapy.Item 436 字节/个
dataclass 104 字节/个
dataclass(slots=True) 64 字节/个
attrs 104 字节/个
attrs(slots=True) 80 字节/个scrapy.Item 的创建和内存开销明显更大;大量 item 时 dataclass / attrs(尤其 slots=True)更省。