摘要
本文内容转自网络,个人学习记录使用,请勿传播
ImagePipeLines的请求传参
环境安装:pip install Pillow
1
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36'
需求:将图片的名称和详情页中图片的数据进行爬取,持久化存储。
分析:
- 深度爬取:请求传参
- 多页的数据爬取:手动请求的发送
爬虫文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import scrapy
from ..items import DeepimgproItem
class ImgSpider(scrapy.Spider):
name = 'img'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://pic.netbian.com/4kmeinv/']
#通用的url模板
url_model = 'https://pic.netbian.com/4kmeinv/index_%d.html'
page_num = 2
def parse(self, response):
#解析出了图片的名称和详情页的url
li_list = response.xpath('//*[@id="main"]/div[3]/ul/li')
for li in li_list:
title = li.xpath('./a/b/text()').extract_first() + '.jpg'
detail_url = 'https://pic.netbian.com'+li.xpath('./a/@href').extract_first()
item = DeepimgproItem()
item['title'] = title
#需要对详情页的url发起请求,在详情页中获取图片的下载链接
yield scrapy.Request(url=detail_url,callback=self.detail_parse,meta={'item':item})
if self.page_num <= 2:
new_url = format(self.url_model%self.page_num)
self.page_num += 1
yield scrapy.Request(url=new_url,callback=self.parse)
#解析详情页的数据
def detail_parse(self,response):
meta = response.meta
item = meta['item']
img_src = 'https://pic.netbian.com'+response.xpath('//*[@id="img"]/img/@src').extract_first()
item['img_src'] = img_src
yield item管道:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
import scrapy
from itemadapter import ItemAdapter
from scrapy.pipelines.images import ImagesPipeline
class DeepimgproPipeline(ImagesPipeline):
# def process_item(self, item, spider):
# return item
def get_media_requests(self, item, info):
img_src = item['img_src']
#请求传参,将item中的图片名称传递给file_path方法
#meta会将自身传递给file_path
print(item['title'],'保存下载成功!')
yield scrapy.Request(url=img_src,meta={'title':item['title']})
def file_path(self, request, response=None, info=None, *, item=None):
#返回图片的名称
#接收请求传参过来的数据
title = request.meta['title']
return title
def item_completed(self, results, item, info):
return item
如何提高scrapy的爬取效率
1 | 增加并发: |
post请求发送
问题:在之前代码中,我们从来没有手动的对start_urls列表中存储的起始url进行过请求的发送,但是起始url的确是进行了请求的发送,那这是如何实现的呢?
解答:其实是因为爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就可以对start_urls列表中的url发起请求:
1
2
3def start_requests(self):
for u in self.start_urls:
yield scrapy.Request(url=u,callback=self.parse)【注意】该方法默认的实现,是对起始的url发起get请求,如果想发起post请求,则需要子类重写该方法。
- yield scrapy.Request():发起get请求
- yield scrapy.FormRequest():发起post请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import scrapy
class FanyiSpider(scrapy.Spider):
name = 'fanyi'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://fanyi.baidu.com/sug']
#父类中的方法:该方法是用来给起始的url列表中的每一个url发请求
def start_requests(self):
data = {
'kw':'dog'
}
for url in self.start_urls:
#formdata是用来指定请求参数
yield scrapy.FormRequest(url=url,callback=self.parse,formdata=data)
def parse(self, response):
result = response.json()
print(result)
scrapy的核心组件
- 从中可以大致了解scrapy框架的一个运行机制
1 | - 引擎(Scrapy) |
中间件
scrapy的中间件有两个:
- 爬虫中间件
- 下载中间件
- 中间件的作用是什么?
- 观测中间件在五大核心组件的什么位置,根据位置了解中间件的作用
- 下载中间件位于引擎和下载器之间
- 引擎会给下载器传递请求对象,下载器会给引擎返回响应对象。
- 作用:可以拦截到scrapy框架中所有的请求和响应。
- 拦截请求干什么?
- 修改请求的ip,修改请求的头信息,设置请求的cookie
- 拦截响应干什么?
- 可以修改响应数据
- 拦截请求干什么?
- 观测中间件在五大核心组件的什么位置,根据位置了解中间件的作用
中间件重要方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
from scrapy import signals
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
class MiddleproDownloaderMiddleware:
#类方法:作用是返回一个下载器对象(忽略)
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
#拦截处理所有的请求对象
#参数:request就是拦截到的请求对象,spider爬虫文件中爬虫类实例化的对象
#spider参数的作用可以实现爬虫类和中间类的数据交互
def process_request(self, request, spider):
return None
#拦截处理所有的响应对象
#参数:response就是拦截到的响应对象,request就是被拦截到响应对象对应的唯一的一个请求对象
def process_response(self, request, response, spider):
return response
#拦截和处理发生异常的请求对象
#参数:reqeust就是拦截到的发生异常的请求对象
def process_exception(self, request, exception, spider):
pass
#控制日志数据的(忽略)
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
开发代理中间件
request.meta[‘proxy’] = proxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
from scrapy import signals
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
from scrapy import Request
class MiddleproDownloaderMiddleware:
#类方法:作用是返回一个下载器对象(忽略)
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
#拦截处理所有的请求对象
#参数:request就是拦截到的请求对象,spider爬虫文件中爬虫类实例化的对象
#spider参数的作用可以实现爬虫类和中间类的数据交互
def process_request(self, request, spider):
#是的所有的请求都是用代理,则代理操作可以写在该方法中
request.meta['proxy'] = 'http://ip:port'
#弊端:会使得整体的请求效率变低
print(request.url+':请求对象拦截成功!')
return None
#拦截处理所有的响应对象
#参数:response就是拦截到的响应对象,request就是被拦截到响应对象对应的唯一的一个请求对象
def process_response(self, request, response, spider):
print(request.url+':响应对象拦截成功!')
return response
#拦截和处理发生异常的请求对象
#参数:reqeust就是拦截到的发生异常的请求对象
#方法存在的意义:将发生异常的请求拦截到,然后对其进行修正
def process_exception(self, request, exception, spider):
print(request.url+':发生异常的请求对象被拦截到!')
#修正操作
#只有发生了异常的请求才使用代理机制,则可以写在该方法中
request.meta['proxy'] = 'https://ip:port'
return request #对请求对象进行重新发送
#控制日志数据的(忽略)
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
开发UA中间件
request.headers[‘User-Agent’] = ua
1
2
3
4def process_request(self, request, spider):
request.headers['User-Agent'] = '从列表中随机选择的一个UA值'
print(request.url+':请求对象拦截成功!')
return None
开发Cookie中间件
request.cookies = cookies
1
2
3
4
5def process_request(self, request, spider):
request.headers['cookie'] = 'xxx'
#request.cookies = 'xxx'
print(request.url+':请求对象拦截成功!')
return None
selenium+scrapy
需求:将网易新闻中的国内,国际,军事,航空四个板块下的新闻标题和内容进行数据爬取
- 注意:哪些数据是动态加载的!
- 技术:selenium,scrapy,中间件
爬虫文件
1
2
3
4
5
6
7
8
9
10
11
12class WangyiSpider(RedisSpider):
name = 'wangyi'
#allowed_domains = ['www.xxxx.com']
start_urls = ['https://news.163.com']
def __init__(self):
#实例化一个浏览器对象(实例化一次)
self.bro = webdriver.Chrome(executable_path='/Users/bobo/Desktop/chromedriver')
#必须在整个爬虫结束后,关闭浏览器
def closed(self,spider):
print('爬虫结束')
self.bro.quit()
中间件文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from scrapy.http import HtmlResponse
#参数介绍:
#拦截到响应对象(下载器传递给Spider的响应对象)
#request:响应对象对应的请求对象
#response:拦截到的响应对象
#spider:爬虫文件中对应的爬虫类的实例
def process_response(self, request, response, spider):
#响应对象中存储页面数据的篡改
if request.url in['http://news.163.com/domestic/','http://news.163.com/world/','http://news.163.com/air/','http://war.163.com/']:
spider.bro.get(url=request.url)
js = 'window.scrollTo(0,document.body.scrollHeight)'
spider.bro.execute_script(js)
time.sleep(2) #一定要给与浏览器一定的缓冲加载数据的时间
#页面数据就是包含了动态加载出来的新闻数据对应的页面数据
page_text = spider.bro.page_source
#篡改响应对象
return HtmlResponse(url=spider.bro.current_url,body=page_text,encoding='utf-8',request=request)
else:
return response
配置文件:
1
2
3
4DOWNLOADER_MIDDLEWARES = {
'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
}
最新方式的全栈数据爬取:CrawlSpider的一个爬虫类
分布式
增量式