Scrapy框架
Scrapy框架环境的安装
环境安装比较多,跟着流程走就ok
1. pip3 install wheel 2. 下载twisted 链接:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 3. 进入下载目录,在该目录下cmd后,pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl 4. pip3 install pywin32 5. pip3 install scrapy
Scrapy的使用
从创建项目开始
# 创建一个工程 scrapy startproject 自己的项目名称 # 进入到项目文件 cd 项目名称 # 创建爬虫文件 scrapy genspider 爬虫文件名字 www.xxx.com # 这里的网址随便填写就行,可以自己更改
其他配置:
- settings.py # 不遵从rbotes协议 ROBOTSTXT_OBEY = False # 进行UA伪装 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3861.0 Safari/537.36 Edg/77.0.230.2' # 指定日志等级 LOG_LEVEL = 'ERROR'
运行项目:
scrapy crawl spiderName(自己起的爬虫文件名字)
持久化存储
- 基于终端命令的持久化:
- 前提:只可以将parse方法的返回值进行本地文件的持久化存储
- 指令:scrapy crawl spiderName -o filepath
- 基于管道的持久化存储:
- 编码流程:
- 数据解析
- 需要在item类中定义相关的属性(存储解析到的数据)
- 将解析到的数据存储或者封装到一个item类型的对象中
- 将item对象提交到管道中
- 在管道中需要接收item,且将item对象中的数据进行任意形式的持久化操作(例如数据库)
- 在配置文件中开启管道
举例(校花网):
创建项目:
scrapy startproject xiaohua
进入项目,创建爬虫文件
cd xiaohua
scrapy genspider meinv www.xxx.com
目录结构如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# UA USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3861.0 Safari/537.36 Edg/77.0.230.2' # robots ROBOTSTXT_OBEY = False # 日志 LOG_LEVEL = 'ERROR' # 管道 ITEM_PIPELINES = { 'xiaohua.pipelines.XiaohuaPipeline': 300, # 300表示的是优先级,数字越小优先级越高 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: utf-8 -*- import scrapy from ..items import XiaohuaItem class MeinvSpider(scrapy.Spider): name = 'meinv' # allowed_domains = ['www.xiaohua.com'] start_urls = ['http://www.xiaohuar.com/2014.html'] def parse(self, response): img_list = response.xpath('//*[@id="images"]/div') for img in img_list: # extract_first():从Selector对象中取出解析出的字符串数据 no = img.xpath("./div/div/text()").extract_first() imgSrc = 'http://www.xiaohuar.com' + img.xpath('./div/a/img/@src').extract_first() name = img.xpath('./p/a/text()').extract_first() # 实例化一个item类型对象 item = XiaohuaItem() # 将解析到的数据存入到该对象中 item['name'] = name item['no'] = no item['imgSrc'] = imgSrc # 将item提交到管道 yield item
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import scrapy class XiaohuaItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() no = scrapy.Field() name = scrapy.Field() imgSrc = scrapy.Field()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class XiaohuaPipeline(object): fp = None # 只会在开始爬虫的时候调用一次 def open_spider(self, spider): print('开始爬虫.....') self.fp = open('./xiaohua.txt', 'w', encoding='utf-8') # item:就是从爬虫文件中接收到的item对象 # process_item这个方法会调用几次 -- 多次 def process_item(self, item, spider): no = item['no'] name = item['name'] imgSrc = item['imgSrc'] self.fp.write(no + '\t' + name + '\t' + imgSrc + '\n') return item def close_spider(self, spider): self.fp.close() print('结束爬虫.....')
写完代码,运行即可
scrapy crawl meinv
补充:
- 关于scrapy的数据解析,和普通的xpath的不同之处是什么
- scrapy是通过 response.xpath()解析,并且返回的列表中存储的是Selector对象,必须使用extract(),如果确定只有一个数据可以使用extract_first()
- scrapy管道的细节处理
- 一个管道类负责将数据存储到某一个载体或者平台中
- 爬虫文件添加的item只会提交给第一个被执行的管道类
- 在管道类的process_item中的renturn item表示的含义就是将当前管道类接收的item传递给下一个将被执行的管道类
- 注意:爬虫类和管道类进行数据交互的形式
- yield item:只可以交互item类型的对象
- spider参数:可以交互任意形式的数据
糗事百科(基于管道分别存储到mysql 和 redis)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: utf-8 -*- import scrapy from ..items import QiubaiproItem class QiubaiSpider(scrapy.Spider): name = 'qiubai' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] def parse(self, response): # 解析糗事百科作者名称和对应内容 div_list = response.xpath('//div[@id="content-left"]/div') for div in div_list: author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first() content = div.xpath('./a[1]/div/span//text()').extract() content = ''.join(content) print(author) item = QiubaiproItem() item['author'] = author item['content'] = content yield item
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import scrapy class QiubaiproItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() author = scrapy.Field() content = scrapy.Field()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import pymysql from redis import Redis # 一个单独的管道类负责将数据存储到mysql class QiubaiproPipeline(object): conn = None cursor = None def open_spider(self, spider): self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', db='spider', charset='utf8') print(self.conn) def process_item(self, item, spider): self.cursor = self.conn.cursor() sql = 'insert into qiubai values ("%s","%s")' % (item['author'], item['content']) # 进行事物处理 try: self.cursor.execute(sql) self.conn.commit() except Exception as e: print(e) self.conn.rollback() return item def close_spider(self, spider): self.cursor.close() self.conn.close() class RedisPipeline(object): conn = None def open_spider(self, spider): self.conn = Redis(host='127.0.0.1', port=6379) def process_item(self, item, spider): dic = { 'author': item['author'], 'content': item['content'] } self.conn.lpush('qiuBaiData', dic)
全站数据爬取
- 基于手动请求发送实现
- 手动请求发送:
- yield scrapy.Request(url,callback)
- yield scrapy.FormRequest(url,formdata,callback)
注意点:
scrapy中的cookie是自动封装好的 scrapy主要是发get请求,也可以发post,因为post请求使用scrapy有点麻烦还不如requests yield scrapy.Request(url,callback) # scrapy发送get请求 yield scrapy.FormRequest(url,formdata,callback) # scrapy发送post请求 # 是父类请求发送的封装,发的是get请求,如果需要发post请求就重写 def start_requests(self): for url in self.start_urls: yield scrapy.Request(url=url, callback=self.parse) # 重写post的话也是这样(当然一般不使用scrapy发post请求,还不如requests) def start_requests(self): for url in self.start_urls: yield scrapy.FormRequest(url=url, formdata={'post请求参数'}, callback=self.parse)
深度爬取
- 不在同一张页面的信息爬取就是在深度爬取
- 基于手动请求发送
- 请求传参:持久化存储,将不同的回调中解析的数据存储到同一个item对象。请求传参传递的就是item对象
- 使用场景:如果使用scrapy爬取的数据没有在同一张页面中
- 传递方式:将传递数据封装到meta字典中,meta传递给了callback
- yield scrapy.Request(url,callback,meta)
- 接收:
- 在callback指定的回调函数中使用response进行接收:
- item = response.meta['key'] # 自己指定的key
案例:糗事百科的全站和深度爬取
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: utf-8 -*- import scrapy from ..items import QiubaideepproItem class QiubaiSpider(scrapy.Spider): name = 'qiubai' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] url = 'https://www.qiushibaike.com/text/page/%d/' page = 2 def parse(self, response): print(f'正在爬取第{self.page}页数据') # 解析糗事百科作者名称和对应内容 div_list = response.xpath('//div[@id="content-left"]/div') for div in div_list: author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first() # content = div.xpath('./a[1]/div/span//text()').extract() # content = ''.join(content) item = QiubaideepproItem() item['author'] = author detail_url = 'https://www.qiushibaike.com' + div.xpath('./a[1]/@href').extract_first() # 对详情页发请求 yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={'item': item}) if self.page <= 5: # 对其他网页进行手动请求发送 new_url = format(self.url % self.page) self.page += 1 # 手动发请求(发生了递归) yield scrapy.Request(new_url, callback=self.parse) # 解析详情页 def parse_detail(self, response): item = response.meta['item'] content_desc = response.xpath('//*[@id="single-next-link"]/div//text()').extract() content_desc = ''.join(content_desc) item['content_desc'] = content_desc yield item
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import scrapy class QiubaideepproItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() author = scrapy.Field() content_desc = scrapy.Field()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class QiubaideepproPipeline(object): def process_item(self, item, spider): print(item) return item
这里没有持久化存储,打印就可以看到结果。
其实这样的全站爬取不如CrawlSpider。
五大核心组件
- 引擎(Scrapy)
用来处理整个系统的数据流处理,触发事物(框架核心)
- 调度器(Scheduler) -- 包括过滤器,队列
用来接收引擎发过来的请求,压入队列中,并且在引擎再次请求的时候返回,可以想象成一个url(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个抓取的网址是什么,同时去除重复网址
- 下载器(Downloader)
用于下载网页内容,并将网页内容返回给spider(Scrapy下载器是建立在twisted这个高效的异步模型上的)
- 爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
- 项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
如何提升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