首页 > 其他分享 >爬虫_06days

爬虫_06days

时间:2024-02-26 15:33:25浏览次数:22  
标签:return 06days self request spider 爬虫 scrapy

# 1 scrapy 框架 架构
	-爬虫:我们写爬取起始地址,解析数据的位置
    -引擎:控制数据流向
    -调度器:控制爬取的先后
    -下载器:负责下载,建立在twisted 之上
    -pipline:持久化
    
    
# 2 目录结构
	
    -创建爬虫命令:scrapy gensipder 名字 网址
    -运行爬虫:scrapy crawl 爬虫名字

# 3 解析数据
	1 response对象有css方法和xpath方法
        -css中写css选择器     response.css('')
        -xpath中写xpath选择   response.xpath('')
    2 重点1:
        -xpath取文本内容
        	'.//a[contains(@class,"link-title")]/text()'
        -xpath取属性
       	 	'.//a[contains(@class,"link-title")]/@href'
        -css取文本
        	'a.link-title::text'
        -css取属性
        	'img.image-scale::attr(src)'
    3 重点2:
        .extract_first()  取一个
        .extract()        取所有
        
        
# 4 整站爬取cnblogs
	- 在 cnblogs 的解析中
    	-网址: yield Request 对象
        -数据: yield Item 对象
    -两个解析
    	-首页,下一页: parse
        	-下一个地址:Request
            - 详情地址:Request+item对象
            	-把item传递到 下一次的解析中
                -Request(url=地址,call_banck=解析的方法,meta={})
        -详情:parse_detail
        	-response.meta 中取出传递的对象(item)
            
            
# 5 持久化
	-1 在Item.py中写个类
    -2 在爬虫中解析出的数据---》组装到Item的类中
    -3 yield 对象
    -4 配置文件中配置项目管道
    	-数字--》优先级
        ITEM_PIPELINES = {
           "myfirstscrapy.pipelines.MyfirstscrapyFilePipeline": 300,
           "myfirstscrapy.pipelines.MyfirstscrapyMysqlPipeline": 100,
        }
    -5 pipelines.py 写保存
    	MyfirstscrapyFilePipeline
        	-open_spider
            -close_spider
            -process_item
        MyfirstscrapyMysqlPipeline
        
# 6 配置文件
	-基础配置:请求头,是否遵循爬虫协议,日志级别。。
    -高级配置:提高爬虫效率

1 爬虫中间件和下载中间件

# 引擎和下载器之间的叫:下载中间件
# 爬虫和引擎之间的叫:爬虫中间件

爬虫中间件(用的少,了解)

1 写一个类:middlewares.py
    MyfirstscrapySpiderMiddleware
2 在配置文件中注册
    SPIDER_MIDDLEWARES = {
       "myfirstscrapy.middlewares.MyfirstscrapySpiderMiddleware": 543,
    }
3 中间件类中得方法
class MyfirstscrapySpiderMiddleware:
    # 内部自动触发,不需要我们手动调用
    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        # 信号
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        # 走架构图第6步,会触发这里
        return None

    def process_spider_output(self, response, result, spider):
         # 架构图,第1,7步走这里
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
        # 出异常会走
        pass

    def process_start_requests(self, start_requests, spider):
        # 第一爬取起始地址时会走
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        # 爬虫开启时会走
        spider.logger.info("Spider opened: %s" % spider.name)

下载中间件

1 写一个类:middlewares.py
    MyfirstscrapyDownloaderMiddleware
2 在配置文件中注册
    DOWNLOADER_MIDDLEWARES = {
       "myfirstscrapy.middlewares.MyfirstscrapyDownloaderMiddleware": 543,
    }
3 中间件类中得方法
class MyfirstscrapyDownloaderMiddleware:
    # 自动会触发,不需要我们手动调用
    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # 请求来了--》执行它--》架构图 第 4 步

        # 必须返回 return 以下之一:
            # - return None: 继续下一个中间件
            # - return a Response:直接返回给引擎 Response---》引擎把Response给爬虫
            # - return a Request:直接把返回给引擎---》引擎会把Request给调度器--》等待下次调度
            # - raise 异常: process_exception() 的执行
        return None

    def process_response(self, request, response, spider):
        # 请求走了--》执行它---》架构图 第 5 步

        # - return  Response:正常继续往后走--》给引擎---》引擎给爬虫去解析
        # - return a Request :给引擎--》引擎放到调度器--》等待下次调度
        # - raise IgnoreRequest:抛异常
        return response

    def process_exception(self, request, exception, spider):
        # 执行中间件出异常

        # 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

    def spider_opened(self, spider):
        spider.logger.info("Spider opened: %s" % spider.name)
        
        
        
        
# 4 重点是:
    process_request
    process_response

修改请求头的user-agent,referer,Authorization...

#### 随机生成user-agent
# pip3 install fake-useragent
from fake_useragent import UserAgent
ua=UserAgent()
# print(ua.random)
# print(ua.firefox)
# print(ua.chrome)
print(ua.edge)





def process_request(self, request, spider):
    # 修改请求头中得user-agent
    print('-----',request.headers)
    # if 'userinfo' in request.url: # 当访问查询用户信息地址,再改referer,改useragent,加登录信息
    # 1 上次访问的域
    request.headers['referer'] = 'http://www.lagou.com'
    # 2 登录认证的信息
    request.headers['Authorization']='asfasf.asfas.asfas'
    # 3 客户端类型--》fake_useragent--》随机生成客户端类型 user-agent
    from fake_useragent import UserAgent
    ua = UserAgent()
    # print(ua.random)
    request.headers['User-Agent'] = ua.random
    return None

加cookie

def process_request(self, request, spider):
    print('-----',request.cookies)
    # cnblogs-->cookie 文件
    request.cookies['name'] = 'lqz'
    return None

加代理(MyfirstscrapyDownloaderMiddleware)

def get_proxy(self):
    import requests
    res = requests.get('http://127.0.0.1:5010/get/').json()
    if res.get('https'):
        return 'https://' + res.get('proxy')
    else:
        return 'http://' + res.get('proxy')

def process_request(self, request, spider):
    # request.meta['proxy'] = self.get_proxy()
    request.meta['proxy'] = 'http://192.168.11.11:8888'
    return None

scrapy集成selenium

# scrapy 下载的内容,是不包含执行完js后的数据--》等同于requests发送请
# 有的网站---》需要执行完js,数据才完整(分离项目)
# 对于前后端分离项目--》使用scrapy发送请求会导致数据不完整--》需要执行完js后才能解析数据
# 在scrapy中集成 selenium---》使用selenium去加载网页-->数据是执行完js后,完整的数据

使用步骤

1 在爬虫类中写 开启浏览器
class CnblogsSpider(scrapy.Spider):
    ###### 爬虫开启---》打开浏览器--->类属性--》对象可以取到
    from selenium.webdriver.edge.service import Service
    ser = Service()
    ser.path = r'.\myfirstscrapy\chromedriver.exe'
    bro = webdriver.Chrome(service=ser)
    
2 爬虫结束,关闭浏览器,爬虫类中写
    ## 爬虫结束---》关闭浏览器
    def close(spider, reason):
        # spider.bro.close()
        spider.bro.quit()

3 在中间件中
    def process_request(self, request, spider):
        from scrapy.http.response.html import HtmlResponse
        spider.bro.get(request.url)
        response=HtmlResponse(url=request.url,body=spider.bro.page_source.encode('utf-8'))
        return response
    
    
4 区分不同的地址,选择使用selenium或scrapy原生爬取
    -1 通过 url区分
    -2 通过在request对象的meta中加入标志
    # 爬虫类中
    yield Request(url=url, callback=self.parse_detail, meta={'item': item,'is_selenium':True})
    #中间件中:
    if request.meta.get('is_selenium'):
    
 # 使用scrapy效率低

 源码去重规则(布隆过滤器)

    scrapy 的调度器,会自动去重

源码去重原理

# 要爬取的Request对象,在进入到scheduler调度器排队之前,先执行enqueue_request,它如果return False,这个Request就丢弃掉,不爬了----》如何判断这个Request要不要丢弃掉,执行了self.df.request_seen(request),它来决定的-----》RFPDupeFilter类中的方法----》request_seen---》会返回True或False----》如果这个request在集合中,说明爬过了,就return True,如果不在集合中,就加入到集合中,然后返回False

# 本质原理,通过集合--》根据爬取的地址--》去重的
    -爬取路径一样---》post--》请求体不一样---》不要去重
    -如果使用默认去重规则---》这个不会提交多次---》只会提交一次--》有问题
    
    
    
    
# 源码分析--->scrapy.Spider
##### 起始爬取的地址
# 1 类属性中写了起始位置: start_urls = ["https://www.cnblogs.com"]
# 2 爬虫运行时,会执行类中得start_requests
 def start_requests(self) -> Iterable[Request]:
     for url in self.start_urls:
         yield Request(url, dont_filter=True)
            
######### 去重位置---from scrapy.core.scheduler import Scheduler
# 默认配置文件中有:
    DUPEFILTER_CLASS = "scrapy.dupefilters.RFPDupeFilter"
    class Scheduler:
        # 在排队之前---》先执行enqueue_request--》完成去重
        def enqueue_request(self, request: Request) -> bool:
            # self 是调度器
            #self.df 是去重类 RFPDupeFilter 的对象
            # 调用RFPDupeFilter中得方法request_seen,会返回True或False
            if self.df.request_seen(request): #返回True说明集合中有了
                return False # 说明不再爬取了
            dqok = self._dqpush(request)

            return True
        
    class RFPDupeFilter:
        def request_seen(self, request: Request) -> bool:
            # 16 进制字符串
            fp = self.request_fingerprint(request)
            #self.fingerprints 是 set() 集合
            if fp in self.fingerprints:
                return True # 如果在集合中,就返回True
            # 加入到集合中,返回False
            self.fingerprints.add(fp)
            return False
        def request_fingerprint(self, request: Request) -> str:
            return self.fingerprinter.fingerprint(request).hex() # 16进制 字符串
        
  # request_fingerprint 生成指纹
    # 请求回来的数据是完全一致的---》如果仅仅使用地址区分-->他们不是一个
    # 但是被request_fingerprint执行完后,它生成的16进制是一样的
    -www.cnblogs.com/?name=lqz&age=19
    -www.cnblogs.com/?age=19&name=lqz
    
    
# 总结:
1 去重是使用集合去重
    -高级在(生成的指纹):
        -get请求,参数如果一样,就是一样的
        -post请求,请求体不一样,就不一样
        
        
        
 # 代码
from scrapy.utils.request import RequestFingerprinter
from scrapy.http import Request
f=RequestFingerprinter()
res1=Request(url='http://www.cnblogs.com/',method='POST',body='name=lqz&age=19')
res2=Request(url='http://www.cnblogs.com/',method='POST',body='name=lqz&age=20')


res1_hex=f.fingerprint(res1)
res2_hex=f.fingerprint(res2)
print(res2_hex)
print(res1_hex)

布隆过滤器

# https://zhuanlan.zhihu.com/p/94668361


# 布隆过滤器是什么?
bloomfilter:是一个通过多哈希函数映射到一张表的数据结构,能够快速的判断一个元素在一个集合内是否存在,具有很好的空间和时间效率。(典型例子,爬虫url去重)

# 布隆过滤器原理
原理: BloomFilter 会开辟一个m位的bitArray(位数组),开始所有数据全部置 0 。当一个元素过来时,通过多个哈希函数(h1,h2,h3....)计算不同的在哈希值,并通过哈希值找到对应的bitArray下标处,将里面的值 0 置为 1

# 布隆过滤器可能存在误差
    -误差大小:由数组长度和hash函数绝对的
    -允许出现误差

# python 字典的key值必须可以 hash
    -不可变数据类型---》数字和字符串---》布尔,元组,对象
    -元组可以作为字典key
    

    
# 使用布隆过滤器  安装:pip3 install pybloom_live
# 布隆过滤器---》可以自动扩容---》规定错误率
# from pybloom_live import ScalableBloomFilter
#
# bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
#
# url = "www.cnblogs.com"
#
# url2 = "www.liuqingzheng.top"
#
# bloom.add(url)
#
# print(url in bloom)
#
# print(url2 in bloom)


# 布隆过滤器---》定长,不扩容
# BloomFilter 是定长的
from pybloom_live import BloomFilter
bf = BloomFilter(capacity=1000)
url = 'www.baidu.com'
bf.add(url)
print(url in bf)
print("www.liuqingzheng.top" in bf)


### redis可以实现布隆过滤器


## 布隆过滤器可以做什么?
    1 爬虫去重
    2 垃圾邮件过滤
    3 黑白名单
    4 布隆过滤器避免缓存击穿

 自定义去重规则(通过布隆过滤器)

# 集合去重,随着数据量越来越大---》很占空间
    sad  3个bytes
    aaa   3个bytes
    
    1个bytes就有8个格
    00100000   00100000 00100000 00000000   00000000 00000000
    
    
# 使用布隆过滤器实现去重
from scrapy.dupefilters import BaseDupeFilter
from scrapy.utils.request import RequestFingerprinter
from pybloom_live import ScalableBloomFilter


class MyPDupeFilter(BaseDupeFilter):
    fingerprints = ScalableBloomFilter(initial_capacity=100, error_rate=0.001,
                                       mode=ScalableBloomFilter.LARGE_SET_GROWTH)
    fingerprinter = RequestFingerprinter()

    def request_seen(self, request):
        print('zoule')
        fp = self.request_fingerprint(request)
        if fp in self.fingerprints:
            return True
        self.fingerprints.add(fp)
        return False

    def request_fingerprint(self, request) -> str:
        return self.fingerprinter.fingerprint(request).hex()

写去重类

from scrapy.dupefilters import BaseDupeFilter
from scrapy.utils.request import RequestFingerprinter
from pybloom_live import ScalableBloomFilter

from scrapy.dupefilters import RFPDupeFilter
class MyPDupeFilter(BaseDupeFilter):
    fingerprints = ScalableBloomFilter(initial_capacity=100, error_rate=0.001,
                                       mode=ScalableBloomFilter.LARGE_SET_GROWTH)
    fingerprinter = RequestFingerprinter()

    def request_seen(self, request):
        print('zoule--------------')
        fp = self.request_fingerprint(request)
        if fp in self.fingerprints:
            return True
        self.fingerprints.add(fp)
        return False

    def request_fingerprint(self, request) -> str:
        return self.fingerprinter.fingerprint(request).hex()

配置文件

DUPEFILTER_CLASS = "myfirstscrapy.MyPDupeFilter.MyPDupeFilter"

分布式爬虫

# 第三方的scrapy-redis 帮助我们实现分布式爬虫
# 什么是分布式爬虫
    10条数据要爬取
        一台机器爬10条
    3台机器爬10条数据
        1 台1--3条
        2 台4--7条
        3 台8--10条

# 实现分布式爬虫的核心
    #1、共享队列    schudler 多台机器共享同一个队列
    #2、同一套去重规则

    
# #### 使用步骤####
    0 下载:pip3 install scrapy-redis
    1 把之前爬虫类,继承class CnblogsSpider(RedisSpider):
    2 去掉起始爬取的地址,加入一个类属性
        redis_key = 'myspider:start_urls'  # redis列表的key,后期我们需要手动插入起始地址
        
     3 配置文件中配置
        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"  # scrapy redis去重类,使用redis的集合去重
        # 不使用原生的调度器了,使用scrapy_redis提供的调度器,它就是使用了redis的列表
        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        REDIS_HOST = 'localhost'                            # 主机名
        REDIS_PORT = 6379                                   # 端口
        ITEM_PIPELINES = {
       'mysfirstscrapy.pipelines.MyCnblogsMySqlPipeline': 301,
        'scrapy_redis.pipelines.RedisPipeline': 400,
    }
        
    4 在不同多台机器上运行scrapy的爬虫,就实现了分布式爬虫:在一台机器上开启多个程序即可
    5 写入到redis的列表中起始爬取的地址:列表key:myspider:start_urls
         rpush myspider:start_urls https://www.cnblogs.com
    
    
    
    
# 执行报错
    scrapy-redis(没有做好兼容)和scrapy(很新)的版本要对应
#  版本对应关系--》改scrapy-redis源码

 

标签:return,06days,self,request,spider,爬虫,scrapy
From: https://www.cnblogs.com/wzh366/p/18034439

相关文章

  • 高德百度地图商家数据查询爬虫采集工具
    南斗地图采集软件是基于百度地图采集、高德地图数据采集、360地图商家采集、Google地图采集、腾讯地图商家采集等多种模式数据采集最新商家信息,一键导出到CSV、EXCEL、VCF等文件。导入到手机通讯录体验入口==> http://map.nandou-china.com/......
  • 爬虫之css选择器
    用soup.select方法#panel节点内部的panel—heading节点print(soup.select('.panel.panel-heading'))#ul里面的liprint(soup.select('ulli'))#id为list-2的内部element节点print(soup.select('#list-2.element'))p......
  • 爬虫之bs4
    1.节点选择器都是Tag类型,直接调用节点名称可选择节点,调用string属性得到节点内容文本。2.提取信息获取名称#print(soup.title.name)获取属性#print(soup.p.attrs)#print(soup.p['name'])#print(soup.p['class'])获取内容print(soup.head.s......
  • 爬虫_05days ↑
    scrapy架构介绍#scrapy:爬虫框架---》使用scrapy创建爬虫项目#pipinstallscrapy#创建scrapy项目 scrapystartproject项目名#架构spiders:爬虫,主要是咱们写代码的地方---》设置起始爬取的地址--》解析数据engine:引擎,大总管,控制数据的整体流动scheduler:调度器,待爬取的地址......
  • 爬虫_04days
    自动登录cnblogs--获取cookieimporttimeimportjsonfromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.chrome.optionsimportOptions#####绕过浏览器检测到自动化软件控制options=Options()options.add_argum......
  • 爬虫简介
    爬虫分类:  通用爬虫:抓取系统重要组成部分,抓取的是一个整张页面的数据  聚焦爬虫:是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容  增量式爬虫:检测网站中数据更新的情况,指挥抓取网站中最新更新出来的数据爬虫的矛与盾:反爬机制  门户网站,可以通过制定相应......
  • 爬虫03_days
    selenium介绍#1由于requests不能执行js---》逐个分析ajax请求--》模拟发送获取数据 -使用requests爬取的数据很大概率跟在浏览器中看到的不一样-requests不能执行js#2seleniumselenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaS......
  • Scrapy爬虫框架
    一、安装Scrapypipinstallscrapy二、创建scrapy项目scrapystartprojectmy_spider三,创建爬虫文件scrapygenspiderexampleexample.com#example文件名称#example.com爬取网站地址#如:scrapygenspiderbaidubaidu.com出现下图就创建成功 四、运行刚才......
  • python 爬虫模板
    前言在我们写爬虫的时候,一般想要的数据都在详情页里面,一般代码进入详情页参数,需要首页里面寻找,所以爬这样的网站,需要定义一个模板我的模板如下: importrandomimporttimeimportrequestsfromauctionimportlogtoolfromauction.BaseCrawlerimportBaseCrawlercla......
  • python实战:用requests+做爬虫
    一,安装requests1,用pip安装(venv)liuhongdi@192news%pip3installrequests2,查看所安装库的版本:(venv)liuhongdi@192news%pip3showrequestsName:requestsVersion:2.31.0Summary:PythonHTTPforHumans.Home-page:https://requests.readthedocs.ioAu......