09_请求传参&中间件&大文件下载&CrawlSpider
五大核心组件
目的
- 大概了解scrapy的运行机制
- 为分布式铺垫
请求传参实现的深度爬取
深度爬取:爬取的数据没有在同一张页面中(例如首页数据+详情页数据)
在scrapy中如果没有请求传参我们是无法持久化存储数据
实现方式:
scrapy.Request(url,callback,meta)
- meta是一个字典,可以将meta传递给callback
- callback取出meta:
- response.meta[‘xx’]
例子:实现多页爬取、请求传参、深度爬取
ℹ️moviePro
import scrapy
from moviePro.items import MovieproItem
class MovieSpider(scrapy.Spider):
name = 'movie'
#allowed_domains = ['www.xxx.com']
start_urls = ['http://www.4567kan.com/index.php/vod/show/id/5.html']
url = 'http://www.4567kan.com/index.php/vod/show/id/5/page/%d.html'
pageNum = 2
def parse(self, response):
li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
for li in li_list:
title = li.xpath('./div/a/@title').extract_first()
detail_url = 'http://www.4567kan.com' + li.xpath('./div/a/@href').extract_first()
item = MovieproItem()
item['title'] = title
#对详情页url发起请求
#meta的作用:可以将meta字典传递给callback
yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={'item':item})
if self.pageNum<5:
new_url = format(self.url%self.pageNum)
self.pageNum+=1
yield scrapy.Request(url=new_url,callback=self.parse)
#被用作于解析详情页的数据
def parse_detail(self,response):
#接收传递过来的meta
item = response.meta['item']
desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]').extract_first()
item['desc'] = desc
yield item
中间件
-
作用:批量拦截请求和响应
-
爬虫中间件
-
下载中间件(推荐)
- ℹ️middlePro
- middlewares.py
- process_request
- process_response
- process_exception
- 拦截请求:
- 篡改请求url
- 伪装请求头信息
- UA
- Cookie
- 设置请求代理(重点)
- 拦截响应
- 篡改响应数据
- 代理操作必须使用中间件才可以实现
- process_exception:
request.meta['proxy'] = 'http://ip:port'
- process_exception:
大文件下载
ℹ️imgPro
- 大文件数据是在管道中请求到的
- 下属管道类是scrapy封装好的我们直接用即可
from scrapy.pipelines.images import ImagesPipeline #提供了数据下载功能,图片、视频、音频
- 重写该管道类的三个方法:
- get_media_requests
- 对图片地址发起请求
- file_path
- 返回图片名称即可
- item_completed
- 返回item,将其返回给下一个即将被执行的管道类
- 在配置文件中添加:
- IMAGES_STORE = ‘dirName’
- get_media_requests
- 还有MediaPipeline和FilesPipeline
案例:爬取多张图片
#pipelines.py
#管道需要接收item中的图片地址和名称,然后在管道中请求到图片的数据对其进行持久化存储
from scrapy.pipelines.images import ImagesPipeline #提供了数据下载功能,图片、视频、音频
import scrapy
class ImgsPipeline(ImagesPipeline):
#根据图片地址发起请求
def get_media_requests(self, item, info):
yield scrapy.Request(url=item['src'],meta={'item':item})
def file_path(self, request, response=None, info=None, *, item=None):
#通过request获取meta
item = request.meta['item']
filePath = item['name']
return filePath #只需要返回图片名称
#将item传递给下一个即将被执行的管道类
def item_completed(self, results, item, info):
return item
#settings.py
IMAGES_STORE = './imgLibs'
ITEM_PIPELINES = {
'imgPro.pipelines.ImgsPipeline': 300,
}
setings.py中的常用配置
-
增加并发,并发线程
CONCURRENT_REQUESTS = 100
-
降低日志级别
LOG_LEVEL = 'INFO'
-
禁止cookie
COOKIES_ENABLED = False
-
禁止重试,对失败的HTTP禁止重新请求
RETRY_ENABLED = False
-
减少下载超时,放弃非常慢的连接
DOWNLOAD_TIMEOUT = 10 #10秒
CrawlSpider
- 其实是Spider的一个子类。Spider爬虫文件中爬虫类的父类。
- 子类的功能一定是多于父类
- 作用:被用作于专业实现全站数据爬取
- 将一个页面下所有页码对应的数据进行爬取
- 基本使用:
- 创建一个工程
- cd 工程
- 创建一个基于CrawlSpider的爬虫文件
- scrapy genspider -t crawl SpiderName www.xxx.com
- 执行工程
- 注意:
- 一个链接提取器对应一个规则解析器(多个链接提取器和多个规则解析器)
- 在实现深度爬取的过程中需要和scrapy.Request()结合使用
- 面试题:
- 如何将一个网站中所有的链接都进行爬取
- 任务(首页和详情页都有):尝试使用CrawlSpider实现深度爬取
案例:获得全部链接
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class FirstSpider(CrawlSpider):
name = 'first'
#allowed_domains = ['http://pic.netbian.com']
start_urls = ['http://pic.netbian.com/4kfengjing/']
#实例化LinkExtractor对象
#链接提取器:根据指定规则(allow参数)在页面中进行连接(url)的提取
#allow='正则':提取链接的规则
# link = LinkExtractor(allow=r'http://pic.netbian.com/4kfengjing/index_\d+\.html')
link = LinkExtractor(allow=r'') #取出网站全站的链接
rules = (
#实例化一个Rule对象
#规则解析器:接收链接提取器提取到的链接,对其发起请求,然后根据指定规则(callback)解析数据
Rule(link, callback='parse_item', follow=True),
)
#follow = True
#将链接提取器 继续作用到 链接提取器提取到的页码 所对应的 页面中
def parse_item(self, response):
print(response)
#基于response实现数据解析
CrawlSpider实现的深度爬取
通用方式:CrawlSpider+Spider实现
案例:多页问题反映并且获取页内内容
class SumSpider(CrawlSpider):
name = 'sum'
#allowed_domains = ['www.xxx.com']
start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']
#提取页码链接
link = LinkExtractor(allow=r'id=1&page=\d+')
#http://wz.sun0769.com/political/politics/index?id=489390
# link_detail = LinkExtractor(allow=r'index\?id=\d+')
rules = (
#解析每一个页码对应页面中的数据
Rule(link, callback='parse_item', follow=False),
# Rule(link_detail,callback='parse_detail')
)
def parse_item(self, response):
li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')
for li in li_list:
title = li.xpath('./span[3]/a/text()').extract_first().strip()
status = li.xpath('./span[2]/text()').extract_first()
detail_url = 'http://wz.sun0769.com' + li.xpath('./span[3]/a/@href').extract_first()
item = SunproItem()
item['title'] = title
item['status'] = status
yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={'item':item})
def parse_detail(self,response):
content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()
item = response.meta['item']
item['content'] = content
yield item