20200228 scrapy高级使用及分布式

昨日回顾

1 安装 pip3 install scrapy
2 创建项目:scrapy startproject 项目名字
3 创建爬虫:scrapy genspider 爬虫名字 地址
4 运行爬虫:scrapy crawl 爬虫名字
5 目录结构
6 setting的配置
7 css选择和xpath选择(extract_first(),extract())
8 持久化:pipline方式

scrapy高级使用及分布式

1.爬虫件参数

yield Request(url=url,callback=self.parse,meta={'item':item})

# 参数
url			再次请求的地址
callback	回调函数(处理数据)
meta		请求传参

2.提升scrapy爬取的效率

- 在配置文件中进行相关的配置即可:(默认还有一套setting)
    
#1 增加并发:
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。

#2 降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’

# 3 禁止cookie:
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False

# 4禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False

# 5 减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

3.scrapy的中间件(下载中间件)

-DownloaderMiddleware

	-process_request
        -retrun None/Respnose/Request/raise
        -None:表示继续处理
        -Respnose:会被引擎调度,进入爬虫
        -Request:会被引擎调度,放到调度器,等待下一次爬取
        -raise:process_exception触发执行
        
	-process_response
        -Response:继续处理,会被引擎调度,放到爬虫中解析
        -Request:会被引擎调度,放到调度器,等待下一次爬取
        -raise:process_exception触发执行
        
    -process_exception
        -None:表示继续处理
        -Respnose:会被引擎调度,进入爬虫
        -Request:会被引擎调度,放到调度器,等待下一次爬取

process_exception

def process_exception(self, request, exception, spider):
       from scrapy.http import Request
       print('xxxx')
       # request.url='https://www.baidu.com/'
       request=Request(url='https://www.baidu.com/')
       return request

process_request

	def process_request(self, request, spider):
        # 请求头
        # print(request.headers)
        # request.headers['User-Agent']=random.choice(self.user_agent_list)

        # 设置cookie(并不是所有的请求,都需要带cookie,加一个判断即可)
        # 可以使用cookie池
        # print(request.cookies)
        # # import requests # 如果自己搭建cookie池,这么写
        # # ret=requests.get('127.0.0.1/get').json()['cookie']
        # # request.cookies=ret
        # request.cookies={'name':'lqz','age':18}

        # 使用代理(使用代理池)
        # print(request.meta)
        # request.meta['proxy'] = 'http://117.27.152.236:1080'
        return None

process_response

  def process_response(self, request, response, spider):
				#详见下面
        return response

4.scrapy中使用selenium

1 在爬虫中启动和关闭selenium
	-启动
  	bro = webdriver.Chrome('./chromedriver')
  -关闭
  	  def closed(self,spider):
        print("爬虫结束,会走我关了")
        self.bro.close()
     
    
    
    
2 在下载中间件中写
    def process_response(self, request, response, spider):
        from scrapy.http import Response,HtmlResponse
        # 因为向该地址发请求,不能执行js,现在用selenium执行js,获取执行完的结果,再返回response对象
        url=request.url
        spider.bro.get(url)
        page_source=spider.bro.page_source
        new_response=HtmlResponse(url=url,body=page_source,encoding='utf-8',request=request)
        return new_response

5.去重规则

如何自己写去重规则,布隆过滤器

from scrapy.dupefilter import RFPDupeFilter
from scrapy.core.scheduler import Scheduler

# 整个去重规则是通过RFPDupeFilter中的request_seen控制
# 在调度器Scheduler中的enqueue_request调用,如果dont_filter=True就不过滤了

# scrapy中如下两个地址,是同一个地址,通过request_fingerprint处理了
# http://www.baidu.com/?name=lqz&age=18
# http://www.baidu.com/?age=18&name=lqz
res1=Request(url='http://www.baidu.com/?name=lqz&age=18')
res2=Request(url='http://www.baidu.com/?age=18&name=lqz')
print(request_fingerprint(res1))
print(request_fingerprint(res2))


# 有更省空间的方式
bitmap方式:比特位:计算机的存储单位  1bit    byte=8个比特位    1kb=1024b   
布隆过滤器:BloomFilter:原理


# 自己重写去重规则
# 1 在setting中配置
DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' #默认的去重规则帮我们去重,去重规则在内存中
# 写一个类,继承BaseDupeFilter,重写方法,主要重写request_seen,如果返回True表示有了,False表示没有

6.分布式爬虫

# scrapy-redis
# 概念:整站爬取,假设有9w条连接地址,一台机器一天只能爬3w条,爬3天
#			现在想用3台机器爬一天
# scrapy项目部署在3台机器上,三台机器重复的爬9w条,3台机器共享爬取的地址,
# 3台机器都去一个队列中取地址爬取

#scrapy-redis 重写了Scheduler和pipline
pip3 install scrapy-redis
#https://github.com/rmax/scrapy-redis :整个源码总共不超过1000行

使用

安装scrapy-redis
pip install scrapy_redis

1 在setting中配置

# 分布式爬虫的配置
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# Ensure all spiders share same duplicates filter through redis.
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}

2 修改爬虫类

	class CnblogsSpider(RedisSpider): # 继承RedisSpider
    name = 'cnblogs'
    allowed_domains = ['www.cnblogs.com']

    redis_key = 'myspider:start_urls'  # 原来的start_ulr去掉,携写成这个

3 项目部署

把项目部署到不同的机器上,或者同一个机器开启多个?

4 redis中写去启动url

# 启动cmd或者redis服务
- redis-cli
- lpush myspider:start_urls https://www.cnblogs.com/(初始网址)

代码

全站爬取博客园

run.py启动文件

#-*- coding: utf-8 -*-
#!/usr/bin/env python3

' a test module '

__author__ = 'Fwzzz'


from scrapy.cmdline import execute

# execute(['scrapy','crawl','cnblogs','--nolog'])
# execute(['scrapy','crawl','cnblogs'])

# 分布式爬虫
execute(['scrapy','crawl','cnblogs_redis','--nolog'])

爬虫文件cnblogs.py

# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request
# 导入items文件类
from cnblog_pa.items import CnblogPaItem


# 了解: 究竟真正的起始爬取的方法在哪  start_requests()
class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'
    allowed_domains = ['www.cnblogs.com']
    # start_urls = ['https://www.cnblogs.com//']


    def start_requests(self):
        # # 深度爬取
        # # 获取所有的页码,进行url拼接  https://www.cnblogs.com/#p2
        # html = Request(url='https://www.cnblogs.com/')
        # last_p = html.css('div[p_200 last]').extract()
        # print(last_p)
        # 需要yield
        yield Request(url='https://www.cnblogs.com/',callback=self.parse)




    def parse(self, response):
        # 获取文章块
        div_list = response.css('div.post_item')
        for div in div_list:
            # 声明item
            item = CnblogPaItem()
            # 文章标题
            title = div.xpath('./div[2]/h3/a/text()').extract_first()
            # 文章链接
            url=div.xpath('./div[2]/div/span[2]/a/@href').extract_first()
            # 用户名
            author = div.xpath('./div[2]/div/a/text()').extract_first()
            # 文章简介
            desc = div.xpath('./div[2]/p/text()').extract()[-1].replace('
','').replace(' ','')
            # print(title_url,user,desc)

            # 持久化的准备传参设置
            item['title'] = title
            item['url'] = url
            item['author'] = author
            item['desc'] = desc


            # 深度爬取  # 广度爬取
            # yield item对象会去保存 ,request对象会去爬取 # callback回调函数
            yield Request(url=url,callback=self.parse_detail,meta={'item':item})
            # 传参使用meta


        # 广度爬取下一页
        # 解析下一页的url   css选择器取属性
        next_url = 'https://www.cnblogs.com' + response.css('div.pager>a:last-child::attr(href)').extract_first()
        # print(1111,next_url)
        yield Request(url=next_url,callback=self.parse)



    # 文章详情
    def parse_detail(self,response):
        # 获取传递的item对象
        item = response.meta.get('item')
        # print(item)
        # 深度爬取文章详情
        # 获取文章的html页面
        content = response.css('#post_detail').extract_first()

        item['content'] = str(content)

        # 存入数据库  (直接跳转到pipeline文件中进行持久化)
        yield item

items.py持久化字段设置

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy

# 数据库持久化
class CnblogPaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    author = scrapy.Field()
    desc = scrapy.Field()
    content = scrapy.Field()
    url = scrapy.Field()

pipelines.py数据库持久化文件

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy

# 数据库持久化
class CnblogPaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    author = scrapy.Field()
    desc = scrapy.Field()
    content = scrapy.Field()
    url = scrapy.Field()

settings文件

BOT_NAME = 'cnblog_pa'

SPIDER_MODULES = ['cnblog_pa.spiders']
NEWSPIDER_MODULE = 'cnblog_pa.spiders'
ROBOTSTXT_OBEY = False
CONCURRENT_REQUESTS = 32

# 普通使用
ITEM_PIPELINES = {
   'cnblog_pa.pipelines.CnblogPaPipeline': 300,
}


# 分布式爬虫的设置
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# Ensure all spiders share same duplicates filter through redis.
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}
# redis连接的配置  (不配置,默认即可)

补充

## 后续

redis高级(持久化rdb和aof,附近的人:地理位置信息,独立用户统计,发布订阅,一主多从,高可用(哨兵)),mongodb,es(集群搭建),docker(镜像,容器,私服搭建,项目部署,dockerfile),mysql主从做读写分离,django中做读写分离,go语言

## redis

缓存雪崩,缓存穿透



## 反扒

0.检测浏览器header
1 User-Agent,
2 cookie
3. ip 封禁
4. 图片验证码(打码平台,手动)
5  图片懒加载()
6 js加密,混淆:pip install PyExecJS 动态执行js
7 css加密
8 图片防盗链

原文地址:https://www.cnblogs.com/fwzzz/p/12733853.html