scrapy 中的 ItemLoader
優(yōu)點(diǎn)
ItemLoader最大的好處是作為一個(gè)容器,可以多個(gè)spider復(fù)用提取規(guī)則。
可以把規(guī)則動(dòng)態(tài)添加,因?yàn)橐?guī)則可以放入數(shù)據(jù)庫(kù)或者文件中。
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 - 帶了空格的文字內(nèi)容,都要進(jìn)行正則表達(dá)式處理。
解決方法: input_processor=MapCompose
在items.py中解決。
值傳入時(shí),進(jìn)行預(yù)處理。
from scrapy.loader.processors import MapCompose
可以在item傳入值預(yù)處理的時(shí)候,連續(xù)調(diào)用兩個(gè)函數(shù)進(jìn)行處理。
例子:string拼接
只調(diào)用一個(gè)匿名函數(shù),就在title后面加上了-jobbole。
class JobBoleArticleItem(scrapy.Item):
title = scrapy.Field(
# input_processor = MapCompose(add_jobbole)
input_processor = MapCompose(lambda x:x+"-jobble")
調(diào)用兩個(gè)函數(shù)的例子。同時(shí)加上了-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轉(zhuǎn)化成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)
)
數(shù)組中提取值:TakeFirst
from scrapy.loader.processors import TakeFirst
class JobBoleArticleItem(scrapy.Item):
create_date = scrapy.Field(
input_processor = MapCompose(date_convert),
output_processor = TakeFirst()
)
自定義 ItemLoader
時(shí)間一次性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中實(shí)例化的時(shí)候,使用ArticleItemLoader。
item_loader = ArticleItemLoader(item=JobBoleArticleItem(), response=response)
正則提取三個(gè)item中的數(shù)字
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),
)
最后得到三個(gè)變量為數(shù)字。
對(duì) tags 進(jìn)行 join
from scrapy.loader.processors import Join
class JobBoleArticleItem(scrapy.Item):
#...
tags = scrapy.Field(
output_processor=Join(",")
)
tag中的評(píng)論幾個(gè)字要去掉
def remove_comment_tags(value):
# 去掉 tags 中提取的評(píng)論
if "評(píng)論" 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()
同時(shí)保持原來的值。原來就是一個(gè)list,要原封不動(dòng)的傳遞出去。就不要讓default的output_processor 去 TakeFirst()。
但是sql中要取出 list 中的值。
另外在pipeline中,要對(duì)item中是否有 front_image_url進(jìn)行判斷。
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