scrapy参数配置和持久化
一、配置参数
# 是否遵循爬虫协议
ROBOTSTXT_OBEY = False
# 浏览器类型(,默认写的是scrapy)
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
# 3、日志级别(默认是info,执行爬虫项目,info会被打印出来)、
# 设置成ERROR,只打印错误信息(提高爬虫效率)
LOG_LEVEL='ERROR'
二、scrapy持久化
# 1 scrapy crawl cnblogs -o cnblogs.json (这个不需要记)
1 在items中写类,类中写字段
class CnblogsSpiderItem(scrapy.Item):
title = scrapy.Field()
desc=scrapy.Field()
url=scrapy.Field()
author=scrapy.Field()
# 重点(文章详情,如果跟之前爬过的文章对应)
content=scrapy.Field()
2 在爬虫中把要保存的字段放到item对象中
article_item['url']=url
article_item['title']=title
article_item['desc']=desc
article_item['author']=author
yield article_item
3 在控制台输入:scrapy crawl cnblogs -o cnblogs.json
# 2 常用方式,只记住这一种
1 在items中写类,类中写字段
class CnblogsSpiderItem(scrapy.Item):
title = scrapy.Field()
desc=scrapy.Field()
url=scrapy.Field()
author=scrapy.Field()
# 重点(文章详情,如果跟之前爬过的文章对应)
content=scrapy.Field()
2 在爬虫中把要保存的字段放到item对象中
article_item['url']=url
article_item['title']=title
article_item['desc']=desc
article_item['author']=author
yield article_item
3 在setting中配置
ITEM_PIPELINES = {
'cnblogs_spider.pipelines.CnblogsSpiderFilePipeline': 300, # 数字表示优先级,数字越小,优先级越大
'cnblogs_spider.pipelines.CnblogsSpiderMysqlPipeline': 400, # 数字表示优先级,数字越小,优先级越大
}
4 在pipline中写
class CnblogsSpiderFilePipeline:
# 爬虫启动他会执行
def open_spider(self,spider):
# spider是爬虫对象
print(spider.name)
print('爬虫开始了')
self.f=open('cnblogs.txt','w',encoding='utf-8')
def close_spider(self,spider):
# 爬虫停止会执行
print('爬虫停止了')
self.f.close()
def process_item(self, item, spider):
self.f.write(item['title']+item['desc']+item['author']+item['url'])
self.f.write('/n')
return item
import pymysql
class CnblogsSpiderMysqlPipeline:
def open_spider(self,spider):
self.conn=pymysql.connect( host='127.0.0.1', user='root', password="123",database='cnblogs', port=3306)
self.cursor=self.conn.cursor()
def close_spider(self,spider):
self.conn.commit()
self.cursor.close()
self.conn.close()
def process_item(self, item, spider):
sql='insert into aritcle (title,`desc`,url,author) values (%s,%s,%s,%s )'
self.cursor.execute(sql,args=[item['title'],item['desc'],item['url'],item['author']])
return item
三、请求传递参数
# 1、给另一个请求传递参数,在响应中拿到(借助meta)
yield Request(url=url, callback=self.parser_detail, meta={'item':article_item})
# 2、在解析方法中通过response对象获取
item=response.meta.get('item')
四、提高爬虫效率
提高scrapy的爬取效率(scrapy是异步框架,基于twisted,性能其实已经很高了),
可以优化的点:
- 在配置文件中进行相关的配置即可:(默认还有一套setting,类比django)
#1 增加并发:
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS值为100,并发就设置成了为100:
CONCURRENT_REQUESTS = 100
#2 降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:
LOG_LEVEL = "ERROR"
# 3 禁止cookie:(比如cnblogs不需要cookie)
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:
COOKIES_ENABLED = False
# 4禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:
RETRY_ENABLED = False
# 5 减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中修改超时时间(单位为秒):
DOWNLOAD_TIMEOUT = 10 # 超时时间为10s
五、scrapy中间件
1、下载中间件 CnblogsSpiderDownloaderMiddleware
process_request方法:
1 请求来的时候
'''
Must either:
- return None: continue processing this request 返回None,进入下一个下载中间件的process_request
- or return a Response object 返回response对象,会给引擎,引擎给爬虫,进入解析
- or return a Request object 返回请求对象,会给引擎,引擎给调度器,放到调度器
- or raise IgnoreRequest: process_exception() methods of 抛异常,就会触发process_exception的执行
'''
# 总结:
返回None,继续爬取
返回Resoponse对象,会给引擎,再交给爬虫,去解析
返回Request对象,会给引擎,给调度器,等待下一次被调度
# 什么情况会用下载中间件:
加代理,加cookie,加浏览器类型
集成 selenium
# 修改cookie
request.cookies={'name':'jason'}
# 使用代理
proxy='http://154.16.63.16:8080' # 从代理池中获取
request.meta["proxy"] =proxy
# 修改请求头
request.headers.setlist(b'User-Agent','asdfasdf')
2、爬虫中间件
了解即可
六、集成selenium
# 1、在爬虫中写:
class ChoutiSpider(scrapy.Spider):
name = 'chouti'
allowed_domains = ['www.bilibili.com']
start_urls = ['https://www.bilibili.com/v/dance/otaku/#/all/default/0/1/']
bro = webdriver.Chrome(executable_path='./chromedriver')
bro.implicity_wait(10)
@staticmethod
def close(spider, reason):
spider.bro.close()
# 2、在中间件中直接使用
class CnblogsSpiderDownloaderMidddleware:
def process_request(self, request, spider):
spider.bro.get(request.url)
response = HtmlResponse(url=request.url, body=bytes(spider.bro.page_source, encoding='utf-8'))
return response
# 隐藏浏览器(无头浏览器)
七、去重规则
1、scrapy使用的去重
# 1、scrapy默认会去重,使用了RFPDupeFilter
from scrapy.dupefilters import RFPDupeFilter
# 2、在默认的settings中配置
# 3、本质原理是使用的集合去重
# 4、更高级部分:
将url的?后面的部分打散了再排序:
示例:
127.0.0.1/?name=lqz&age=19
127.0.0.1/?age=19&name=lqz
这两个其实是同一个网址,但是如果直接将url放进集合中是不能去重的
# 本质原理:
fp = self.request_fingerprint(request) # 得到一个指纹,上面那两个地址得到的结果就是一样的
2、自定义去重规则
# 如果自己要写一个去重类,如何使用?
- 写一个类,继承BaseDupeFilter,重写def request_seen(self, request):
- 返回True 表示爬虫过了
- 返回False 表示没有爬过
# 跟高级的去重方案:
- 集合去重的问题:当需要处理的地址特别多时(比如上亿个地址),集合就会变得特别大,会非常占内存
- 布隆过滤器,可以用极小内存完成大量去重