赞
踩
环境安装:pip install Pillow
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'
需求:将图片的名称和详情页中图片的数据进行爬取,持久化存储。
分析:
爬虫文件:
import 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 <= 5: 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
管道:
# 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开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。
降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为WORNING或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘ERROR’
问题:在之前代码中,我们从来没有手动的对start_urls列表中存储的起始url进行过请求的发送,但是起始url的确是进行了请求的发送,那这是如何实现的呢?
解答:其实是因为爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就可以对start_urls列表中的url发起请求:
def start_requests(self):
for u in self.start_urls:
yield scrapy.Request(url=u,callback=self.parse)
【注意】该方法默认的实现,是对起始的url发起get请求,如果想发起post请求,则需要子类重写该方法。
import 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)
用来处理整个系统的数据流处理, 触发事务(框架核心)
- 调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
- 下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
- 爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
- 项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
scrapy的中间件有两个:
中间件重要方法:
# 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.http import HtmlResponse class MiddleproDownloaderMiddleware: #拦截处理所有的请求对象 #参数:request就是拦截到的请求对象,spider爬虫文件中爬虫类实例化的对象 #spider参数的作用可以实现爬虫类和中间类的数据交互 def process_request(self, request, spider): return None #拦截处理所有的响应对象 #参数:response就是拦截到的响应对象,request就是被拦截到响应对象对应的唯一的一个请求对象 def process_response(self, request, response, spider): #篡改了拦截到的响应数据:返回一个自定义的新的响应对象 response = HtmlResponse(request=request,body='123',url=request.url,encoding='utf-8') 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
# 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: #类方法:作用是返回一个下载器对象(忽略) @classmethod 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)
request.headers[‘User-Agent’] = ua
def process_request(self, request, spider):
request.headers['User-Agent'] = '从列表中随机选择的一个UA值'
print(request.url+':请求对象拦截成功!')
return None
request.cookies = cookies
def process_request(self, request, spider):
request.headers['cookie'] = 'xxx'
#request.cookies = 'xxx'
print(request.url+':请求对象拦截成功!')
return None
实现网站的全站数据爬取
crawlspider其实就是scrapy封装好的一个爬虫类,通过该类提供的相关的方法和属性就可以实现全新高效形式的全站数据爬取。
使用流程:
新建一个scrapy项目
cd 项目
创建爬虫文件(*):
scrapy genspider -t crawl spiderName www.xxx.com
爬虫文件中发生的变化有哪些?
注意:
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
爬取不同页码下的标题数据
import scrapy from time import sleep from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule #LinkExtractor链接提取器:可以根据指定的规则进行链接的提取 #Rule规则解析器:可以根据指定规则对请求到的数据进行数据解析 class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.xxx.com'] start_urls = ['https://wz.sun0769.com/political/index/politicsNewest?id=1&page=1'] #link就表示创建出来的一个链接提取器 #allow参数后面要跟一个正则表达式,就可以作为链接提取的规则 #link首先会去start_urls表示的页面中进行链接的提取 link = LinkExtractor(allow=r'id=1&page=(\d+)') rules = ( #创建了一个规则解析器 #链接提取器提取到的链接会发送给Rule这个规则解析器 #规则解析器接收到链接后,会对链接进行请求发送,并且根据callback指定的函数进行数据解析 Rule(link, callback='parse_item', follow=True), ) #解析方法的调用次数取决于link提取到的链接的个数 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() print(title)
爬取页码对应页面的标题数据和详情页中的详细内容数据(深度爬取)
import scrapy from time import sleep from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from crawlPro.items import CrawlproItem #LinkExtractor链接提取器:可以根据指定的规则进行链接的提取 #Rule规则解析器:可以根据指定规则对请求到的数据进行数据解析 class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['https://wz.sun0769.com'] start_urls = ['https://wz.sun0769.com/political/index/politicsNewest?id=1&page=1'] #链接提取器和规则解析器一定是一对一关系 #提取页码链接 link = LinkExtractor(allow=r'id=1&page=\d+') rules = ( #解析页面对应页面中的标题数据 Rule(link, callback='parse_item', follow=False), ) #解析方法的调用次数取决于link提取到的链接的个数 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() detail_url = 'https://wz.sun0769.com' + li.xpath('./span[3]/a/@href').extract_first() item = CrawlproItem() item['title'] = title yield scrapy.Request(url=detail_url, callback=self.parse_detail,meta={'item':item}) def parse_detail(self,response): item = response.meta['item'] detail_content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]//text()').extract() item['content'] = detail_content yield item
分布式在日常开发中并不常用,只是一个噱头!
概念:
声明:
如何是的scrapy可以实现分布式呢?
编码流程(重点):
1.创建项目
2.cd 项目
3.创建基于crawlSpider的爬虫文件
4.settings配置文件的修改
常规内容修改(robots和ua等),先不指定日志等级
指定可以被共享的管道类
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
指定可以被共享的调度器
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否允许暂停
SCHEDULER_PERSIST = True
指定数据库
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
5.修改redis数据库的配置文件(redis.windows.conf)
在配置文件中改行代码是没有没注释的:
bind 127.0.0.1
#将上述代码注释即可(解除本机绑定,实现外部设备访问本机数据库
如果配置文件中还存在:protected-mode = true,将true修改为false,
修改为false后表示redis数据库关闭了保护模式,表示其他设备可以远程访问且修改你数据库中的数据
6.启动redis数据库的服务端和客户端
7.运行项目,发现程序暂定一直在等待,等待爬取任务
8.需要向可以被共享的调度器的队列(redis_key的值)中放入一个起始的url
爬虫应用场景分类
通用爬虫
聚焦爬虫
功能爬虫
增量式:
import scrapy import redis from ..items import Zlsdemo2ProItem class JianliSpider(scrapy.Spider): name = 'jianli' # allowed_domains = ['www.xxx.com'] start_urls = ['https://sc.chinaz.com/jianli/free.html'] conn = redis.Redis(host='127.0.0.1',port=6379) def parse(self, response): div_list = response.xpath('//*[@id="container"]/div') for div in div_list: title = div.xpath('./p/a/text()').extract_first() #充当数据指纹 detail_url = 'https:'+div.xpath('./p/a/@href').extract_first() ex = self.conn.sadd('data_id',detail_url) item = Zlsdemo2ProItem() item['title'] = title if ex == 1: print('有最新数据的更新,正在采集......') yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={'item':item}) else: print('暂无数据更新!') def parse_detail(self,response): item = response.meta['item'] download_url = response.xpath('//*[@id="down"]/div[2]/ul/li[1]/a/@href').extract_first() item['download_url'] = download_url yield item
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。