scrapy 中的 ItemLoader
优点
ItemLoader最大的好处是作为一个容器,可以多个spider复用提取规则。
可以把规则动态添加,因为规则可以放入数据库或者文件中。
ItemLoader不用考虑是否为空,是否是0的值。
初步
在spider中
from scrapy.loader import ItemLoader
# 通过 ItemLoader 加载 item
item_loader = ItemLoader(item=JobBoleArticleItem(), response=response)
item_loader.add_css("title", ".entry-header h1::text")
# item_loader.add_xpath()
item_loader.add_value("url", response.url)
item_loader.add_value("front_image_url", get_md(response.url))
item_loader.add_css("create_date", ".entry-meta-hide-on-mobile::text")
item_loader.add_value("front_image_url", [front_image_url])
item_loader.add_css("praise_nums", "div.post-adds h10::text")
item_loader.add_css("fav_nums", ".bookmark-btn::text")
item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text")
item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")
item_loader.add_css("content", ".entry")
article_item = item_loader.load_item()
问题:
-
article_item
里面的_value
的值都是list - 带了空格的文字内容,都要进行正则表达式处理。
解决方法: input_processor=MapCompose
在items.py中解决。
值传入时,进行预处理。
from scrapy.loader.processors import MapCompose
可以在item传入值预处理的时候,连续调用两个函数进行处理。
例子:string拼接
只调用一个匿名函数,就在title后面加上了-jobbole
。
class JobBoleArticleItem(scrapy.Item):
title = scrapy.Field(
# input_processor = MapCompose(add_jobbole)
input_processor = MapCompose(lambda x:x+"-jobble")
调用两个函数的例子。同时加上了-jobbole
和-bobby
。
def add_jobbole(value):
return value + "-bobby"
class JobBoleArticleItem(scrapy.Item):
title = scrapy.Field(
# input_processor = MapCompose(add_jobbole)
input_processor = MapCompose(lambda x:x+"-jobble", add_jobbole)
)
例子:string转化成date
def date_convert(value):
try:
create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
except Exception as e:
create_date = datetime.datetime.now().date()
return create_date
class JobBoleArticleItem(scrapy.Item):
create_date = scrapy.Field(
input_processor = MapCompose(date_convert)
)
数组中提取值:TakeFirst
from scrapy.loader.processors import TakeFirst
class JobBoleArticleItem(scrapy.Item):
create_date = scrapy.Field(
input_processor = MapCompose(date_convert),
output_processor = TakeFirst()
)
自定义 ItemLoader
时间一次性TakeFirst
。
from scrapy.loader import ItemLoader
继承ItemLoader,把原来的default_output_processor
替换掉
class ArticleItemLoader(ItemLoader):
# 自定义 itemloader
default_output_processor = TakeFirst()
把TakeFirst()
删除掉。
class JobBoleArticleItem(scrapy.Item):
title = scrapy.Field(
# input_processor = MapCompose(add_jobbole)
input_processor = MapCompose(lambda x:x+"-jobble", add_jobbole)
)
create_date = scrapy.Field(
input_processor = MapCompose(date_convert),
# output_processor = TakeFirst()
)
在spider中,
from ArticleSpider.items import JobBoleArticleItem, ArticleItemLoader
在spider中实例化的时候,使用ArticleItemLoader
。
item_loader = ArticleItemLoader(item=JobBoleArticleItem(), response=response)
正则提取三个item中的数字
import re
def get_nums(value):
match_re = re.match(".*?(\d+).*", value)
if match_re:
nums = int(match_re.group(1))
else:
nums = 0
return nums
class JobBoleArticleItem(scrapy.Item):
#...
praise_nums = scrapy.Field(
input_processor=MapCompose(get_nums),
)
comment_nums = scrapy.Field(
input_processor=MapCompose(get_nums),
)
fav_nums = scrapy.Field(
input_processor=MapCompose(get_nums),
)
最后得到三个变量为数字。
对 tags 进行 join
from scrapy.loader.processors import Join
class JobBoleArticleItem(scrapy.Item):
#...
tags = scrapy.Field(
output_processor=Join(",")
)
tag中的评论几个字要去掉
def remove_comment_tags(value):
# 去掉 tags 中提取的评论
if "评论" in value:
return ""
else:
return value
class JobBoleArticleItem(scrapy.Item):
tags = scrapy.Field(
input_processor = MapCompose(remove_comment_tags),
output_processor=Join(",")
)
front-image-url 变成 list
def return_value(value):
return value
front_image_url = scrapy.Field(
output_processor=MapCompose(return_value)
)
可以覆盖掉原来的
default_output_processor = TakeFirst()
同时保持原来的值。原来就是一个list,要原封不动的传递出去。就不要让default的output_processor 去 TakeFirst()。
但是sql中要取出 list 中的值。
另外在pipeline中,要对item中是否有 front_image_url进行判断。
class ArticleImagePipeline(ImagesPipeline):
def item_completed(self, results, item, info):
if "front_image_url" in item:
for ok, value in results:
image_file_path = value["path"]
item["front_image_path"] = image_file_path
return item