Scrapy(五):Response与Request、数据提取、Selector、Pipeline
学习自Requests and Responses — Scrapy 2.5.0 documentation
Request在Spider中生成,被Downloader执行,之后会得到网页的Response
1、Request
1)构造
scrapy.http.Request(*args,**kw)
2)构造时传入参数
参数 | 说明 | 补充 |
url | ||
callback | 对该URL的返回页面进行处理的回调函数;当该项未指定时,则默认用parse()方法 | |
method | HTTP请求方法,默认'GET' | GET、POST、PUT |
meta | 一些元数据 | |
body | 请求体 | |
headers | 请求头 | |
cookies | ||
encoding | 请求的编码方式 | |
priority | 请求优先度 | |
dont_filter | 标识该请求不被Scheduler过滤;当我们要经常用到某个request时用到 | |
errback | 异常处理方法;包括网页404错误 | |
flags | ||
cb_kwargs |
Dict;标识传入参数 return response.follow(url,self.parse_additional_page,cb_kwargs=dict(item=item) |
补充说明:
1)Request回调函数的作用时机是该request对象对应的response被下载之后,该回调函数将用该Response作为其第一个参数。举例如下:
def parse_page1(self, response):
return scrapy.Request("http://www.example.com/some_page.html",
callback=self.parse_page2) def parse_page2(self, response):
# this would log http://www.example.com/some_page.html
self.logger.info("Visited %s", response.url)
2)一些情况下我们想向回调函数传递参数,可以通过Request.cb_kwargs属性实现。举例如下:
def parse(self,response):
request = scrapy.Request(
url,
callback=self.parse_page,
cb_kwargs=dict(main_url=response.url)
)
# 为回调函数传入额外参数
request.cb_kwars['foo']='bar'
yield request #传入参数可直接写在回调函数的参数中,
#而不用通过response.xxx访问
def parse_pages(self,response,main_url,foo):
yield dict(
main_url=main_url,
ther_url=response.url,
foo=foo
)
3)使用errback处理异常情况
errback是构造Request时定义的一个异常处理函数,当有异常发生时,会调用该方法。它接收Failure作为第一个参数,可以用于追踪超时、DNS错误等。
2、Response
1)属性、方法
属性 | 类型 | 说明 |
url | ||
status | int | 响应状态码;默认200 |
headers | Dict | 响应头部 |
body | bytes | 响应正文 |
text |
str |
文本形式的响应正文 response.text = response.body.decode(response.encoding) |
encoding | str | 响应正文的编码 |
request | Request | 产生该响应的Request对象 |
meta | 与该Response对应的Request的meta属性;在构造Request对象时,可以将传递给响应处理函函数的信息通过meta参数传入;响应处理函数处理相应时,通过response.meta将信息提取出来 | |
cb_kwargs | 传入参数;与Request中的cb_kwargs相对应,通过Response.cb_kwargs提取出来 | |
selector | Selector对象用于在Response中提取数据 |
方法 | 说明 |
xpath(query) | 根据XPath路径表达式提取要素 |
css(query) | 根据CSS语法提取要素 |
urljoin(url) | 用于构造绝对url,当传入的url是一个相对地址时,根据response.url计算出相应的绝对url |
follow | 返回一个Request实例,接收与Request.__init__方法相同的参数。 |
follow_all | 返回一个Request Iterable,接收与__init__方法相同的参数。 |
部分方法的详细说明:
follow:
follow(
url, callback, mehod='GET', headers, body,
cookies, meta, encoding='utf-8', priority=0,
dont_filter=False, errback, cb_kwargs, flags
)
返回url对应的Request实例。参数写法与Request对象构建时相同,只是url可以是相对URL,而非绝对URL。
follow_all:
follow_all(
urls, callback, method='GET', headers, body,
cookies, meta, encoding='utf-8', priority=0,
dont_filter=False, errback, cb_kwargs, flags
)
返回Iterable Request,这些Requests与urls相关联。其他方面与follow方法相同。
3、用一个Spider程序来说明Request与Response
import scrapy class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self,response)
page=response.url.split("/")[-2]
filename=f'quotes-{page}.html'
with open(filename,'wb') as f:
f.write(response.body)
self.log(f'Saved file {filename}')
①属性与方法
name:Spider的名字,每个Spider的名字不能相同;
start_requests:必须返回Request()的迭代器(可以是一个Requests List或yield Request())。这些Requests是针对爬虫起始URL进行的;后续的Requests将从这些初始URL中生成。
parse:对之前所有的Requests的Response进行处理,参数中的response保存了页内容并且有一系列方法来处理它。parse方法中一般将爬取到的数据保存为Dict并会自行寻找下一个新的URL(当然这也需要用yield Request(url)进行说明)。
上文的Parse中提取到的网页内容,保存在response.body中,如果要保存为二进制文件,直接保存该项就行了,如果要保存为文本文件,用response.text。
②补充
如果不想写start_requests方法,只需要设置start_urls属性,就可以不用写之前的方法,而对start_urls中的初始URL调用默认的start_requests来对这些URL进行处理。
2、数据提取
最好的提取数据方法是在scrapy shell中进行。
①以http://quotes.toscrape.com/page/1/为例,运行以下指令打开Shell
scrapy shell https://scrapy.org
②之后就可以用response的各项属性和方法进行数据提取了
response.xpath('//title/text()')
提取结果形式为:[ <Selector xpath='xpath表达式' data='提取到的信息' ]
[<Selector xpath='//title/text()' data='Scrapy | A Fast and Powerful Scraping...'>]
如果要提取其中的数据部分,可以用方法extract或getall(提取全部)、get或extract_first(提取首项),当有多个提取项时,extract、getall方法的提取结果是结果List,需要用切片[]的方式提取;也可以用join方法把所有数据连接成为一个str。
③使用正则表达式进行数据提取:re方法
用法:response.xpath('xpath表达式').re(r'正则表达式')
说明:用正则表达式对提取到的要素进行数据再提取,规则与之前正则表达式那一节所说相同,不过re方法,应该是相当于re模块下的search方法,区别在于不需要用group方法,这里re直接返回的结果就是匹配结果,如果正则表达式中有括号,则匹配结果List中保存有原文中符合匹配规则的项
In [8]: response.xpath('//title/text()').re(r'(S\w+)')
Out[8]: ['Scrapy', 'Scraping']
④用yield关键字实现自动爬取
将需要保存的内容,写为Dict形式,用yield标记,即可不停地提取并保存数据:
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
⑤存储爬取数据
scrapy crawl 爬虫名 -o 文件 #追加写
scrapy crawl 爬虫名 -0 文件 #覆盖写 大写字母而非数字
如果要爬取的内容较多,或者更为复杂的存储类型,可以通过在pipelines.py写相应的数据存储语句。
⑥自动翻页功能:response.urlopen(next_page)
该方法只用于静态有规律的URL翻页功能。
next_page = response.urljoin(next_page)
这句话的作用是,将next_page连接到原URL上,构成新的URLnext_page;这样,利用for循环、if语句、yield语句的综合,可以实现不断爬取下一页内容的功能:
#以下代码均写在parse方法中的数据提取语句之后 for i in range(4): #假设提取前3页
next_page=response.urljoin('pw='+str(i))#得到next_page的URL
yield scrapy.Request(next_page,callback=self.parse)
⑦使用参数
方法:在运行爬虫的指定中加入-a后缀
scrapy crawl quotes -0 quotes-humor.json -a tag=humor
这些参数将传入爬虫的__init__方法中,并成为爬虫的默认参数,访问时通过self.tag在__init__中使用。
4、Selector
Selector通过XPath和CSS表达式筛选网页要素
Selector对象是response对象调用XPath与CSS方法后得到的,比如:
scrapy shell https://docs.scrapy.org/en/latest/_static/selectors-sample1.html
In [1]: response.xpath('//title/text()')
Out[1]: [<Selector xpath='//title/text()' data='Example website'>]
如果从Selector中将data提取出来,方法有很多,这里不再多说,见本文第二部分
5、Pipeline
①简介
Item被爬取之后,将被送入Pipeline进行进一步的处理。
每一个Pipeline都是一个class,这个class继承了方法process_item,该方法接收item作为参数,在该方法中对该Item进行一系列处理。
典型的处理方法有:对HTML数据的清洗、检查爬取数据的正确性、检查重复项、存储数据到数据库中。
②类方法
process_item:每个pipeline都必须实现的方法,在其中完成对item的处理
该方法必须返回1)一个Item对象;2)抛出一个DropItem异常;当抛出该异常后,对应的Item将不再会被处理。
open_spider:当Spider开始运行时调用该方法
close_spider:当Spider结束运行时调用该方法
③例子
1)保存提取到了Price的Item,修改其Price,放弃那些不含Price的Item:
from itemadapter import ItemAdapter
from scrapy.exceptions import DropItem
class PricePipeline:
def process_item(self,item,spider):
adapter = ItemAdapter(item)
if adapter.get('price'):
adapter['price'] = adapter['price'] *2
return item
else:
raise DropItem(f'Miss price in {item}')
2)把数据保存到JSON文件中
import json
from itemadapter import ItemAdapter
class JsonWriterPipeline:
def open_spider(self,spider):
self.file=open('items.jl','w')
def close_spider(self,spider):
self.file.close()
def process_item(self,item,spider):
line=json.dumps(dict(item),ensure_ascii=Flase)+'\n'
self.file.write(line)
return item
④激活Pipeline
需要在Setting中设置ITEM_PIPELINES项,才能正确激活Pipeline
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
⑤补充
在没有较多规则限制的情况下,可以在输出时直接输出为json文件,通过-o或-O实现;
Scrapy(五):Response与Request、数据提取、Selector、Pipeline的更多相关文章
- Scrapy中response属性以及内容提取
一.属性 url :HTTP响应的url地址,str类型 status:HTTP响应的状态码, int类型 headers :HTTP响应的头部, 类字典类型, 可以调用get或者getlist方法对 ...
- Scrapy 学习笔记(一)数据提取
Scrapy 中常用的数据提取方式有三种:Css 选择器.XPath.正则表达式. Css 选择器 Web 中的 Css 选择器,本来是用于实现在特定 DOM 元素上应用花括号内的样式这样一个功能的. ...
- scrapy 的response 的相关属性
Scrapy中response介绍.属性以及内容提取 解析response parse()方法的参数 response 是start_urls里面的链接爬取后的结果.所以在parse()方法中,我 ...
- Scrapy学习篇(六)之Selector选择器
当我们取得了网页的response之后,最关键的就是如何从繁杂的网页中把我们需要的数据提取出来,python从网页中提取数据的包很多,常用的有下面的几个: BeautifulSoup它基于HTML代码 ...
- scrapy获取当当网中数据
yield 1. 带有 yield 的函数不再是一个普通函数,而是一个生成器generator,可用于迭代 2. yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回yiel ...
- iOS五种本地缓存数据方式
iOS五种本地缓存数据方式 iOS本地缓存数据方式有五种:前言 1.直接写文件方式:可以存储的对象有NSString.NSArray.NSDictionary.NSData.NSNumber,数据 ...
- java web 中有效解决中文乱码问题-pageEncoding与charset区别, response和request的setCharacterEncoding 区别
这里先写几个大家容易搞混的编码设置代码: 在jsp代码中的头部往往有这两行代码 pageEncoding是jsp文件本身的编码contentType的charset是指服务器发送给客户端时的内容编码J ...
- python 爬虫与数据可视化--数据提取与存储
一.爬虫的定义.爬虫的分类(通用爬虫.聚焦爬虫).爬虫应用场景.爬虫工作原理(最后会发一个完整爬虫代码) 二.http.https的介绍.url的形式.请求方法.响应状态码 url的形式: 请求头: ...
- Python爬虫框架Scrapy实例(三)数据存储到MongoDB
Python爬虫框架Scrapy实例(三)数据存储到MongoDB任务目标:爬取豆瓣电影top250,将数据存储到MongoDB中. items.py文件复制代码# -*- coding: utf-8 ...
随机推荐
- List<Integer>里有可能存String类型元素吗?
这其实是我遇到的一个线上bug,在这里分享给大家. 如果是用反射,那就很简单了,毕竟泛型只是在编译期进行约束,对运行期是无能为力的. 想想看,如果不使用反射,有没有办法做到呢? 问题起因 在我们公司的 ...
- java解洛谷P1003铺地毯问题
此题给出的最大地毯数量为10000,创建[10001][4]长度的二维数组 以稀松数组的方法,[第i个地毯]的 [0][1][2][3]分别保存地毯的坐标和大小 再用需要求的坐标比较即可 public ...
- 如何保存并复制python虚拟环境
关于虚拟环境的一些基础概念学习了本期视频 保存 以我的一个虚拟环境示例: 在要保存的虚拟环境下使用: pip freeze > requirements.txt 复制 pip install - ...
- Vue.js项目的兼容性与部署配置
一.处理兼容性问题的相关插件: 1> 解决移动端某些版本的浏览器,点击事件有3s延时触发的问题 · 安装 fastclick 依赖包:npm install fastclick --save-d ...
- shiro 框架之 加密处理。
一.shiro 加密? /* Shiro? 一.为什么要加密? 为调高数据库的安全性,需要给密码加密. 二.常见的加密算法? 1.1哈希算法 md5:加密算法 哈希函数 1.2.对称算法 1.3.非对 ...
- 使用XmlWriter写入XML
麻了..整理完了发现XmlWriter不能添加元素,只能重写,还是得用Xdocument..好像DOM能实现添加元素 点击查看代码 **MemoryStream msXml = new MemoryS ...
- C# 将OFD转为PDF
OFD格式的文档是一种我国独有的国家标准版式的文档,在不同场景需求中,可以通过格式转换的方法将PDF转为OFD,或者将OFD转为PDF.本次内容,将通过C#程序介绍如何实现由OFD到PDF的转换,并附 ...
- 分布式事务框架-Litx补偿事务框架源码解析
前言 之前某段时间在研究分布式事务过程中,对实现原理比较好奇,于是去Gitee上找了几个人气比较高的框架进行学习,其中印象深刻的有Litx,因为Litx源码不多,且都是基于Spring和Dubbo底层 ...
- JVM学习十二 - (复习)JVM内存结构
JVM 内存结构 Java 虚拟机的内存空间分为 5 个部分: 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 JDK 1.8 同 JDK 1.7 比,最大的差别就是:元数据区取代了永久代.元 ...
- VC 获取已系统安装的字体
转载请注明来源:https://www.cnblogs.com/hookjc/ BOOL CALLBACK EnumFonts(CONST LOGFONT* lplf, CONST TEXTMETRI ...