• scrapy知识进阶


    scrapy得请求传参

    # 把药传递的数据放到meta中
    yield Request(url=next_url,meta={'item':item},callback=self.parse)
    # 在response对象中取出来
    item=response.meta.get('item')
    

    download组件会将当次请求(request)所携带的meta赋值给当次响应(response)的meta

    如何提高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
    

    <1>增加并发数

    在scrapy开启的默认并发线程为32个,可以适当地增加。
    在settings配置文件中修改:
    CONCURRENT_REQUESTS = 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
    

    实现流程:

    scrapy有自己一套默认的内置配置文件,你在settings.py中写的配置,会被覆盖默认的配置选项,先将默认的配置加载到字典中,在将你设置的配置update 进去。

    scrapy的中间件

    scrapy的中间件分为:爬虫中间件和下载中间件,都写在middlewares中

    # 配置文件
    DOWNLOADER_MIDDLEWARES = {
       'firstscrapy.middlewares.FirstscrapyDownloaderMiddleware': 543,
    }
    
    SPIDER_MIDDLEWARES = {
        'firstscrapy.middlewares.FirstscrapySpiderMiddleware': 543,
    }
    

    下载中间件

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.
    
        # Must either:
        # - return None: continue processing this request 
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None #继续往下走
    
    
    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.
    
        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response
    
    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.
    
        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass
    

    在中间中写代码

    -process_request:返回不同的对象,后续处理不同(加代理...)
      		# 1 更换请求头
            # print(type(request.headers))
            # print(request.headers)
            #
            # from scrapy.http.headers import Headers
            # request.headers['User-Agent']=''
    
            # 2 加cookie ---cookie池
            # 假设你你已经搭建好cookie 池了,
            # print(request.cookies)
            # request.cookies={'username':'asdfasdf'}
    
            # 3 加代理
            # print(request.meta)
            # request.meta['download_timeout'] = 20
            # request.meta["proxy"] = 'http://27.188.62.3:8060'
    -process_response:返回不同的对象,后续处理不同
    - process_exception
    def process_exception(self, request, exception, spider):
            print('xxxx')
            # 不允许直接改url
            # request.url='https://www.baidu.com'
            from scrapy import Request
            request=Request(url='https://www.baidu.com',callback=spider.parser)
            return request
    
    

    selenium在scrapy中的使用流程

    # 当前爬虫用的selenium是同一个
    
    # 1 在爬虫中初始化webdriver对象
        from selenium import webdriver
        class CnblogSpider(scrapy.Spider):
            name = 'cnblog'
            ...
     bro=webdriver.Chrome(executable_path='../chromedriver.exe')
    # 2 在中间件中使用(process_request)
    spider.bro.get('https://dig.chouti.com/')   response=HtmlResponse(url='https://dig.chouti.com/',body=spider.bro.page_source.encode('utf-8'),request=request)
        return response
    	
    # 3 在爬虫中关闭
        def close(self, reason):
            print("我结束了")
            self.bro.close()
    

    爬虫去重源码分析

    from scrapy.utils.request import referer_str, request_fingerprint
    def __init__(self, path=None, debug=False):
        self.file = None
        self.fingerprints = set()   创建一个集合
        self.logdupes = True
        self.debug = debug
        self.logger = logging.getLogger(__name__)
        if path:
            self.file = open(os.path.join(path, 'requests.seen'), 'a+')
            self.file.seek(0)
            self.fingerprints.update(x.rstrip() for x in self.file)
            
    def request_seen(self, request):
        fp = self.request_fingerprint(request)
        if fp in self.fingerprints: # 如何在集合中,return True 不再继续往下爬了
            return True
        self.fingerprints.add(fp)  # 如果不在集合中,把它加进去
        if self.file:
            self.file.write(fp + '
    ')
    
    def request_fingerprint(self, request):
        # 指纹去重,用的集合,将URL的地址取指纹(MD5),放到集合中
        return request_fingerprint(request)
    

    针对大量数据的去重,可以考虑使用布隆过滤器,其的错误率的大小,取决于数组的位数和哈希函数的个数。

    from scrapy.dupefilters import BaseDupeFilter
    class UrlFilter(BaseDupeFilter):
        def __init__(self):
            self.bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
    
    def request_seen(self, request):
        if request.url in self.bloom:
            return True
        self.bloom.add(request.url)
    

    在配置文件中配置

     DUPEFILTER_CLASS = 'cnblogs.qc.UrlFilter' #默认的去重规则帮我们去重,去重规则在内存中
    

    分布式爬虫(scrapy-redis)

    <1>安装scrapy-redis

    pip install scrapy-redis
    

    <2>Spider继承RedisSpider

    <3>将start_urls配置到配置文件中

    # 不能写start_urls = ['https:/www.cnblogs.com/']
    需要写 redis_key = 'myspider:start_urls' 
    

    <4>在settings.py文件中

    # redis的连接
    REDIS_HOST = 'localhost'                            # 主机名
    REDIS_PORT = 6379                                   # 端口
    	# 使用scrapy-redis的去重
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    # 使用scrapy-redis的Scheduler
    # 分布式爬虫的配置
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 持久化的可以配置,也可以不配置
    ITEM_PIPELINES = {
       'scrapy_redis.pipelines.RedisPipeline': 299
    }
    

    现在要让爬虫运行起来,需要去redis中以myspider:start_urls为key,插入一个起始地址lpush myspider:start_urls https://www.cnblogs.com/

    破解知乎登陆(js逆向和解密)

    """
    client_id=c3cef7c66a1843f8b3a9e6a1e3160e20&
    grant_type=password&
    timestamp=1596702006088&
    source=com.zhihu.web&
    signature=eac4a6c461f9edf86ef33ef950c7b6aa426dbb39&
    username=%2B86liuqingzheng&
    password=1111111&
    captcha=&
    lang=en&
    utm_source=&
    ref_source=other_https%3A%2F%2Fwww.zhihu.com%2Fsignin%3Fnext%3D%252F"
    """
    

    实现流程:

    # 破解知乎登陆
    
    import requests    #请求解析库
    
    import base64							  #base64解密加密库
    from PIL import Image	  			      #图片处理库
    import hmac								  #加密库
    from hashlib import sha1				  #加密库
    import time
    from urllib.parse import urlencode		  #url编码库
    import execjs							  #python调用node.js
    from http import cookiejar as cookielib
    class Spider():
        def __init__(self):
            self.session = requests.session()
            self.session.cookies = cookielib.LWPCookieJar()    #使cookie可以调用save和load方法
            self.login_page_url = 'https://www.zhihu.com/signin?next=%2F'
            self.login_api = 'https://www.zhihu.com/api/v3/oauth/sign_in'
            self.captcha_api = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=en'
            self.headers = {
                'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER',
            }
    
            self.captcha =''         #存验证码
            self.signature = ''	   #存签名
    
        # 首次请求获取cookie
        def get_base_cookie(self):
            self.session.get(url=self.login_page_url, headers=self.headers)
    
        def deal_captcha(self):
            r = self.session.get(url=self.captcha_api, headers=self.headers)
            r = r.json()
            if r.get('show_captcha'):
                while True:
                    r = self.session.put(url=self.captcha_api, headers=self.headers)
                    img_base64 = r.json().get('img_base64')
                    with open('captcha.png', 'wb') as f:
                        f.write(base64.b64decode(img_base64))
                    captcha_img = Image.open('captcha.png')
                    captcha_img.show()
                    self.captcha = input('输入验证码:')
                    r = self.session.post(url=self.captcha_api, data={'input_text': self.captcha},
                                          headers=self.headers)
                    if r.json().get('success'):
                        break
    
        def get_signature(self):
            # 生成加密签名
            a = hmac.new(b'd1b964811afb40118a12068ff74a12f4', digestmod=sha1)
            a.update(b'password')
            a.update(b'c3cef7c66a1843f8b3a9e6a1e3160e20')
            a.update(b'com.zhihu.web')
            a.update(str(int(time.time() * 1000)).encode('utf-8'))
            self.signature = a.hexdigest()
    
        def post_login_data(self):
            data = {
                'client_id': 'c3cef7c66a1843f8b3a9e6a1e3160e20',
                'grant_type': 'password',
                'timestamp': str(int(time.time() * 1000)),
                'source': 'com.zhihu.web',
                'signature': self.signature,
                'username': '+8618953675221',
                'password': '',
                'captcha': self.captcha,
                'lang': 'en',
                'utm_source': '',
                'ref_source': 'other_https://www.zhihu.com/signin?next=%2F',
            }
    
            headers = {
                'x-zse-83': '3_2.0',
                'content-type': 'application/x-www-form-urlencoded',
                'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER',
            }
    
            data = urlencode(data)
            with open('zhih.js', 'rt', encoding='utf-8') as f:
                js = execjs.compile(f.read(), cwd='node_modules')
            data = js.call('b', data)
    
            r = self.session.post(url=self.login_api, headers=headers, data=data)
            print(r.text)
            if r.status_code == 201:
                self.session.cookies.save('mycookie')
                print('登录成功')
            else:
                print('登录失败')
    
        def login(self):
            self.get_base_cookie()
            self.deal_captcha()
            self.get_signature()
            self.post_login_data()
    if __name__ == '__main__':
        zhihu_spider = Spider()
        zhihu_spider.login()
    

    爬虫的反爬措施总结

    1 user-agent
    2 referer
    3 cookie(cookie池,先访问一次)
    4 频率限制(代理池,延迟)
    5 js加密(扣出来,exjs模块指向)
    6 css加密
    7 验证码(打码平台),半手动
    8 图片懒加载
    
  • 相关阅读:
    地球人找工作之笔霸传奇
    地球人找工作之简历的秘密
    地球人找工作之前期准备
    Swift中的一些关键字
    swift动画小试牛刀
    【extjs6学习笔记】1.8 初始: ExtJS命名约定
    【extjs6学习笔记】1.7 初始:加载第三方库
    【extjs6学习笔记】1.6 初始:本地化
    【extjs6学习笔记】0.1 准备:基础概念(02)
    【extjs6学习笔记】1.5 初始:关于布局
  • 原文地址:https://www.cnblogs.com/surpass123/p/13448996.html
Copyright © 2020-2023  润新知