爬虫工具可以干什么_爬虫高级(中间件、CrawlSpider、分布式、增量式、反爬十种方法总结)...

  • 拓展:scrapy如何进行图片数据爬取(ImagePipeLine)
  • 中间件
  • CrawlSpider
  • 分布式
  • 增量式

中间件

c19bd5c48db313d4e401c5f594d3395d.png
先从spider的红线开始走

爬虫中间件

下载中间件

作用:批量拦截请求和响应

比如发送两个请求,返回了一个响应和一个异常

如何拦截请求和响应?

用middleware.py文件中的这三个方法:process_request、process_response、process_exception

  • process_request: 请求(可以过滤)
  • process_response: 响应
  • process_exception: 异常

拦截请求干什么?

  • 进行请求头信息的篡改==>需要写在process_request方法中
    • - request.headers['User-Agent'] = 'xxx'
  • 给请求设置代理==》process_exception
    • - request.meta['proxy'] = 'https://ip:port'
  • 拦截响应干什么?
    • 篡改响应数据

中间件的作用

middleware.py,代码

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

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals
class MiddleproDownloaderMiddleware(object):
    #拦截所有的请求
    #参数:request就是拦截到的请求,spider就是爬虫类实例化好的对象
    def process_request(self, request, spider):
        print('i am process_request()')
        request.headers['User-Agent'] = 'xxx'
    #拦截所有的响应
    #request就是拦截到响应对应的请求对象
    def process_response(self, request, response, spider):
        print('i am process_response()')
        return response
    #拦截发生异常的请求对象
    def process_exception(self, request, exception, spider):
        #为何需要拦截发生异常的请求?
        #将拦截到异常的请求进行修正操作,修正成正常的请求对象后一定要对其进行重新发送
        print('i am process_exception()')
        #对异常的请求进行修正操作
        request.meta['proxy'] = 'https://ip:port'
        return request

settings.py,代码

DOWNLOADER_MIDDLEWARES = {
   'middlePro.middlewares.MiddleproDownloaderMiddleware': 543,
}

例:网易案例,篡改response数据

爬虫文件,wangyi.py,代码

# -*- coding: utf-8 -*-
import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem
class WangyiSpider(scrapy.Spider):
    name = 'wangyi'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://news.163.com/']
    model_urls = [] #五个板块对应的url

    #实例化好了浏览器对象
    bro = webdriver.Chrome(executable_path=r'C:/Users/Administrator/Desktop/爬虫/chromedriver.exe')

    #解析出五个板块对应的url
    def parse(self, response):
        #解析五个板块对应的url
        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
        model_index = [3,4,6,7,8]
        for index in model_index:
            li_tag = li_list[index]
            model_url = li_tag.xpath('./a/@href').extract_first()
            self.model_urls.append(model_url)
        #需要每一个板块的url发起请求(注意:板块url请求到的页面中有动态加载数据(新闻数据))
        for url in self.model_urls:
            yield scrapy.Request(url=url,callback=self.parse_model)

    #用来解析每一个板块中的新闻数据(新闻的标题和新闻详情页的url)
    def parse_model(self,response):
        div_list = response.xpath('/html/body/div[1]/div[3]/div[4]/div[1]/div/div/ul/li/div/div')
        for div in div_list:
            title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
            new_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()

            item = WangyiproItem()
            item['title'] = title
            if new_url:
                #对新闻的详情页url发起请求
                yield scrapy.Request(new_url,callback=self.parse_new,meta={'item':item})

    #用来解析新闻内容
    def parse_new(self,response):
        item = response.meta['item']
        content = response.xpath('//*[@id="content"]//text()').extract()
        content = ''.join(content)
        item['content'] = content

        yield item

    def closed(self,spider):
        print('整个爬虫结束!')
        self.bro.quit()

middleware.py,代码

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

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals
from time import sleep
from scrapy.http import HtmlResponse

class WangyiproDownloaderMiddleware(object):

    def process_request(self, request, spider):
        return None

    def process_response(self, request, response, spider):
        model_urls = spider.model_urls
        bro = spider.bro
        if request.url in model_urls:
            #response就是我们想要拦截到的五个板块对应的响应对象
            #将response的响应数据进行修改
            bro.get(request.url)
            sleep(2)
            page_text = bro.page_source
            return HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
        else:
            return response

    def process_exception(self, request, exception, spider):

        pass

CrawlSpider

  • 作用:实现全栈数据爬取
    • CrawlSpider其实就是Spider的一个子类
  • 使用:
    • 创建爬虫文件:scrapy genspider -t crawl spiderName http://www.xxx.com
就是可以用正则来定位所有的符合条件的标签

例:爬取阳光生活

http://wz.sun0769.com/political/index/politicsNewest?id=1&page=2

爬虫文件sun.py,代码

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class SunSpider(CrawlSpider):
    name = 'sun'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&type=4&page=']
    #连接提取器:可以根据指定规则(allow=正则)进行连接的提取
    link = LinkExtractor(allow=r'id=1&page=d+')
    # link = LinkExtractor(allow=r'') 可以将网站中所有的连接都获取
    rules = (
        #规则解析器:接收link的连接且对其进行请求发送,然后根据指定形式的规则(callback)对其进行数据解析
        Rule(link, callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        print(response)

middleware.py,代码

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

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals
from time import sleep
from scrapy.http import HtmlResponse

class WangyiproDownloaderMiddleware(object):


    def process_request(self, request, spider):

        return None

    def process_response(self, request, response, spider):
        model_urls = spider.model_urls
        bro = spider.bro
        if request.url in model_urls:
            #response就是我们想要拦截到的五个板块对应的响应对象
            #将response的响应数据进行修改
            bro.get(request.url)
            sleep(2)
            page_text = bro.page_source
            return HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
        else:
            return response

    def process_exception(self, request, exception, spider):

        pass

items.py,代码

import scrapy
class WangyiproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    content = scrapy.Field()

pipeline.py,代码

class WangyiproPipeline(object):
    def process_item(self, item, spider):
        print(item)
        return item

settings.py,代码

BOT_NAME = 'wangyiPro'
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'

SPIDER_MODULES = ['wangyiPro.spiders']
NEWSPIDER_MODULE = 'wangyiPro.spiders'

ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
# 下载器
DOWNLOADER_MIDDLEWARES = {
   'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
}
# 管道
ITEM_PIPELINES = {
   'wangyiPro.pipelines.WangyiproPipeline': 300,
}

分布式

  • 概念:可以组建一个分布式的机群,然后让其联合且分布的对同一个资源进行数据爬取。
  • 原生的scrapy框架无法实现分布式?
    • scrapy无法实现调度器的共享
    • scrapy无法实现管道的共享
  • 如何实现分布式?
    • 组件:scrapy-redis(手动安装)
    • 作用:可以给scrapy提供可以被共享的管道和调度器

分布式的实现流程:

一、新建工程
二、cd 工程
三、新建爬虫文件(CrawlSpider) scrapy genspider -t crawl spiderName www.xxx.com
四、修改爬虫文件:
    1.导包:from scrapy_redis.spiders import RedisCrawlSpider
    2.将爬虫类的父类修改为RedisCrawlSpider
    3.将start_url进行替换,替换成redis_key = ‘xxx’
    4.实现后续的请求和解析操作
五、修改setting的配置文件
    1.开启可以被共享的管道
        ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 400
        }
    2.开启可以被共享的调度器
        # 使用scrapy-redis组件的去重队列
        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
        # 使用scrapy-redis组件自己的调度器
        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        # True表示可以实现增量,False不可以实现增量
        SCHEDULER_PERSIST = True
    3.指定redis的host
        REDIS_HOST = 'redis服务的ip地址'
        REDIS_PORT = 6379

五、对redis的配置文件进行配置redis.windows.conf
    - 56行:将默认绑定注释掉
    - 75行:protected-mode no
六、启动redis的服务端和客户端
七、执行工程
八、向调度器的队列中扔入一个起始的url
    - 需要在redis的客户端进行操作:
        - lpush fbsQueue www.xxx.com
九、爬取到的数据全部存储到了redis的xxx:items这个数据结构中进行的存储
lpush是对redis里面列表类型进行添加操作

例:分布式爬虫

settings.py,代码

#开启可以被共享的管道
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}


#开启可以被共享的调度器
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# True表示可以实现增量,False不可以实现增量
SCHEDULER_PERSIST = True

#指定redis
REDIS_HOST = '192.168.20.90'
REDIS_PORT = 6379

items.py,代码

import scrapy


class FbsproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()

爬虫文件,代码

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from fbsPro.items import FbsproItem


class FbsSpider(RedisCrawlSpider):
    name = 'fbs'
    # allowed_domains = ['www.xxx.com']
    # start_urls = ['http://www.xxx.com/']
    #表示的就是可以被共享的调度器队列的名称
    redis_key = 'fbsQueue'
    #提取页码连接
    link = LinkExtractor(allow=r'page/d+.html')
    rules = (
        Rule(link, callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
        for li in li_list:
            title = li.xpath('./div/a/@title').extract_first()
            item = FbsproItem()
            item['title'] = title

            yield item

增量式

监测网站数据更新的情况,只爬取最新更新出来的数据。

原理:去重

实现:

  • 记录表:可以将爬虫爬取过的信息记录在记录表中。以后再次爬取数据的时候,先在记录表中做查询,返回的结果如果是没有爬取过改数据,则进行爬取,否则跳过即可。
    • 特性:记录表可以去重,且可以进行持久化存储。
      • 因此redis的set集合是最为合适
  • 数据指纹:指的就是爬取过的一组数据的唯一标示。

例:

settings.py,代码

ITEM_PIPELINES = {
   'zlsPro.pipelines.ZlsproPipeline': 300,
}
把管道解开注释

pipeline.py,代码

class ZlsproPipeline(object):
    def process_item(self, item, spider):
        conn = spider.conn
        conn.lpush('movieData',item)
        return item

items.py,代码

class ZlsproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    desc = scrapy.Field()

爬虫文件

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from redis import Redis
from zlsPro.items import ZlsproItem
class MovieSpider(CrawlSpider):
    name = 'movie'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.4567kan.com/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/5/page/1.html']
    link = LinkExtractor(allow=r'page/d+.html') #提取页码连接
    conn = Redis(host='127.0.0.1',port=6379)

    rules = (
        Rule(link, callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
        for li in li_list:
            title = li.xpath('./div/a/@title').extract_first()
            detail_url = "https://www.4567kan.com"+li.xpath('./div/a/@href').extract_first()
            item = ZlsproItem()
            item['title'] = title

            ex = self.conn.sadd("movie_urls",detail_url)
            if ex == 1:#当前这个记录在记录表中不存在,可以爬取
                print('有新数据更新,正在爬取......')
                yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})
            else:
                print('数据暂无更新!!!')

    def parse_detail(self,response):
        item = response.meta['item']
        desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
        item['desc'] = desc

        yield item

反爬十种方法

  • robots
    • 不遵守robots协议,settings里面操作
  • US检测
    • 在settings.py里面加上UA检测,UA在network里面
  • 动态加载数据
    • 用抓包工具找ajax请求,requests发送请求,带上参数,头部....
  • 图片懒加载
    • 直接去解析图片的伪属性即可,只能应用于静态网站

6116d827164fa89e849779e62297b414.png
  • cookie
    • 用session对象去cookie
  • 验证码
    • 超级鹰去解析,把验证码当成图片来保存在本地,然后用超级鹰去识别,返回一个字符串,把这个字符串当作验证码的参数传递到请求里面
    • 点击验证码的话,可以用seleuim,去裁剪验证码,去超级鹰去识别,然后根据坐标去用动作链去操作。
  • 动态变化的请求参数
    • js逆向,去找原生的js生成动态参数d的请求方法
  • 代理
    • proxies参数 = random.randint(代理池列表)
    • 代理池列表是列表套字典的形式,值可以去代理网站爬取
  • 数据加密
    • js逆向
  • js混淆
    • 用反混淆网站去做js处理,https://www.bm8.com.cn/jsConfusion/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值