Scrapy的Item_loader机制详解
一、ItemLoader与Item的区别
ItemLoader是负责数据的收集、处理、填充,item仅仅是承载了数据本身- 数据的收集、处理、填充归功于
item loader中两个重要组件:
- 输入处理
input processors - 输出处理
output processors
- 输入处理
二、ItemLoader的使用
- 1、创建一个项目并创建一个爬虫
2、在
item.py中使用
import redis
import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst, Join
from w3lib.html import remove_tags from utils.common import extract_num def add_jobbole(value):
return value + 'zhangyafei' def date_convert(value):
try:
value = value.strip().replace('·', '').strip()
create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
except Exception as e:
create_date = datetime.datetime.now().date
return create_date def get_nums(value):
try:
if re.match('.*?(\d+).*', value).group(1):
nums = int(re.match('.*?(\d+).*', value).group(1))
else:
nums = 0
except:
nums = 0
return nums def remove_comment_tags(value):
if "评论" in value:
return ''
return value def return_value(value):
return value def gen_suggests(index, info_tuple):
# 根据字符串生成搜索建议数组
used_words = set()
suggests = []
for text, weight in info_tuple:
if text:
# 调用es的analyze接口分析字符串
words = es.indices.analyze(index=index, analyzer="ik_max_word", params={'filter': ["lowercase"]}, body=text)
anylyzed_words = set([r["token"] for r in words["tokens"] if len(r["token"]) > 1])
new_words = anylyzed_words - used_words
else:
new_words = set() if new_words:
suggests.append({"input": list(new_words), "weight": weight}) return suggests class ArticleItemLoader(ItemLoader):
# 自定义itemloader
default_output_processor = TakeFirst() class JobboleArticleItem(scrapy.Item):
title = scrapy.Field()
create_date = scrapy.Field(
input_processor=MapCompose(date_convert),
)
url = scrapy.Field()
url_object_id = scrapy.Field()
front_image_url = scrapy.Field(
output_processor=MapCompose(return_value)
)
front_image_path = scrapy.Field()
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 = scrapy.Field(
input_processor=MapCompose(remove_comment_tags),
output_processor=Join(",")
)
content = scrapy.Field() def get_insert_sql(self):
insert_sql = """
insert into jobbole_article(title, url, create_date, fav_nums)
VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE content=VALUES(fav_nums)
"""
params = (self["title"], self["url"], self["create_date"], self["fav_nums"]) return insert_sql, params def save_to_es(self):
article = ArticleType()
article.title = self['title']
article.create_date = self["create_date"]
article.content = remove_tags(self["content"])
article.front_image_url = self["front_image_url"]
if "front_image_path" in self:
article.front_image_path = self["front_image_path"]
article.praise_nums = self["praise_nums"]
article.fav_nums = self["fav_nums"]
article.comment_nums = self["comment_nums"]
article.url = self["url"]
article.tags = self["tags"]
article.meta.id = self["url_object_id"] article.suggest = gen_suggests(ArticleType._doc_type.index, ((article.title, 10), (article.tags, 7))) article.save() redis_cli.incr("jobbole_count") return
spider中的使用
def parse(self, response):
"""
1.获取文章列表页的文章url交给scrapy下载后并进行解析
2.获取下一页的url交给scrapy进行下载,下载完成后交给parse解析
"""
"""
解析文章列表页中的所有文章url交给scrapy下载并进行解析
"""
if response.status == 404:
self.fail_urls.append(response.url)
self.crawler.stats.inc_value("failed_urls") post_nodes = response.css('#archive .post-thumb a')
for post_node in post_nodes:
img_url = post_node.css('img::attr(src)').extract_first()
# img_url = [img_url if 'http:' in img_url else ('http:' + img_url)]
post_url = post_node.css('::attr(href)').extract_first()
yield scrapy.Request(url=parse.urljoin(response.url, post_url), meta={'img_url': img_url},
callback=self.parse_detail)
next_url = response.css('.next.page-numbers::attr(href)').extract_first()
# 获取下一页的url交给scrapy下载并进行解析
if next_url:
yield scrapy.Request(url=next_url, callback=self.parse) def parse_detail(self, response):
# 通过item loader加载item
front_image_url = response.meta.get("front_image_url", "") # 文章封面图
item_loader = ArticleItemLoader(item=JobboleArticleItem(), response=response)
item_loader.add_css("title", ".entry-header h1::text")
item_loader.add_value("url", response.url)
item_loader.add_value("url_object_id", get_md5(response.url))
item_loader.add_css("create_date", "p.entry-meta-hide-on-mobile::text")
item_loader.add_value("front_image_url", [front_image_url])
item_loader.add_css("praise_nums", ".vote-post-up h10::text")
item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text")
item_loader.add_css("fav_nums", ".bookmark-btn::text")
item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")
item_loader.add_css("content", "div.entry") article_item = item_loader.load_item() yield article_item
三、常见的内置处理器
1、
Identity不对数据进行处理,直接返回原来的数据
2、
TakeFirst返回第一个非空值,常用于单值字段的输出处理
3、
Join相当于把列表中的元素拼接起来
- 4、
MapCompose把几个方法组合起来
四、数据清洗方法详解
processor
scrapy提供了一个processors类,里面有下列几种方法:Join,TakeFirst,MapCompose,Compose,Identity,SelectJmes
对这几种方法的用法简单介绍一下:
from scrapy.loader.processors import Join,TakeFirst,MapCompose,Compose,Identity,SelectJmes #以特定字符连接,示例以空连接,对字符串也能操作
c = Join('')
c(['a','b'])
>>>'ab'
#******************** #传入函数的列表的每一个元素都会经过第一个函数,
#得到值在经过第二个函数,如果有返回值为None的,则抛弃,
#最后返回一个列表
c=MapCompose(str.strip,str.upper)
c([' a ','b'])
>>>['A', 'B']
#******************** #如果传入一个列表时则会报下面这个错误
#descriptor 'strip' requires a 'str' object but received a 'list'
#但如果Compose的第一个函数是取列表的第一个元素,不会报错
#即Compose是处理单一数据,MapCompose是批量处理
c=Compose(str.strip,str.upper)
c(' ac ')
>>>'AC'
#******************** #拿到JSON格式数据时会有作用
proc = SelectJmes('a')
proc({'a':'b','c':'d'})
>>>'b'
input--output
Item Loader 为每个 Item Field 单独提供了一个 Input processor 和一个 Output processor;
Input processor 一旦它通过 add_xpath(),add_css(),add_value() 方法收到提取到的数据便会执行,执行以后所得到的数据将仍然保存在 ItemLoader 实例中;当数据收集完成以后,ItemLoader 通过 load_item() 方法来进行填充并返回已填充的 Item 实例。
即input_processor是在收集数据的过程中所做的处理,output_processor是数据yield之后进行的处理,通过下面这个例子会更加理解:
#type字段取出来时是'type': ['2室2厅', '中楼层/共6层'] #定义一个在第一个元素后面加a的函数
def adda(value):
return value[0]+'a' type = scrapy.Field(output_processor = Compose(adda))
>>>'type': '2室2厅a' type = scrapy.Field(input_processor = Compose(adda))
>>>'type': ['2室2厅a', '中楼层/共6层a']
#如果使用MapCompose的话,两个结果会一样,这也是Compose和MapCompose的区别
当指定了取列表的第一个元素后,有些信息想保留整个列表便可以使用name_out,Identity()是取自身的函数。
class TeItem(ItemLoader):
default_out_processor = TakeFirst()
name_out = Identity()
也可以在基于scrapy.Item的item中定义一些规则:
class Scrapy1Item(scrapy.Item):
name = scrapy.Field(output_processor=Identity())
优先级
scrapy提供了很多种方式去自定义输入输出的内容,具有一定的优先级,优先级最高的是name_out这种,其次是在scrapy.Field()中定义的output_processor和input_processor,最后是default_out_processor = TakeFirst()这种。
Scrapy的Item_loader机制详解的更多相关文章
- 从mixin到new和prototype:Javascript原型机制详解
从mixin到new和prototype:Javascript原型机制详解 这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...
- 浏览器 HTTP 协议缓存机制详解
最近在准备优化日志请求时遇到了一些令人疑惑的问题,比如为什么响应头里出现了两个 cache control.为什么明明设置了 no cache 却还是发请求,为什么多次访问时有时请求里带了 etag, ...
- JVM的垃圾回收机制详解和调优
JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...
- ThreadPoolExecutor运转机制详解
ThreadPoolExecutor运转机制详解 - 走向架构师之路 - 博客频道 - CSDN.NET 最近发现几起对ThreadPoolExecutor的误用,其中包括自己,发现都是因为没有仔细看 ...
- Linux 内存机制详解宝典
Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...
- PHP的垃圾回收机制详解
原文:PHP的垃圾回收机制详解 最近由于使用php编写了一个脚本,模拟实现了一个守护进程,因此需要深入理解php中的垃圾回收机制.本文参考了PHP手册. 在理解PHP垃圾回收机制(GC)之前,先了解一 ...
- Java 反射 设计模式 动态代理机制详解 [ 转载 ]
Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...
- Android事件分发机制详解
事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...
- Android Binder机制详解:手写IPC通信
想要掌握一样东西,最好的方式就是阅读理解它的源码.想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码.本文的思路也是来自于此. 简介 Binder是Andro ...
随机推荐
- 论文翻译:2021_A Perceptually Motivated Approach for Low-complexity, Real-time Enhancement of Fullband Speech
论文地址:一种低复杂度实时增强全频带语音的感知激励方法 论文代码 引用格式:A Perceptually Motivated Approach for Low-complexity, Real-tim ...
- Mysql脚本 生成测试数据
使用: ./xie.sh -uroot -p'123456' #!/bin/bash #混合测试数据库脚本 #将创建一个single数据库,其中创建一个s1表 #如果数据库存在,将会写入数据,可以在写 ...
- Gitlab用户在组中有五种权限
Gitlab用户在组中有五种权限:Guest.Reporter.Developer.Master.Owner Guest:可以创建issue.发表评论,不能读写版本库 Reporter:可以克隆代码, ...
- Three.js 实现3D全景侦探小游戏🕵️
背景 你是嘿嘿嘿侦探社实习侦探️,接到上级指派任务,到甄开心小镇调查市民甄不戳宝石失窃案,根据线人流浪汉老石提供的线索,小偷就躲在小镇,快把他找出来,帮甄不戳寻回失窃的宝石吧! 本文使用 Three ...
- 【web】sqli-labs学习
第一页 1~4预备知识(基于错误的注入) 几个常用函数: 1. version()--MySQL 版本 2. user()--数据库用户名 3. database()--数据库名 4. @@dat ...
- HSPICE 电平触发D触发器仿真
一. HSPICE的基本操作过程 打开HSPICE程序,通过OPEN打开编写好的网表文件. 按下SIMULATE进行网表文件的仿真. 按下AVANWAVES查看波形图(仿真结果). 二. 网表文件结构 ...
- java 多线程:Callable接口;FutureTask类实现对象【Thread、Runnable、Callable三种方式实现多线程的区别】
Callable接口介绍: Java5开始,Java提供了Callable接口,像是Runnable接口的增强版,Callable接口提供了一个 call()方法可以作为线执行体. call()方法比 ...
- 云小课|DGC数据开发之基础入门篇
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:欢迎来到DGC数据 ...
- c++设计模式概述之组合(composite)
代码写的不够规范,目的是为了缩短代码篇幅, 实际中请不要这样做 1.概述 这里的组合,是将 部分组合到整体.所以, 用到的对象有: 部分.整体. 这里的例子,生活中可以类比厨房的筷筒: 里面放了筷子, ...
- 【LeetCode】1161. Maximum Level Sum of a Binary Tree 解题报告 (C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS 日期 题目地址:https://leetcod ...