3.用python写网络爬虫,下载缓存

news2024/12/26 12:47:26

目录

3.1 为链接爬虫添加缓存支持

3.2 磁盘缓存

3.2.1 实现

3.2.2缓存测试

 3.2.3节省磁盘空间

3.2.4 清理过期数据

 3.2.5缺点

 3.3 数据库缓存

3.3.1 NoSQL 是什么

3.3.2 安装 MangoDB

 3.3.3 MongoDB 概述

3.3.4 MongoDB 缓存实现

3.3.5 压缩 

3.3.6 缓存测试

3.4 本章小结


3.1 为链接爬虫添加缓存支持

要想支持缓存 ,我们需要修改第1章中编写的download函数,使其在要想支持缓存 ,我们需要修改第1章中编写的download函数,使其在URL下载之前进行缓存检查 。另外,我们还需要把限速功能移至函数内部,只有在真正发生下载时才会触发限速 ,而在加载缓存时不会触发 。为了避免每次下载都要传入多个参数,我们借此机会将download函数重构为一个类,这样参数只需在构造方法中设置一次 ,就能在后续下载时多次复用 。下面是支持了缓存功能的代码实现。
class Downloade r :
        de f 一_init_ ( s elf, delay= S ,
               user_agent= ’ wswp ’ , proxies=None,
                   num retries=! , cache=N。ne ) :
               s elf. throttle = Throttle ( delay)
               self . us er_agent = user_agent
               s e l f . proxies = proxies
               s e l f . num retries = num retries
               s e l f . cache = cache
     de f_call_( s elf, url ) :
           result = None
           if self.cache :
                try :        
                      result = se lf. cache [ url ]
               except KeyError :
                       #  url is not ava i l able in cache
                       pass
              else:
                     if self. num retries > 0 and \
                          500 <= result [ ’ code ’ l < 600 :
                          #  s erver e rror so ignore result from cache
                          #  and re-down load
                          result = N.ne
           if result is N.ne :
              # result was not loaded from cache
              # so still need to download
              self . throttle . wait ( ur l )
              proxy = random . choice ( s elf . proxi e s) if s e lf. proxies
                   else None
              headers = { ’ User-agent ’ : s el f . user_agent }
              result = s el f . download ( url , headers , pr。xy,
                   self . num retries )
             if self . cache :
                # s ave result to cache
                self . cache [ url ] = result
           return result [ ’ html ’ ]
def downl oad (self, url , headers , proxy , num_retr ies ,
      data=None ) :
return { ’ html ’ : html ,
前面代码中的Download类有一个比较有意思的部分 ,那就是 _call_ 特殊方法 ,在该方法中 我们实现了下载前检查缓存的功能 。该方法首先会检查缓存是否已经定义 。如果已经定义, 则检查之前是否已经缓存了该URL。如果该URL己被缓存 ,则检查之前的下载中是否遇到了服务端错误 。最后 ,如果也没有发生过服务端错误 ,则表明该缓存结果可用 。如果上述检查 中 的 任何一项失败, 都需要正常下载该 URL,然后将得到的结果添加到缓存中 。这里的 download方法和之前的download函数基本一样 ,只是在返回下载的HTML时额外返回了 HTTP态码,以便在缓存中存储错误码 。当然 ,如果你只需要一个简单的下载功能,而不需要限速或缓存的话,可以直接调用 该方法,这样就不会通过 _call_ 方法调用了 。

3.2 磁盘缓存

要想缓存下载结果 ,我们先来尝试最容易想到的方案 ,将下载到的网页存储到文件系统中 。为了 实现该功能 ,我们需要将URL安全地映射为跨平台的文件名 。表 3.1所示为几大主流文件系统的 限制 。
为了保证在不同文件系统中 ,我们的文件路径都是安全的 ,就需要限制其只能包含数字 、字母和基本符号 ,并将其他字符替换为下划线 ,其实现代码
           >>>import re
           >>> url = ’ http:/ /example . web s craping . c。m/de fault /view/
                  Australia- 1’
           >>> re . sub ( ’ [ 《 /0-9a-zA- Z\-]’ ,'_' , url )
'http_//example . webs c raping . com/default /view/Australia- 1 ’

3.2.1 实现

介绍一下了创建基于磁盘的缓存时需要考虑的文件系统限制 ,包括允许使用哪些字符、文件名长度限制 ,以及确保文件和目录的创建位置不同。把URL到文件名的这些映射逻辑结合起来 ,就形成了磁盘缓存的主要部分。

 在上面的代码中 ,构造方法传入了 一个用于设定缓存位置的参数 ,然后在urltopath方法中应用了前面讨论的文件名限制 。现在 ,我们还缺少根据文件名存取数据的方法 ,下面的代码实现了这两个缺失的方法 。

 

在一setitern ( )一中,我们使用 u r l _to _pa th ( ) 方法将 URL 映射为 安全文件名 ,在必要情况下还需要创建父目录 。这里使用的pickle模块会把输入转化为字符串 ,然后保存到磁盘中 。而在_getitem_ ()方法中 ,首先将URL映射为安全文件名 。然后 ,如果文件存在 ,则加载其内容,并执行反序列化,恢复其原始数据类型 :如果文件不存在 ,则说明缓存中还没有该URL的 数据 ,此时会抛出KeyError异常。

3.2.2缓存测试

现在 ,我们通过向爬虫传递cache回调 ,来检验DiskCache类 。该类的完整源代码可以从https : / /bitbucket .org/wswp/code/src/tip/chapter03/diskcache.py获取 。我们可以通过执行如下脚本,使用链接爬虫测试磁盘缓存 。
第一次执行该命令时,由于缓存为空,因此网页会被正常下载。但当我们 第二次执行该脚本时,网页加载自缓存中,爬虫应该更快完成执行,其执行 结果如下所示 。

 

和上面的预期一样,爬取操作很快就完成了。当缓存为空时,我的计算机中的爬虫下载耗时超过23 分钟1 而在第二次全部使用缓存时,该耗时只有0.186秒( 比第一次爬取快了超过7000倍。由于硬件的差异 ,在不同的计算机中的准确执行时间也会有所区别 。不过毋庸置疑的是,磁盘缓存速度更快。

 3.2.3节省磁盘空间

为最小化缓存所需的磁盘空间,我们可以对下载得到的HTML文件进行压缩处理。处理的实现方法很简单,只需在保存到磁盘之前使用zlib压缩序列化字符串即可,如下面的代码所示 。

而从磁盘加载后解压的代码如下所示 。

returnpickle.loads(zlib.decompress(fp.read()))  

压缩完所有网页之后,缓存大小从 4.4MB下降到2.3MB,而在我的计算机上爬取缓存示例网站的时间是0.212秒,和未压缩时的0.186秒相比只是略有增加。当然,如果你的项目对速度十分敏感的话 ,也可以禁用压缩功能。

3.2.4 清理过期数据

当前版本的磁盘缓存使用键值对的形式在磁盘上保存缓存,未来无论何时 请求都会返回结果。对于缓存网页而言该功能可能不太理想,因为网页内 容随时都有可能发生变化,存储在缓存中的数据存在过期风险。本节中,我 们将为缓存数据添加过期时间,以便爬虫知道何时需要重新下载网页。在缓 存网页时支持存储时间戳的功能也很简单, 如下面的代码所示 。

 

在构造方法中,我们使用timedelta对象将默认过期时间设置为30天。然后,在_set一方法中,把当前时间戳保存到序列化数据中:而在_get一 方法中,对比当前时间和缓存时间,检查是否过期 为了测试过期时间功能,我们可以将其缩短为5秒,如下所示 。

 

和预期一样,缓存结果最初是可用的经过5秒的睡眠之后,再次调用同-URL,则会抛出KeyError异常,也就是说缓存下载失效了

 3.2.5缺点

基于磁盘的缓存系统比较容易实现,无须安装其他模块,并且在文件管理 器中就能查看结果。但是,该方法存在一个缺点即受制于本地文件系统 的 限制 。本章早些时候,为了将URL映射为安全文件名,我们应用了多种限制,然而这又会引发另一个问题,那就是一些URL会被映射为相同的文件名 。比如,在对如下几个URL进行字符替换之后就会得到相同的文件名 。
这就意味着,如果其中一个URL生成了缓存,其他3个URL也会被认为 已经生成缓存,因为它们映射到了同一个文件名。另外,如果一些长URL只 在255个字符之后存在区别,截断后的版本也会被映射为相同的文件名。这 个问题非常重要,因为U虹的最大长度并没有明确限制尽管在实践中U肚 很少会超过2000个字符,且早期版本的IE浏览器也不支持超过2083个字 符的U虹。避免这些限制 的 一种解决方案是使用URL的哈希值作为文件名尽管该方法可以带来一定改善,但是最终还是会面临许多文件系统具有的一个关键问题,那就是每个卷和每个目录下的文件数量是有限制的。如 果缓存存储在FAT32 文件系统中,每个目录的最大文件数是65535。该限制可以通过将缓存分割到不同目录来避免,但是文件系统可存储的文件总数也是有限制的。我使用的ext4分区目前支持略多于1500万个文件,而一个大型网站往往拥有超过1亿个网页。很遗憾 ,DiskCache方法想要通用的话存在太多限制。要想避免这些问题,我们需要把多个缓存网页合并到一个文件中,并使用类似B+树的算法进行索引。我们并不会自己实现这种算法,而是在下一节中介绍己实现这类算法的数据库 。

 3.3 数据库缓存

为了避免磁盘缓存方案的己知限制,下面我们会在现有数据库系统之上创建缓存。爬取时,我们可能需要缓存大量数据,但又无须任何复杂的连接操作,因此我们将选用NoSQL数据库,这种数据库比传统的关系型数据库更易于扩展。在本节中,我们将会选用目前非常流行的MongoDB作为缓存数据库 。

3.3.1 NoSQL 是什么

NoSQL全称为Not OnlySQL,是一种相对较新的数据库设计方式。传统的关系模型使用的是固定模式,并将数据分割到各个表中。然而,对于大数据集的情况,数据量太大使其难以存放在单一服务器中,此时就需要扩展到多台服务器不过,关系模型对于这种扩展的支持并不够好,因为在查询多 个表时,数据可能在不同的服务器中。相反,NoSQL数据库通常是无模式的,从设计之初就考虑 了跨服务器无缝分片的问题。在NoSQL中,有多种方式可以实现该目标,分别是列数据存储( 如 HBase )、键值对存储( 如 Redis )、面向文档的数据库(如MongoDB)以及图形数库( 如 Neo4j )。

3.3.2 安装 MangoDB

MongoDB可以从https://www.mongodb.org/downloads下载得到。然后,我们需要使用如下命令外安装其Python封装库。

 3.3.3 MongoDB 概述

下面是通过 MongoDB 存取数据 的 示例代码 。

上面的例子存在一个问题, 那就是如果我们对相 同 的 URL 插入另一条不 同 的文档时, MongoDB 会欣然接受并执行这次插入操作, 其执行过程如下所示。

 

 

此时,同一URL下出现了多条记录,但我们只关心最新存储的那条数据。为了避免重复,我们将 ID设置为URL,并执行upsert操作。该操作表示当记录存在时更新记录,否则插入新记录,其代码如下所示 。

现在,当我们尝试向同一URL插入记录时,将会更新其内容,而不是创建冗余的数据,如下面的代码所示。

 

可以看出,在添加了这条记录之后,虽然HTML的内容更新了,但该URL的记录数仍然是 1 。

3.3.4 MongoDB 缓存实现

现在我们己经准备好创建基于MongoDB的缓存了,这里使用了和之前的DiskCache类相同的类接 口。

 

 

3.3.5 压缩 

为了使数据库缓存与之前的磁盘缓存功能一致,我们最后还要添加一个功能:压缩。其实现方法和磁盘缓存相类似,即序列化数据后使用 zlib 库进行压缩,如下面的代码所示 。

3.3.6 缓存测试

MongoCache类的源码可以从https://bitbucket.org/wswp/code/src/tip/chapter03/mongocache.py 获取,和DiskCache一样,这里我们依然通过执行该脚本测试链接爬虫 。

 

 

可以看出,加载数据库缓存的时间几乎是加载磁盘缓存的两倍。不过,MongoDB可以让我们免受文件系统的各种限制,还能在下一章介绍的并发爬虫处理中更加高效 。

3.4 本章小结

本章中,我们了解到缓存己下载的网页可以节省时间,并能最小化重新爬取网站所耗费的带宽 。 缓存的主要缺点是会占用磁盘空间,不过我们可以使用压缩的方式减少空间占用。此外,在类似 MongoDB 等现有数据库的基础之上创建缓存,可以避免文件系统的各种限制 。

 

 

 

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

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

相关文章

变分自编码(VAE,Variational Auto-Encoder)知识点速览

目录 1. 主要思想 2. 训练和推理过程 3. 编码器和解码器的结构 4. 主要用途 5. 相较于 auto-encoder 的优劣 1. 主要思想 变分自编码器&#xff08;Variational AutoEncoder&#xff0c;简称VAE&#xff09;是一种生成模型&#xff0c;它通过对数据的隐含表示&#xff08;l…

红日靶场(一)外网到内网速通

红日靶场&#xff08;一&#xff09; 下载地址&#xff1a;http://vulnstack.qiyuanxuetang.net/vuln/detail/2/ win7:双网卡机器 win2003:域内机器 win2008域控 web阶段 访问目标机器 先进行一波信息收集&#xff0c;扫一下端口和目录 扫到phpmyadmin&#xff0c;还有一堆…

韶音和cleer、南卡对比哪个好?韶音、南卡、Cleer开放式耳机横评

现如今&#xff0c;不管是通勤路上还是在家休闲娱乐&#xff0c;又或者是运动时&#xff0c;经常能看见很多人佩戴着耳机听音乐&#xff0c;但是&#xff0c;经常佩戴耳机听音乐的小伙伴都知道&#xff0c;入耳式耳机佩戴久了&#xff0c;容易造成耳部酸痛感、胀痛感&#xff0…

Android 安卓开发语言kotlin与Java该如何选择

一、介绍 如今在Android开发中&#xff0c;应用层开发语言主要是Java和Kotlin&#xff0c;Kotlin是后来加入的&#xff0c;主导的语言还是Java。kotlin的加入仿佛让会kotlin语言的开发者更屌一些&#xff0c;其实不然。 有人说kotlin的引入是解决开发者复杂的逻辑&#xff0c;并…

【VSCode】设置关键字高亮的插件 | Highlight Word

目录 一、概述二、安装 highlight-words 插件三、配置 highlight-words 插件3.1 默认配置3.2 修改 settings.json 配置文件 四、设置高亮快捷键F8五、效果演示 一、概述 本文主要介绍在 VSCode 看代码时&#xff0c;怎样使某个单词高亮显示&#xff0c;主要通过以下三步实现&am…

Docker的run流程

底层原理 Docker怎么工作&#xff1f; Docker为什么比VM虚拟机块&#xff1f; 1.Docker有比虚拟机更少的抽象层 2.docker利用的是宿主机的内核&#xff0c;vm需要是Guest OS 所以说&#xff0c;新建一个容器的时候&#xff0c;docker不需要像虚拟机一样加载一个系统内核&am…

[conda]tf_agents和tensorflow-gpu安装傻瓜式教程

1.打开终端或Anaconda Prompt&#xff08;Windows用户&#xff09;。 2.输入以下命令创建新的Python环境&#xff1a; conda create --name <env_name> python<version>其中&#xff0c;<env_name>是您想要创建的环境名称&#xff0c;<version>是您想…

保留纵向连续性的迭代次数估算方法

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有3个节点&#xff0c;训练集AB各由5张二值化的图片组成&#xff0c;让B全是0&#xff0c;让差值结构中有6个1.其中有3组 差值结构 A-B 迭代次数 行分布 列分布 0 1 1 0 1 1 3*5*1*2*0-0*0*0*0*0 3977.834 0 1 …

springboot中自定义JavaBean返回的json对象属性名称大写变小写问题

文章目录 springboot中自定义JavaBean返回的json对象属性名称大写变小写问题一、继承类二、手动添加Get方法三、JsonProperty四、spring-boot json(jackson)属性命名策略 springboot中自定义JavaBean返回的json对象属性名称大写变小写问题 开发过程中发现查询返回的数据出现自…

模拟电路系列分享-运放的关键参数3

目录 概要 整体架构流程 技术名词解释 1.输入电压范围 2.优劣范围: 3.理解 技术细节 1.共模抑制比 2.优劣范围 3.理解 小结 概要 提示&#xff1a;这里可以添加技术概要 实际运放与理想运放具有很多差别。理想运放就像一个十全十美的人&#xff0c;他学习100 分&#xff0c;寿…

chatgpt赋能python:Python中的相加功能函数:介绍、应用和示例

Python中的相加功能函数&#xff1a;介绍、应用和示例 Python是一个功能强大的编程语言&#xff0c;拥有许多强大的内置函数和模块。其中一个非常常见的功能是相加或者加法操作。让我们看一下Python中的相加功能函数。 什么是相加&#xff1f; 简而言之&#xff0c;相加是将…

6.17、进程与线程

比如&#xff0c;一边游戏&#xff0c;一边qq聊天&#xff0c;一边听歌&#xff0c;怎么设计&#xff1f; 进程 进程&#xff08;process&#xff09;&#xff1a;程序的一次执行过程&#xff0c;或是正在内存中运行的应用程序。如&#xff1a;运行中的QQ&#xff0c;运行中…

二叉树的基本操作(如何计算二叉树的结点个数,二叉树的高度)

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

内网隧道代理技术(八)之Python 反弹Shell

Python 反弹Shell Python介绍 Python由荷兰数学和计算机科学研究学会的吉多范罗苏姆于1990年代初设计&#xff0c;作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构&#xff0c;还能简单有效地面向对象编程。Python语法和动态类型&#xff0c;以及解释型语言的本…

月薪2万,被新同事15秒气走。

今年&#xff0c;AIGC掀起了巨浪&#xff0c;身边不少人感到前所未有的焦虑&#xff1a; 朋友圈好友晒出的AI美图&#xff0c;仅需15秒&#xff0c;竟比我2周的设计更出色&#xff1b; 公司用AI写的文案&#xff0c;转化率提升了10%&#xff0c;可能要优化人员了; 职场危机提前…

Boost序列化全解析

程序开发中&#xff0c;序列化是经常需要用到的。像一些相对高级语言&#xff0c;比如JAVA, C#都已经很好的支持了序列化&#xff0c;那么C呢&#xff1f;当然一个比较好的选择就是用Boost&#xff0c;这个号称C准标准库的东西。 什么时候需要序列化呢&#xff1f;举个例子&am…

可视化的工时管理:让项目进度真实可见

在现代项目管理中&#xff0c;工时表软件作为一种强大而有效的工具&#xff0c;能够帮助团队更好地管理项目进度。无论是大小型项目&#xff0c;正确使用工时表软件都可以提高团队的效率和项目的可追踪性。本文将介绍一些关键步骤&#xff0c;以帮助企业利用工时表软件来管理项…

【计算机图形学】期末总结大全,建议收藏

文章目录 一、图形学及其研究内容二、图形的输入设备和显示设备三、图形的显示设备四、显示子系统五、图形软件标准五、图形软件包六、习题七、直线段扫描转换算法八、直线段扫描转换算法练习题九、扫描线填充算法十、实区域填充算法十一、反走样技术十二、图形裁剪基础概念十三…

开源社区必会知识点— —git提交pr

开源社区必会 1 fork仓库并提交之后给开源社区提交pr 1.1 fork开源仓库 ①登录github&#xff0c;找到开源仓库A&#xff0c;然后点击fork 这样&#xff0c;就会在你自己github账号下创建一个同名的仓库B&#xff08;仓库名可修改&#xff09; ②然后本地修改&#xff0c;提…

[RocketMQ] Consumer消费者启动主要流程源码 (六)

客户端常用的消费者类是DefaultMQPushConsumer, DefaultMQPushConsumer的构造器以及start方法的源码。 1.创建DefaultMQPushConsumer实例 最终都是调用下面四个参数的构造函数: /*** 创建DefaultMQPushConsumer实例** param namespace namespace地址* par…