如有雷同,就是我直接抄的的!
文章目录
scrapy五大核心组件简介
引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)
调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
创建一个工程
scrapy startproject xxx
创建一个爬虫文件
先通过cd设置文件目录
再通过scrapy genspider Spidername www.xxxx.com
import scrapy
class FirstSpider(scrapy.Spider):
#爬虫文件名称:爬虫源文件唯一标识
name = 'first'
#允许的域名:限定start_url中那些可以进行请发送
allowed_domains = ['www.xxx.com']
#起始的url:列表中url会被scrapy自动进行请求的发送
start_urls = ['http://www.baidu.com/','http://www.youku.com/']
#用作数据解析:scray对start_urls列表中请求后,会调用parse对每一个请求后响应对象的相应数据进行解析
def parse(self, response):
pass
执行工程
示例first.py
ROBOTXT_OBEY=True导致请求失败
scrapy crawl spiderName
这样请求会发现出现请求失败,查找原因
发现是设置中的ROBOTXT_OBEY=True,表示遇到遵从反爬虫机制Robot协议,导致请求失败
将ROBOTXT_OBEY=Flase,再目录中的Settings.py更改即可
成功!
只显示返回结果
在执行scrapy crawl first,会返回一大串的类似日志的东西
如果想要respond的结果可以这么写
scrapy crawl first --nolog
不过不建议使用
当程序出现错误时,如:
这种拼写错误,–nolog就会返回一个空信息
解决方法
在settings.py中添加一行代码
输出指定类型的日志信息
LOG_LEVEL=‘ERROR’
这样就能在scrapy crawl first情况下只输出错误提示信息
改正错误后,正常运行
基于终端指令持久化存储
scrapy crawl 爬虫名称 -o xxx.json
scrapy crawl 爬虫名称 -o xxx.xml
scrapy crawl 爬虫名称 -o xxx.csv
执行输出指定格式进行存储:将爬取到的数据写入不同格式的文件中进行存储
终端指令:scrapy crawl <name> -o <path>
只可以将parse方法的返回值储存到本地的文件中
虽然它比较高效便捷,但是它的局限性比较强,数据只可以储存到指定的路径中
注意:持久化存储对应的文本类型只可以为 ‘json’,’jsonlines’,‘csv’,‘xml’,‘marshal’,‘pickle’
基于管道的的持久化存储
1.数据解析
2.将解析的数据封装到item类型的对象中
在items.py中的item类型中,我们可以在类中定义相关的属性
xxx=scrapy.Field()
3.将item类型的对象提交给管道进行持久化存储的操作
pipelines.py
在管道类process_item中要将其接收到的item对象中存储道德数据进持久化的存储操作
process_item专门用来处理item类型对象,该方法可以接受提交过来的item对象,该方法接受到一个item就会调用一次
重写父类的一个方法
open_spider(self,spider) 该方法只在爬虫开始时调用一次
**close_spider(self,spider)**只会在爬虫结束时候调用一次
4.在配置文件中开启管道
在setting.py中
这里的300表示优先级,数值越低优先级越高
如果需要将一份数据存储到不同地方,可以采用scrapy添加管道类的方法,如我建立一个管道类QiushiPipeline_2
setting.py中修改优先级,这里时先存到QiushiPipeline,在存到QiushiPipeline_2中对应的文件地址,值得注意的是,后一个管道类(这里是QiushiPipeline_2)获取的item的方式是通过前一个管道类process_item方法return item,来获得的,前一个管道类return item 就会将item传递给下一个即将被执行的管道类
请求传参
在某些情况下,我们爬取的数据不在同一个页面中,例如,我们爬取一个电影网站,电影的名称,评分在一级页面,而要爬取的其他电影详情在其二级子页面中。这时我们就需要用到请求传参。
请求传参的使用场景
当我们使用爬虫爬取的数据没有存在于同一张页面的时候,则必须使用请求传参
# -*- coding: utf-8 -*-
import scrapy
from bossPro.items import BossproItem
class BossSpider(scrapy.Spider):
name = 'boss'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=']
url = 'https://www.zhipin.com/c101010100/?query=python&page=%d'
page_num = 2
#回调函数接受item
def parse_detail(self,response):
item = response.meta['item']
job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract()
job_desc = ''.join(job_desc)
# print(job_desc)
item['job_desc'] = job_desc
yield item
#解析首页中的岗位名称
def parse(self, response):
li_list = response.xpath('//*[@id="main"]/div/div[3]/ul/li')
for li in li_list:
item = BossproItem()
job_name = li.xpath('.//div[@class="info-primary"]/h3/a/div[1]/text()').extract_first()
item['job_name'] = job_name
# print(job_name)
detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/h3/a/@href').extract_first()
#对详情页发请求获取详情页的页面源码数据
#手动请求的发送
#请求传参:meta={},可以将meta字典传递给请求对应的回调函数
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})
#分页操作
if self.page_num <= 3:
new_url = format(self.url%self.page_num)
self.page_num += 1
yield scrapy.Request(new_url,callback=self.parse)
主网页中的数据可以得到标题以及详情页数据的网址,再通过对详情页url发起请求,可以得到详情数据,这两个实在不同函数中实现的,所以如果需要传递Item类型的对象,则需要在meta字典(meta={})传递给请求对应的回调函数
提升scrapy的爬取效率
增加并发:
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。
降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’
禁止cookie
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False
禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False
减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
scrapy图片数据爬取
基于文件下载的管道类
在scrapy中我们之前爬取的都是基于字符串类型的数据,那么要是基于图片数据的爬取,那又该如何呢?
其实在scrapy中已经为我们封装好了一个专门基于图片请求和持久化存储的管道类ImagesPipeline,那也就是说如果想要基于scrapy实现图片数据的爬取,则可以直接使用该管道类即可。
ImagesPipeline使用流程
在配置文件中进行如下配置:
IMAGES_STORE = ‘./imgs’:表示最终图片存储的目录
管道类的编写:
from scrapy.pipelines.images import ImagesPipeline
import scrapy
class imgsPipeline(ImagesPipeline):
# 对item中的图片进行请求操作
def get_media_requests(self, item, info):
yield scrapy.Request(item['img_src'],meta={'item':item})
# 定制图片的名称
def file_path(self, request, response=None, info=None):
item = request.meta.get('item')
file_name=item['img_name']+'.jpg'
print('[%s]正在保存中....' %file_name)
return file_name
def item_completed(self, results, item, info):
return item # 该返回值会传递给下一个即将被执行的管道类
下载中间件(DownloaderMiddlewares)
位于scrapy引擎和下载器之间的一层组件。
作用:我们主要使用下载中间件处理请求,一般会对请求设置随机的User-Agent ,设置随机的代理。目的在于防止爬取网站的反爬虫策略。
常用的User-Agent大全
——————————
免费的网络代理网站1
免费的网络代理网站2
(2)在下载器完成将Response传递给引擎中,下载中间件可以对响应进行一系列处理。比如用selenium返回动态渲染的网页源码,进行gzip解压等。
scrapy中selenium的应用
在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出来的数据值。
但是通过观察我们会发现,通过浏览器进行url请求发送则会加载出对应的动态加载出的数据。那么如果我们想要在scrapy也获取动态加载出的数据,则必须使用selenium创建浏览器对象,然后通过该浏览器对象进行请求发送,获取动态加载的数据值。
selenium在scrapy中使用的原理分析:
当引擎将国内板块url对应的请求提交给下载器后,下载器进行网页数据的下载,然后将下载到的页面数据,封装到response中,提交给引擎,引擎将response在转交给Spiders。Spiders接受到的response对象中存储的页面数据里是没有动态加载的新闻数据的。要想获取动态加载的新闻数据,则需要在下载中间件中对下载器提交给引擎的response响应对象进行拦截,切对其内部存储的页面数据进行篡改,修改成携带了动态加载出的新闻数据,然后将被篡改的response对象最终交给Spiders进行解析操作。
selenium在scrapy中的使用流程:
1.重写爬虫文件的构造方法,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次)
2.重写爬虫文件的closed(self,spider)方法,在其内部关闭浏览器对象。该方法是在爬虫结束时被调用
3.重写下载中间件的process_response方法,让该方法对响应对象进行拦截,并篡改response中存储的页面数据
4.在配置文件中开启下载中间件