爬虫-(5)

news2025/1/16 17:06:59

内容概览

  • scrapy架构介绍
  • scrapy解析数据
  • settings相关配置
  • 持久化方案
  • 爬取全站文章
  • 爬虫中间件与下载中间件

scrapy架构介绍

在这里插入图片描述

# 引擎(ENGINE)
引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。有关详细信息,请参见上面的数据流部分。

# 调度器(SCHEDULER)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

# 下载器(DOWLOADER)
用于下载网页内容, 并将网页内容返回给ENGINE,下载器是建立在twisted这个高效的异步模型上的

# 爬虫(SPIDERS)--->在这里写代码
SPIDERS是开发人员自定义的类,用来解析responses,并且提取items,或者发送新的请求

# 项目管道(ITEM PIPLINES)
在items被提取后负责处理它们,主要包括清理、验证、持久化(比如存到数据库)等操作

# 下载器中间件(Downloader Middlewares)
位于Scrapy引擎和下载器之间,主要用来处理从ENGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到ENGINE的响应response,你可用该中间件做以下几件事:设置请求头,设置cookie,使用代理,集成selenium

# 爬虫中间件(Spider Middlewares)
位于ENGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)

scrapy解析数据

  • 方式一:使用bs4(不用了)
    import scrapy
    
    from bs4 import BeautifulSoup
    
    
    class CnblogsSpider(scrapy.Spider):
        name = 'cnblogs'
        allowed_domains = ['www.cnblogs.com']
        start_urls = ['http://www.cnblogs.com/']
    
        def parse(self, response):
            # response类似于requests模块的response对象
            print(response.text)  # 返回的数据
            # 解析数据
            soup = BeautifulSoup(response.text, 'lxml')
            article_list = soup.find_all(class_='post-item')  # 拿到所有文章
            for article in article_list:
                title_name = article.find(name='a', class_='post-item-title').text  # 获取文章标题
                print(title_name)
    
    
  • 方式二:scrapy自带的解析(css,xpath)
    import scrapy
    
    
    class CnblogsSpider(scrapy.Spider):
        name = 'cnblogs'
        allowed_domains = ['www.cnblogs.com']
        start_urls = ['http://www.cnblogs.com/']
    
        def parse(self, response):
            """css选择器"""
            article_list = response.css('article.post-item')
            for article in article_list:
                title_name = article.css('section>div>a::text').extract_first()
                author_img = article.css('section>div>p>a>img::attr(src)').extract_first()
                desc_list = article.css('section>div>p::text').extract()
                # print(desc_list)  # 有可能数据在列表的第二个位置,做数据处理
                desc = desc_list[0].replace('\n', '').strip()
                if not desc:
                    desc = desc_list[1].replace('\n', '').strip()
                author_name = article.css('section>footer>a>span::text').extract_first()
                article_date = article.css('section>footer>span>span::text').extract_first()
                print(f"""
                文章标题:{title_name}
                作者头像:{author_img}
                文章摘要:{desc}
                作者名字:{author_name}
                发布日期:{article_date}
                """)
    
            """xpath选择器"""
            article_list = response.xpath('//article[contains(@class,"post-item")]')
            for article in article_list:
                title_name = article.xpath('./section/div/a/text()').extract_first()
                author_img = article.xpath('./section/div/p//img/@src').extract_first()
                desc_list = article.xpath('./section/div/p/text()').extract()
                desc = desc_list[0].replace('\n', '').strip()
                if not desc:
                    desc = desc_list[1].replace('\n', '').strip()
                author_name = article.xpath('./section/footer/a/span/text()').extract_first()
                article_date = article.xpath('./section/footer/span/span/text()').extract_first()
                print(f"""
                        文章标题:{title_name}
                        作者头像:{author_img}
                        文章摘要:{desc}
                        作者名字:{author_name}
                        发布日期:{article_date}
                        """)
    
    
  • 总结
    1 response对象有css方法和xpath方法
    	-css中写css选择器
        -xpath中写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()        取所有
    

settings相关配置

基础配置

#1  是否遵循爬虫协议
ROBOTSTXT_OBEY = False
#2 LOG_LEVEL 日志级别
LOG_LEVEL='INFO'  # 报错如果不打印日志,在控制台看不到错误

# 3 USER_AGENT
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'

# 4 DEFAULT_REQUEST_HEADERS 默认请求头
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

# 5 SPIDER_MIDDLEWARES 爬虫中间件
#SPIDER_MIDDLEWARES = {
#    'cnblogs.middlewares.CnblogsSpiderMiddleware': 543,
#}
# 6 DOWNLOADER_MIDDLEWARES  下载中间件
#DOWNLOADER_MIDDLEWARES = {
#    'cnblogs.middlewares.CnblogsDownloaderMiddleware': 543,
#}

# 7 ITEM_PIPELINES 持久化配置
#ITEM_PIPELINES = {
#    'cnblogs.pipelines.CnblogsPipeline': 300,
#}


#8 爬虫项目名字
BOT_NAME = 'myfirstscrapy'

#9 指定爬虫类的py文件的位置
SPIDER_MODULES = ['myfirstscrapy.spiders']
NEWSPIDER_MODULE = 'myfirstscrapy.spiders'

增加爬虫效率的配置

#1 增加并发:不设置默认16
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改
CONCURRENT_REQUESTS = 100
值为100,并发设置成了为100#2 降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:
LOG_LEVEL = 'ERROR'

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

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

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

持久化方案

持久化:保存在硬盘上
有两种方式,第二种方式常用

  • 第一种
    """解析函数中parse,要return [{},{},{}]"""
    def parse(self, response):
        """xpath选择器"""
        data_list = []
        article_list = response.xpath('//article[contains(@class,"post-item")]')
        for article in article_list:
            title_name = article.xpath('./section/div/a/text()').extract_first()
            author_img = article.xpath('./section/div/p//img/@src').extract_first()
            desc_list = article.xpath('./section/div/p/text()').extract()
            desc = desc_list[0].replace('\n', '').strip()
            if not desc:
                desc = desc_list[1].replace('\n', '').strip()
            author_name = article.xpath('./section/footer/a/span/text()').extract_first()
            article_date = article.xpath('./section/footer/span/span/text()').extract_first()
            print(f"""
                    文章标题:{title_name}
                    作者头像:{author_img}
                    文章摘要:{desc}
                    作者名字:{author_name}
                    发布日期:{article_date}
                    """)
            data_list.append(
                {'文章标题': title_name, '作者头像': author_img, '文章摘要': desc, '作者名字': author_name, '发布日期': article_date, })
        return data_list
    
    """scrapy crawl cnblogs -o 文件名(json,pickle,csv结尾)"""
    scrapy crawl cnblogs -o a.csv
    
  • 第二种:使用pipline;常用的,管道形式,可以同时存到多个位置的
    """
    1 在items.py中写一个类[相当于写django的表模型],继承scrapy.Item
    2 在类中写属性,写字段,所有字段都是scrapy.Field类型
    """
    import scrapy
    
    class CnblogsItem(scrapy.Item):
        title_name = scrapy.Field()
        author_img = scrapy.Field()
        desc = scrapy.Field()
        author_name = scrapy.Field()
        article_date = scrapy.Field()
    
    """3 在爬虫中导入类,实例化得到对象,把要保存的数据放到对象中"""
    import scrapy
    
    from myfirstscrapy.items import CnblogsItem
    
    
    class CnblogsSpider(scrapy.Spider):
        name = 'cnblogs'
        allowed_domains = ['www.cnblogs.com']
        start_urls = ['http://www.cnblogs.com/']
    
        def parse(self, response):
            # item = CnblogsItem()  # 外面定义,会有问题
            article_list = response.xpath('//article[contains(@class,"post-item")]')
            for article in article_list:
                item = CnblogsItem()  # 定义在for内部,每次都是一个新对象
                title_name = article.xpath('./section/div/a/text()').extract_first()
                author_img = article.xpath('./section/div/p//img/@src').extract_first()
                desc_list = article.xpath('./section/div/p/text()').extract()
                desc = desc_list[0].replace('\n', '').strip()
                if not desc:
                    desc = desc_list[1].replace('\n', '').strip()
                author_name = article.xpath('./section/footer/a/span/text()').extract_first()
                article_date = article.xpath('./section/footer/span/span/text()').extract_first()
                item['title_name'] = title_name
                item['author_img'] = author_img
                item['desc'] = desc
                item['author_name'] = author_name
                item['article_date'] = article_date
                yield item
    
    """4 修改配置文件,指定pipline,数字表示优先级,越小越大"""
    ITEM_PIPELINES = {
       'myfirstscrapy.pipelines.CnblogsMysqlPipeline': 200,
       'myfirstscrapy.pipelines.CnblogsFilesPipeline': 300,
    }
    
    """
    5 写一个pipline:CrawlCnblogsPipeline
    -open_spider:数据初始化,打开文件,打开数据库链接
    -process_item:真正存储的地方
        -一定不要忘了return item,交给后续的pipline继续使用
    -close_spider:销毁资源,关闭文件,关闭数据库链接
    """
    import pymysql
    
    class CnblogsFilesPipeline:
    def open_spider(self, spider):
        self.f = open('cnblogs.txt', 'at', encoding='utf-8')
    
    def process_item(self, item, spider):
        self.f.write(
            f'文章标题:{item["title_name"]},文章摘要:{item["desc"]},文章作者:{item["author_name"]},作者头像:{item["author_img"]},发布日期:{item["article_date"]}\n')
        return item
    
    def close_spider(self, spider):
        self.f.close()
    
    class CnblogsMysqlPipeline:
    def open_spider(self, spider):
        self.conn = pymysql.connect(
            user='root',
            password='123',
            host='127.0.0.1',
            database='cnblogs',
            port=3306,
            autocommit=True,
        )
        self.cursor = self.conn.cursor()
    
    def process_item(self, item, spider):
        self.cursor.execute(
            'insert into article (title_name, author_img, `desc`, author_name,article_date) values (%S,%S,%S,%S,%S)',
            args=[item['title_name'], item['author_img'], item['desc'], item['author_name'], item['article_date']])
        return item
    
    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()
    

爬取全站文章

"""
上边已经爬取完第一页,数据也保存了
接下来要做两件事:
	1. 继续爬取下一页:解析出下一页的地址,包装成request对象
	2. 继续爬取详情页:解析出详情页地址,包装成request对象
"""

request与response对象传递参数

# Request创建:在parse中,for循环中,创建Request对象时,传入meta
	yield Request(url=url, callback=self.detail_parse,meta={'item':item})
    
# Response对象:detail_parse中,通过response取出meta取出item,把文章详情写入
	yield item

解析下一页并继续爬取

import scrapy

from scrapy import Request
from myfirstscrapy.items import CnblogsItem


class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'
    allowed_domains = ['www.cnblogs.com']
    start_urls = ['http://www.cnblogs.com/']
    
    def parse(self, response):
        """xpath选择器"""
        # item = CnblogsItem()  # 外面定义,会有问题
        article_list = response.xpath('//article[contains(@class,"post-item")]')
        for article in article_list:
            item = CnblogsItem()  # 定义在for内部,每次都是一个新对象
            title_name = article.xpath('./section/div/a/text()').extract_first()
            author_img = article.xpath('./section/div/p//img/@src').extract_first()
            desc_list = article.xpath('./section/div/p/text()').extract()
            desc = desc_list[0].replace('\n', '').strip()
            if not desc:
                desc = desc_list[1].replace('\n', '').strip()
            author_name = article.xpath('./section/footer/a/span/text()').extract_first()
            article_date = article.xpath('./section/footer/span/span/text()').extract_first()
            url = article.xpath('./section/div/a/@href').extract_first()
            item['title_name'] = title_name
            item['author_img'] = author_img
            item['desc'] = desc
            item['author_name'] = author_name
            item['article_date'] = article_date
            item['url'] = url
            # yield item  # 先不存,因为数据不全
            yield Request(url=url, callback=self.detail_parse, meta={'item': item})  # 详情页面,使用detail_parse解析

        next_url = 'https://www.cnblogs.com' + response.css('div.pager>a:last-child::attr(href)').extract_first()
        yield Request(url=next_url, callback=self.parse)  # callback默认就是当前方法,可以不填

    def detail_parse(self,response):
        item = response.meta.get('item')  # 获取到上面传入的item
        article_content = response.css('div.post').extract_first()  # 解析文章详情
        item['article_content'] = str(article_content)  # 把详情写入当前meta中的item中
        # print(item)
        yield item  # 保存

爬虫中间件与下载中间件

# scrapy的所有中间件都写在middlewares.py中,跟djagno非常像,做一些拦截

# 爬虫中间件(用的很少,了解即可)
	MyfirstscrapySpiderMiddleware
        def process_spider_input(self, response, spider): # 进入爬虫会执行它
        def process_spider_output(self, response, result, spider): #从爬虫出来会执行它
        def process_spider_exception(self, response, exception, spider):#出了异常会执行
        def process_start_requests(self, start_requests, spider):#第一次爬取执行
        def spider_opened(self, spider): #爬虫开启执行
# 下载中间件
	MyfirstscrapyDownloaderMiddleware
        def process_request(self, request, spider): # request对象从引擎进入到下载器会执行
    	def process_response(self, request, response, spider):# response对象从下载器进入到引擎会执行
    	def process_exception(self, request, exception, spider):#出异常执行它
    	def spider_opened(self, spider): #爬虫开启执行它
            
            
        #重点:process_request,process_response

        
# 下载中间件的process_request
	-返回值:
        - return None: 继续执行下面的中间件的process_request
        - return a Response object: 不进入下载中间件了,直接返回给引擎,引擎把它通过6给爬虫
        - return a Request object:不进入中间件了,直接返回给引擎,引擎把它放到调度器中
        - raise IgnoreRequest: process_exception() 抛异常,会执行process_exception

# 下载中间件的process_response
	-返回值:
       - return a Response object:正常,会进入到引擎,引擎把它给爬虫
       - return a Request object: 会进入到引擎,引擎把它放到调度器中,等待下次爬取
       - raise IgnoreRequest     会执行process_exception

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/70005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SLURM批处理调度系统介绍

SLURM (Simple Linux Utility for Resouce Management) 非常成熟的开源社区集群批处理调度系统。 介绍 2008年左右起源于LLNL实验室, 最新版本20.11开源软件(约50万行c源码),开发和发布一直活跃用户群: Top10里有5家使用(天河HPC是其一)商业公司SchedMD提供顾问咨…

经典算法之LRU算法

一、理论 LRU算法算是个常见的算法,很有必要去了解它,现在我们就来看看什么是 LRU LRU 的全称是 Least Recently Used(最近最少使用),就如它的含义一样,最近最少使用的。在实际的场景中大多会把它当作一种…

CAN总线学习笔记 | STM32CubeMX配置CAN环回测试

CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试 一、STM32CubeMX配置 CAN是挂载在APB1总线上,设置PCLK1时…

Flink系列之Flink中四层Graph详解

title: Flink系列 四、Flink Runtime 四层 Graph 详解 首先回顾一下 Flink 的整体架构设计: {% asset_img processes.svg %} 关于上图中的一些概念的解释: 1、DataFlow Graph 是一个逻辑概念,表示这个应用程序的一个执行图,事…

开关电源环路笔记(11)-TL431电路的几个电阻的取值

上次文章发出来后,有兄弟留言布置了作业,让说说几个电阻的取值,就是下图的Rled,Rbias,R1,Rlower等。那么就写写吧,毕竟,这个电路确实用得非常多,实际工作中确实也需要知道…

登录信息记录模块实现(UserAgentUtils)

如果文章对你有帮助欢迎【关注❤️❤️❤️点赞👍👍👍收藏⭐⭐⭐】一键三连!一起努力! 一、UserAgentUtils简介 user-agent-utils 是一个用来解析 User-Agent 字符串的 Java 类库。 其能够识别的内容包括&#xff1a…

32_STM32内部温度传感器实验

目录 内部温度传感器简介 STM32ADC对应引脚 内部温度传感器使用注意使用事项 开启内部温度传感器步骤 实验源码 内部温度传感器简介 内部温度传感器框图 从图上可以看出温度传感器可通过TSVREFR控制位连接到ADC的固定通道16,温度的值最终肯定是被转换成电压值,电压值然后通…

学会python这十个语言技法,让你有上手风范

如何让你的代码更有python味? 大部分编程语言都有共性,也有个性。下手害怕个性,视为异端,抵触之;上手善用个性,欣欣然,妙用之。 1、三元表达式 别抱怨python没有三元表达式,请看&…

《大数据导论》之数据的概念、类型和组织形式

目录 说在前面 一、数据的概念 二、数据类型 ​三、数据组织形式 说在前面 大数据时代悄然来临,带来了整个信息技术发展的巨大变革,并深刻影响着社会生产和人们生活的方方面面。全球范围内,世界各国政府也非常重视整个大数据的研究和产业…

hadoop pig-0.17.0 安装配置

下载Apache Pig 首先,从以下网站下载最新版本的Apache Pig,下载Pig步骤取自W3C:Pig安装教程 步骤1 打开Apache Pig网站的主页。在News部分下,点击链接release page(释放),如下面的快照所示。 步…

解决git中出现的“bash syntax error near unexpected token ’(‘”错误

今天来分享一篇关于我在git使用过程中出现的一个错误。 错误信息: bash: syntax error near unexpected token (’ 翻译过来就是提示我在’(这里有错误,而这个错误是我在使用git commit提交时候产生的,我当时是这么写的: git c…

java中Optional的应用,以及map和flatMap的区别

关于Option的介绍可以看深入理解java8中的Optional 类就可以了,但是复杂一点的使用在网上却没有搜到,这里结合我开发时遇到的真实案例来讲一下Option的使用。 1.案例一 在真实业务操作过程中,都是对象里面套对象,这边先简单定义操…

flink window 框架详细解读

1. dataStream window group window time window 基于时间驱动 滚动时间窗口 tumble time window 滑动时间窗口 sliding time window 会话时间窗口 session time window count window 基于数据驱动 滚动计数窗口 tumble count window 滑动计数窗口 sliding count window 2. s…

皕杰报表之雷达图

雷达图(又可称为戴布拉图、螂蛛网图)是数据分析报表的一种。即将各项数据分析所得的数字或比率,就其比较重要的项目集中划在一个图表上,来表现一组数据的各项数据比率的情况,使用者能一目了然的了解这个数据的指标的变…

LeetCode中等题之通过最少操作次数使数组的和相等

题目 给你两个长度可能不等的整数数组 nums1 和 nums2 。两个数组中的所有值都在 1 到 6 之间(包含 1 和 6)。 每次操作中,你可以选择 任意 数组中的任意一个整数,将它变成 1 到 6 之间 任意 的值(包含 1 和 6&#…

计算机组成大题分析(三)

假定计算机 M 字长为 16 位,按字节编址,连接 CPU 和主存的系统总线中地址线为 20 位、数据线为 8位,采用 16 位定长指今字,指令格式及其说明如下: 其中,op1-op3 为操作码,rs,t 和 rd 为通用寄存…

【计算机视觉+CNN】keras+ResNet残差网络实现图像识别分类实战(附源码和数据集 超详细)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 一、深度卷积神经网络模型结构 1:LeNet-5 LeNet-5卷积神经网络首先将输入图像进行了两次卷积与池化操作,然后是两次全连接层操作,最后使用Softmax分类器作为多分类输出,它对…

MySQL插入汉字报错的解决方案

MySQL插入汉字报错的原因是字符集的问题,MySQL默认使用的是Latin(拉丁文)字符集,可以在创建数据库时指定其字符集:CREATE DATABASE test DEFAULT CHARACTER SET utf8 或者修改MySQL的配置文件,可以参考以下…

Qt事件循环嵌套,BlockingQueuedConnection与QWaitCondition比较

前言: 之前写过有关事件循环和条件变量的博客: Qt使用事件循环,信号,stop变量,sleep阻塞,QWaitConditionQMutex条件变量,退出子线程工作_大橘的博客-CSDN博客_qt stop函数 Qt事件循环&#x…

Unity3D导出Android工程中使用并交互

, 目录 1,版本信息 2,前期准备 Unity方面: Android方面: 3,Android与Unity3D交互 1,版本信息 unity2020 android studio 2021 *不要用android studio 2020系列,存在不能导入Library的b…