zoukankan      html  css  js  c++  java
  • 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 图片懒加载
    
  • 相关阅读:
    Maven 环境的配置
    zTree的简单例子
    plsql免安装客户端的配置
    HDU 1232 畅通工程
    HDU 5698 瞬间移动
    Codeforces 1015E1 Stars Drawing (Easy Edition)
    Codeforces 784B Santa Claus and Keyboard Check
    Codeforces 500C New Year Book Reading
    NSarray 赋值 拷贝 等问题记录
    UINavigationController 操作记录
  • 原文地址:https://www.cnblogs.com/surpass123/p/13448996.html
Copyright © 2011-2022 走看看