4.用python写网络爬虫,并发下载

news2024/11/17 10:58:40

目录

前言

4.1     100万个网页

4.1.1 解析Alexa列表

4.2 串行爬虫

 4.3 多 线程爬虫

4.3.1 线程和进程如何工作

4.3.2 实现

4.3.3 多进程爬虫

4.4性能

4.5 本章小结


前言

    在之前的章节中,我们的爬虫都是串行下载网页的,只有前一次下载完成之后才会启动新下载。在爬取规模较小的示例网站时,串行下载尚可应对,一但面对大型网站时就会显得捉襟见肘了。在爬取拥有100万网页的大型网站时,假设我们以每秒一个网页的速度昼夜不停地下载,耗时也要超过11天。如果我们可以同时下载多个网页,那么下载时间将会得到显著改善 。
    本章将介绍使用多线程和多进程这两种下载网页的方式 , 并将它们与串行下载的性能进行比较。

4.1     100万个网页

    想要测试并发下载的性能,最好要有一个大型的目标网站。为此,本章将使用Alexa提供的最受欢迎的100万个网站列表,该列表的排名根据安装了Alexa工具栏的用户得出。尽管只有少数用户使用了这个浏览器插件,其数据 并不权威,但对于我们这个测试来说已经足够 了 。
    我们可以通过浏览Alexa网站获取该数据,其网址为http://www.alexa.corn/topsites。此外,我们也 可 以通过 http : / /s3.arnazonaws.这样就不用再去抓取Alexa网站的数据了corn/alexa-static/top-lrn.csv.zip直接下载这一列表的压缩文件这样就不用再去抓取Alexa网站的数据了

4.1.1 解析Alexa列表

Alexa网站列表是以电子表格的形式提供的表格中包含两列内容,分别是排名和域名,如下图所示

 

抽取数据包含如下4个步骤 。
下载 .zip 文件 。
从 .zip 文件中提取出csv文件 。
解析csv文件。
遍历csv文件中的每一行,从中抽取出域名数据 。
下面是实现上述功能的代码。

 

    你可能已经注意到,下载得到的压缩数据是在使用 StringIO封装之后,才传给 ZipFile 的。这是因 为 ZipFile 需要一个类似文件的接口,而不是字符串。接下来,我们从文件名列表中提取出 csv 文件的名称 。由于这个.zip文件中只包含一个文件,所以我们直接选择第一个文件名即可。然后遍历该文件,将第二列中的域名数据添加到 URL 列表中。为了使 URL 合法,我们还会在每个域名 前添加 http://协议。
    要想在之前开发的爬虫中复用上述功能,还需要修改 scrape_ callback 接口。

 

    这里添加了一个新的输入参数maxurls,用于设定从Alexa文件中提取 的U虹数量。默认情况下,该值被设置为 1000个U旺,这是因为下载 100 万个网页的耗时过长( 正如本章开始时提到的, 串行下载需要花费超过11天的时间 )。

4.2 串行爬虫

    下面是串行下载时,之前开发的链接爬虫使用AlexaCallback 回调的代码。
完整源码可 以 从 https://bitbucket.org/wswp/code/src/tip/chapter04/sequential_te s t . py 获取我们可以在命令行中执行如下命令运行该脚本。

    根据该执行结果估算,串行下载时平均每个 URL 需要花费1.6秒。 

 4.3 多 线程爬虫

    现在,我们将串行下载网页的爬虫扩展成并行下载。需要注意的是如果滥用这一功能,多线程爬虫请求内容速度过快,可能会造成服务器过载,或是IP地址被封禁。为了避免这一问题,我们的爬虫将会设置一个delay标识,用于设定请求同一域名时的最小时间间隔。
    作为本章示例 的Alexa网站列表由于包含了100万个不同的域名,因而不会出现上述问题。但是,当你以后爬取同一域名下的不同网页时,就需要注意两次下载之间至少需要1秒钟的延时。

4.3.1 线程和进程如何工作

下图所示为一个包含有多个线程的进程的执行过程。

 

    当运行Python脚本或其他计算机程序时,就会创建包含有代码和状态的进程。这些进程通过计算机的一个或多个CPU来执行。不过,同一时 CPU只会执行一个进程,然后在不同进程问快速切换,这样就给人以多个程序同时运行的感觉。同理,在一个进程中,程序的执行也是在不同线程间进行切换的,每个线程执行程序的不同部分。这就意味着当一个线程等待网页下载时进程可以切换到其他线程执行,避免浪费CPU时间。因此,为了充分利用计算机中的所有资源尽可能快地下载数据,我们需要将下载分发到多个进程和线程中。

4.3.2 实现

    幸运的是,在Python中实现多线程编程相对来说比较简单。我们可以保留与第1章开发的链接爬虫类似的 队列结构 , 只是改为在多个线程中启动爬虫循环 , 以 便并行下载这些链 接。 下 面的 代码是修改后 的 链接爬虫起始部分, 这里把 c rawl 循环移到 了 函 数内部。

 

    下面是 threaded_crawler 函数的 余部分,这里在多 线程中 动了processqueue,并等待其 完成 。
    当有URL可爬取时,上面代码中的循环会不断创建线程,直到达到线程池的最大值。在爬取过程 中,如果当前队列中没有更多可以爬取的URL时,线程会提前停止。假设我们有2个线程以及2个待下载的URL。当第一个线程完成下载时,待爬取队列为空,因此该线程退出。第二个线程稍后也完成了下载,但又发现了另一个待下载的URL。此时thread循环注意到还有URL需要下载,并且线程数未达到最大值,因此又会创建一个新的下载线程 。
    对 threadedcrawler 接口的测试代码可以从https://bitbucket.org/wswp/code/src/tip/chapter04/ threadedtest.py 获取。现在,让我们使用如下命令,测试多线程版本链接爬虫的性能。

 

      由于我们使用了5个线程,因此下载速度几乎是串行版本的5倍。在4.4节中会对多线程性能进行更进一步的分析。

4.3.3 多进程爬虫

   为了进一步改善性能,我们对多线程示例再度扩展,使其支持多进程。目前,爬虫队列都是存储在本地内存当中,其他进程都无法处理这一爬虫。为了解决该问题,需要把爬虫队列转移MongoDB当中。单独存储队列,意味着即使是不同服务器上的爬虫也能够协同处理同一个爬虫任务 。
    请注意,如果想要拥有更加健壮的队列,则需要考虑使用专用的消息传输工具,比如Celery。 不过,为了尽量减少本书中介绍的技术种类,我们在这里选择复用MongoDB。下面是基于 MongoDB 实现的 队列 代码 。

 

 

 

    上面代码中的队列定义了3种状态:OUTSTANDING、PROCESS ING和COMPLETE。当添加一个新URL时,其状态为OUTSTANDING:当URL从队列中取出准备下载时,其状态为PROCE SSING;当下载结束后,其状态为COMPLETE。该实现中,大部分代码都在关注从队列中取出的 URL无法正常完成时的处理,比如处理URL的进程被终止的情况。为了避免丢失这些URL的结果 ,该类使用了一个timeout参数,其默认值为300秒。在repair()方法中,如果某个URL的处理时间超过了这个timeout值,我们就认定处理过程出现了错误,U虹的状态将被重新设为OUTSTAND ING,以便再次处理 。

    为了支持这个新的队列类型,还需要对多线程爬虫的代码进行少量修改 ,下面的代码中已经对修改部分进行了加粗处理 。

 

    第一个改动是将 Python 内建队列替换成基于 MongoDB 的新队列,这里将其命名为MongoQueue。由于该队列会在内部实现中处理重复URL的问 题,因此不再需要seen变量。最后 在URL处理结束后调用complete()方法用于记录该U肚己经被成功解析 。
    更新后的多线程爬虫还可以启动多个进程,如下面的代码所示 。

 

    这段代码的结构看起来十分熟悉,因为多进程模块和之前使用的多线程模块接口相似。这段代码 中首先获取可用CPU的个数,在每个新进程中启动多线程爬虫,然后等待所有进程完成执行 。
    现 在 , 让我们使用 如下命令 , 测试多 进程版本链接爬虫的性能 。 测试 process_link_crawler 的接口和之前测试多线程爬虫时一样,可以从https://bitbucket.org/wswp/ code/src/tip/chapter04/process_test.py获取 。
     通过脚本检测,测试服务器包含2个CPU,运行时间大约是之前使用单一进程执行多线程爬虫 时的一半。在下一节中,我们将进一步研究这三种方式的相对性能。

 

4.4性能

    为了进一步了解增加线程和进程的数量会如何影响下载时间,我们对爬取1000个网页时的结果进行了对比,如下图所示

 

    表格的最后一列给出的是相对于串行下载的时间比。可以看出,性能的增长与线程和进程的数量并不是成线性比例的,而是趋于对数。比如,使用1个进程和5个线程时,性能大约为串行时的4倍 ,而使用20个线程时性能只达到了串行下载时的10倍。虽然新增的线程能够加快下载速度,但是起到的效果相比于之前添加的线程会越来越小。其实这是可以预见到的现象,因为此时进程需要在更多线程之间进行切换,专门用于每一个线程的时间就会变少。此外,下载的带宽是有限的,最终添加新线程将无法带来更快的下载速度。因此,要想获得更好的性能,就需要在多台服务器上分布式部署爬虫,并且所有服务器都要指向同一个MongoDB队列实例。

4.5 本章小结

本章中 , 我们介绍 了 串 行下载存在瓶颈的原因, 然后给 出 了 通过 多线程和
多 进程高效下载大量网 页 的 方法 。

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

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

相关文章

【Nginx】第一章 Nginx简介

内容概览 1、nginx简介 (1)介绍nginx的应用场景和具体可以做什么事情 (2)介绍什么是反向代理 (3)介绍什么是负载均衡 (4)介绍什么是动静分离 2、nginx安装 3、nginx常用的命…

GDPU C语言 番外篇

1. 冒泡排序 &#x1f351; 冒泡排序详解 &#x1f351; 测试地址 #include<stdio.h>int main() {int n;int a[1010];scanf("%d", &n);int i,j;for(i 0; i < n; i)scanf("%d", &a[i]);//冒泡排序for(i 0; i < n-1; i)//最多需要进…

文件系统考古 3:1994 - The SGI XFS Filesystem

在 1994 年&#xff0c;论文《XFS 文件系统的可扩展性》发表了。自 1984 年以来&#xff0c;计算机的发展速度变得更快&#xff0c;存储容量也增加了。值得注意的是&#xff0c;在这个时期出现了更多配备多个 CPU 的计算机&#xff0c;并且存储容量已经达到了 TB 级别。对于这些…

7.NVIC中断优先级管理

1.NVIC中断优先级分组&#xff1a; 分组就是确定系统初始化中断分组之后&#xff0c;确定系统是哪一个分组&#xff0c;从而确定好每一个中断到底有几位抢占优先级和响应优先级&#xff1b;CM3内核支持256个中断&#xff0c;其中包含了16个内核中断和240个外部中断&#xff0c…

@Validated 和 @Valid校验提示信息的双语化以及动态参数添加

对于国际性应用来说&#xff0c;提示信息的双语化是必须的&#xff0c;那么Validated 和 Valid校验提示信息怎么来进行双语化呢&#xff0c;下面我们一起学习一下。 Validated 和 Valid校验提示信息的双语化 SpringBoot项目双语化配置 因为这里是基于SpringBoot项目学习的&a…

最佳实践|亚马逊可持续发展的架构模型

在过去的十年里面&#xff0c;亚马逊云科技一直都致力于帮助企业和开发者实现数字化转型&#xff0c;包括如何使用云技术帮助企业提高运营中资源利用率&#xff1b;如何通过云基础架构、容器、DevOps 进行业务的创新和敏捷性&#xff1b;未来的十年&#xff0c;亚马逊云科技将帮…

idea导入SpringBoot项目,没有启动按钮,没有maven

解决办法&#xff1a;&#xff08;快捷键双击Shift&#xff0c;在搜索框中搜索maven&#xff0c;点击Add Maven Project&#xff0c;就 行了&#xff09; 如果在idea出现下图这种&#xff0c;说明成功了

HAL库介绍

HAL 库&#xff08;Hardware Abstraction Layer&#xff0c;硬件抽象层&#xff09;和标准库&#xff08;Standard Peripheral Library&#xff0c;标准外设库&#xff0c;又称为 SPL&#xff09;都是 STM32 微控制器的固件库。它们的主要区别在于结构和抽象级别。 1.HAL 库&a…

使用IDEA启动项目,build时出现OOM相关异常

在使用idea启动项目时&#xff0c;即使使用参数设置了很大的JVM内存&#xff0c;但在项目构建时&#xff0c;仍会出现OOM异常&#xff0c;这一般是由于idea限制了项目构建时的heap size导致的&#xff0c;可以按需将这个数值调大。 参数位置&#xff1a;setting > Build,Ex…

C# 变量与类型

目录 一 变量类型 二 变量类型大纲 三 使用示范 一 变量类型 C# 有两种类型&#xff1a;值类型和引用类型。 值类型的变量直接包含它们的数据。 引用类型的变量存储对数据&#xff08;称为“对象”&#xff09;的引用。 对于引用类型&#xff0c;两个变量可以引用同一个对…

图像增强之图像锐化(边缘增强)之拉普拉斯算子

note mat (0,1,0;1,-4,1;0,1,0) code // 图像增强之图像锐化(边缘增强)之拉普拉斯算子 void GetLaplaceMat(Mat& laplaceMat) {laplaceMat (Mat_<int>(3,3) << 0,1,0,1,-4,1,0,1,0); } void EdgeSharpenLaplace(Mat&src, Mat& res) {Mat laplaceMa…

【微信小程序】添加了自定义组件引用还是报错

例如&#xff0c;在微信开发者工具中添加了自定义组件group-list&#xff0c;再到页面使用&#xff0c;点重新编译后&#xff0c;报错了。 在Console输出报错信息如下&#xff1a; Component is not found in path "components/group-list/group-list" (using by &q…

ElementUI中使用页签,在一个页面中可以包含多个页面

<el-tabs v-model"activeName" style"margin-left: 10px" tab-click"handleClick"><el-tab-pane label"资质证明文件" name"first"></el-tab-pane><el-tab-pane label"资质大全文件" name&q…

Jupyter----将新创建的环境添加到jupyter中

1、激活你的环境 2、安装ipykernel conda install ipykernel 3、将虚拟环境添加到jupyter中 python -m ipykernel install --user --name 环境名称&#xff08;maliao&#xff09; --display-name "显示名称&#xff08;maliao&#xff09;" 安装完成后即可在Kernel中…

IMX6ULL——音频驱动

WM8960简介 WM8960内部 ADC和 DAC都为24 I2S 总线接口 2S 总线用于主控制器和音频CODEC 芯片之间传输音频数据。 I2S 接口需要3 根信号线(如果需要实现收和发&#xff0c;那么就要4根信号线&#xff0c;收和发分别使用一根信号线)&#xff1a; SCK&#xff1a;串行时钟信号&…

java之路 —— 带你了解安全框架Shiro

文章目录 前言一、组件二、主要开发步骤三、常用的API四、认证的流程 前言 在学习之前&#xff0c;让我们先了解一下什么是shiro。 Shiro&#xff08;Apache Shiro&#xff09;是一个Java安全框架&#xff0c;提供了身份认证、授权、加密和会话管理等功能。它的设计目标是简单…

VMware workstation 17 pro 安装 Windows 10 操作系统

① 访问官方网址下载 MediaCreationTool22H2.exe ② 下载 Windows 10 iso 镜像 ③

Android13 安装最新版 Frida

本文所有教程及源码、软件仅为技术研究。不涉及计算机信息系统功能的删除、修改、增加、干扰&#xff0c;更不会影响计算机信息系统的正常运行。不得将代码用于非法用途&#xff0c;如侵立删&#xff01; Android13 安装最新版 Frida 环境 win10Pixel4Android13Python3.9Frida1…

真箱独立版盲盒小程序定制开发

真箱独立版盲盒小程序定制开发需要以下步骤&#xff1a; 需求分析&#xff1a;确定客户具体的需求和功能要求&#xff0c;例如支持哪些盲盒商品种类、购物车、支付方式等。 用户界面设计&#xff1a;根据客户要求&#xff0c;设计合适的用户界面&#xff0c;包括首页、…

创建四大经济区shp矢量图

准备 1、具有省域划分的shp矢量图 2、Arcgis 一、创建新要素 右击目录-新建-要素文件 选择要素开始编辑 矩形框选需要的行政边界要素点—右击要素线复制—粘贴至新要素文件中 &#xff08;长按shift键&#xff0c;多点选择&#xff09; 结果图&#xff08;部分&#xff0…