当前位置:首页 » 编程语言 » python分布式调度

python分布式调度

发布时间: 2022-12-11 18:22:02

A. python爬虫技术有哪些做的比较好的

基础爬虫:(1)基础库:urllib模块/requests第三方模块首先爬虫就是要从网页上把我们需要的信息抓取下来的,那么我们就要学习urllib/requests模块,这两种模块是负责爬取网页的。这里大家觉得哪一种用的习惯就用哪一种,选择一种精通就好了。我推荐读者使用使用requests模块,因为这一种简便很多,容易操作、容易理解,所以requests被称为“人性化模块”。(2)多进程、多线程、协程和分布式进程:为什么要学着四个知识呢?假如你要爬取200万条的数据,使用一般的单进程或者单线程的话,你爬取下载这些数据,也许要一个星期或是更久。试问这是你想要看到的结果吗?显然单进程和单线程不要满足我们追求的高效率,太浪费时间了。只要设置好多进程和多线程,爬取数据的速度可以提高10倍甚至更高的效率。(3)网页解析提取库:xpath/BeautifulSoup4/正则表达式通过前面的(1)和(2)爬取下来的是网页源代码,这里有很多并不是我们想要的信息,所以需要将没用的信息过滤掉,留下对我们有价值的信息。这里有三种解析器,三种在不同的场景各有特色也各有不足,总的来说,学会这三种灵活运用会很方便的。推荐理解能力不是很强的朋友或是刚入门爬虫的朋友,学习BeautifulSoup4是很容易掌握并能够快速应用实战的,功能也非常强大。(4)反屏蔽:请求头/代理服务器/cookie在爬取网页的时候有时会失败,因为别人网站设置了反爬虫措施了,这个时候就需要我们去伪装自己的行为,让对方网站察觉不到我们就是爬虫方。请求头设置,主要是模拟成浏览器的行为;IP被屏蔽了,就需要使用代理服务器来破解;而cookie是模拟成登录的行为进入网站。

B. python分布式爬虫是什么意思

一、分布式爬虫架构

在了解分布式爬虫架构之前,首先回顾一下Scrapy的架构,如下图所示。

我们需要做的就是在多台主机上同时运行爬虫任务协同爬取,而协同爬取的前提就是共享爬取队列。这样各台主机就不需要各自维护爬取队列,而是从共享爬取队列存取Request。但是各台主机还是有各自的Scheler和Downloader,所以调度和下载功能分别完成。如果不考虑队列存取性能消耗,爬取效率还是会成倍提高。

二、维护爬取队列

那么这个队列用什么来维护?首先需要考虑的就是性能问题。我们自然想到的是基于内存存储的Redis,它支持多种数据结构,例如列表(List)、集合(Set)、有序集合(Sorted Set)等,存取的操作也非常简单。

Redis支持的这几种数据结构存储各有优点。

  • 列表有lpush()、lpop()、rpush()、rpop()方法,我们可以用它来实现先进先出式爬取队列,也可以实现先进后出栈式爬取队列。

  • 集合的元素是无序的且不重复的,这样我们可以非常方便地实现随机排序且不重复的爬取队列。

  • 有序集合带有分数表示,而Scrapy的Request也有优先级的控制,我们可以用它来实现带优先级调度的队列。

  • 我们需要根据具体爬虫的需求来灵活选择不同的队列。

    三、如何去重

    Scrapy有自动去重,它的去重使用了Python中的集合。这个集合记录了Scrapy中每个Request的指纹,这个指纹实际上就是Request的散列值。我们可以看看Scrapy的源代码,如下所示:


    importhashlib
    defrequest_fingerprint(request, include_headers=None):
    ifinclude_headers:
    include_headers = tuple(to_bytes(h.lower())
    forhinsorted(include_headers))
    cache = _fingerprint_cache.setdefault(request, {})
    ifinclude_headersnotincache:
    fp = hashlib.sha1()
    fp.update(to_bytes(request.method))
    fp.update(to_bytes(canonicalize_url(request.url)))
    fp.update(request.bodyorb'')
    ifinclude_headers:
    forhdrininclude_headers:
    ifhdrinrequest.headers:
    fp.update(hdr)
    forvinrequest.headers.getlist(hdr):
    fp.update(v)
    cache[include_headers] = fp.hexdigest()
    returncache[include_headers]

    request_fingerprint()就是计算Request指纹的方法,其方法内部使用的是hashlib的sha1()方法。计算的字段包括Request的Method、URL、Body、Headers这几部分内容,这里只要有一点不同,那么计算的结果就不同。计算得到的结果是加密后的字符串,也就是指纹。每个Request都有独有的指纹,指纹就是一个字符串,判定字符串是否重复比判定Request对象是否重复容易得多,所以指纹可以作为判定Request是否重复的依据。

    那么我们如何判定重复呢?Scrapy是这样实现的,如下所示:


    def__init__(self):
    self.fingerprints = set()

    defrequest_seen(self, request):
    fp = self.request_fingerprint(request)
    iffpinself.fingerprints:
    returnTrue
    self.fingerprints.add(fp)

    在去重的类RFPDupeFilter中,有一个request_seen()方法,这个方法有一个参数request,它的作用就是检测该Request对象是否重复。这个方法调用request_fingerprint()获取该Request的指纹,检测这个指纹是否存在于fingerprints变量中,而fingerprints是一个集合,集合的元素都是不重复的。如果指纹存在,那么就返回True,说明该Request是重复的,否则这个指纹加入到集合中。如果下次还有相同的Request传递过来,指纹也是相同的,那么这时指纹就已经存在于集合中,Request对象就会直接判定为重复。这样去重的目的就实现了。

    Scrapy的去重过程就是,利用集合元素的不重复特性来实现Request的去重。

    对于分布式爬虫来说,我们肯定不能再用每个爬虫各自的集合来去重了。因为这样还是每个主机单独维护自己的集合,不能做到共享。多台主机如果生成了相同的Request,只能各自去重,各个主机之间就无法做到去重了。

    那么要实现去重,这个指纹集合也需要是共享的,Redis正好有集合的存储数据结构,我们可以利用Redis的集合作为指纹集合,那么这样去重集合也是利用Redis共享的。每台主机新生成Request之后,把该Request的指纹与集合比对,如果指纹已经存在,说明该Request是重复的,否则将Request的指纹加入到这个集合中即可。利用同样的原理不同的存储结构我们也实现了分布式Reqeust的去重。

    四、防止中断

    在Scrapy中,爬虫运行时的Request队列放在内存中。爬虫运行中断后,这个队列的空间就被释放,此队列就被销毁了。所以一旦爬虫运行中断,爬虫再次运行就相当于全新的爬取过程。

    要做到中断后继续爬取,我们可以将队列中的Request保存起来,下次爬取直接读取保存数据即可获取上次爬取的队列。我们在Scrapy中指定一个爬取队列的存储路径即可,这个路径使用JOB_DIR变量来标识,我们可以用如下命令来实现:


    scrapy crawl spider -s JOB_DIR=crawls/spider

    更加详细的使用方法可以参见官方文档,链接为:https://doc.scrapy.org/en/latest/topics/jobs.html。

    在Scrapy中,我们实际是把爬取队列保存到本地,第二次爬取直接读取并恢复队列即可。那么在分布式架构中我们还用担心这个问题吗?不需要。因为爬取队列本身就是用数据库保存的,如果爬虫中断了,数据库中的Request依然是存在的,下次启动就会接着上次中断的地方继续爬取。

    所以,当Redis的队列为空时,爬虫会重新爬取;当Redis的队列不为空时,爬虫便会接着上次中断之处继续爬取。

    五、架构实现

    我们接下来就需要在程序中实现这个架构了。首先实现一个共享的爬取队列,还要实现去重的功能。另外,重写一个Scheer的实现,使之可以从共享的爬取队列存取Request。

    幸运的是,已经有人实现了这些逻辑和架构,并发布成叫Scrapy-Redis的Python包。接下来,我们看看Scrapy-Redis的源码实现,以及它的详细工作原理

C. python rq 的这个库,可以用于分布式吗

只能应付简单的异步计算需求而已。复杂的分布式计算不是很靠谱(因为 rq 实在是太简单了,个人认为它缺少资源调度、稳定性保障和监控机制),而且对中文支持有 bug ,提交过 patch 忘记作者合了没有。

D. 如何用 Python 构建一个简单的分布式系统

分布式爬虫概览
何谓分布式爬虫?
通俗的讲,分布式爬虫就是多台机器多个
spider
对多个
url
的同时处理问题,分布式的方式可以极大提高程序的抓取效率。
构建分布式爬虫通畅需要考虑的问题
(1)如何能保证多台机器同时抓取同一个URL?
(2)如果某个节点挂掉,会不会影响其它节点,任务如何继续?
(3)既然是分布式,如何保证架构的可伸缩性和可扩展性?不同优先级的抓取任务如何进行资源分配和调度?
基于上述问题,我选择使用celery作为分布式任务调度工具,是分布式爬虫中任务和资源调度的核心模块。它会把所有任务都通过消息队列发送给各个分布式节点进行执行,所以可以很好的保证url不会被重复抓取;它在检测到worker挂掉的情况下,会尝试向其他的worker重新发送这个任务信息,这样第二个问题也可以得到解决;celery自带任务路由,我们可以根据实际情况在不同的节点上运行不同的抓取任务(在实战篇我会讲到)。本文主要就是带大家了解一下celery的方方面面(有celery相关经验的同学和大牛可以直接跳过了)
Celery知识储备
celery基础讲解
按celery官网的介绍来说
Celery
是一个简单、灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具。它是一个专注于实时处理的任务队列,同时也支持任务调度。
下面几个关于celery的核心知识点
broker:翻译过来叫做中间人。它是一个消息传输的中间件,可以理解为一个邮箱。每当应用程序调用celery的异步任务的时候,会向broker传递消息,而后celery的worker将会取到消息,执行相应程序。这其实就是消费者和生产者之间的桥梁。
backend:
通常程序发送的消息,发完就完了,可能都不知道对方时候接受了。为此,celery实现了一个backend,用于存储这些消息以及celery执行的一些消息和结果。
worker:
Celery类的实例,作用就是执行各种任务。注意在celery3.1.25后windows是不支持celery
worker的!
procer:
发送任务,将其传递给broker
beat:
celery实现的定时任务。可以将其理解为一个procer,因为它也是通过网络调用定时将任务发送给worker执行。注意在windows上celery是不支持定时任务的!
下面是关于celery的架构示意图,结合上面文字的话应该会更好理解
由于celery只是任务队列,而不是真正意义上的消息队列,它自身不具有存储数据的功能,所以broker和backend需要通过第三方工具来存储信息,celery官方推荐的是
RabbitMQ和Redis,另外mongodb等也可以作为broker或者backend,可能不会很稳定,我们这里选择Redis作为broker兼backend。
实际例子
先安装celery
pip
install
celery
我们以官网给出的例子来做说明,并对其进行扩展。首先在项目根目录下,这里我新建一个项目叫做celerystudy,然后切换到该项目目录下,新建文件tasks.py,然后在其中输入下面代码
这里我详细讲一下代码:我们先通过app=Celery()来实例化一个celery对象,在这个过程中,我们指定了它的broker,是redis的db
2,也指定了它的backend,是redis的db3,
broker和backend的连接形式大概是这样
redis://:password@hostname:port/db_number
然后定义了一个add函数,重点是@app.task,它的作用在我看来就是将add()
注册为一个类似服务的东西,本来只能通过本地调用的函数被它装饰后,就可以通过网络来调用。这个tasks.py中的app就是一个worker。它可以有很多任务,比如这里的任务函数add。我们再通过在命令行切换到项目根目录,执行
celery
-A
tasks
worker
-l
info
启动成功后就是下图所示的样子
这里我说一下各个参数的意思,-A指定的是app(即Celery实例)所在的文件模块,我们的app是放在tasks.py中,所以这里是
tasks;worker表示当前以worker的方式运行,难道还有别的方式?对的,比如运行定时任务就不用指定worker这个关键字;
-l
info表示该worker节点的日志等级是info,更多关于启动worker的参数(比如-c、-Q等常用的)请使用
celery
worker
--help
进行查看
将worker启动起来后,我们就可以通过网络来调用add函数了。我们在后面的分布式爬虫构建中也是采用这种方式分发和消费url的。在命令行先切换到项目根目录,然后打开python交互端
from
tasks
import
addrs
=
add.delay(2,
2)
这里的add.delay就是通过网络调用将任务发送给add所在的worker执行,这个时候我们可以在worker的界面看到接收的任务和计算的结果。
这里是异步调用,如果我们需要返回的结果,那么要等rs的ready状态true才行。这里add看不出效果,不过试想一下,如果我们是调用的比较占时间的io任务,那么异步任务就比较有价值了
上面讲的是从Python交互终端中调用add函数,如果我们要从另外一个py文件调用呢?除了通过import然后add.delay()这种方式,我们还可以通过send_task()这种方式,我们在项目根目录另外新建一个py文件叫做
excute_tasks.py,在其中写下如下的代码
from
tasks
import
addif
__name__
==
'__main__':
add.delay(5,
10)
这时候可以在celery的worker界面看到执行的结果
此外,我们还可以通过send_task()来调用,将excute_tasks.py改成这样
这种方式也是可以的。send_task()还可能接收到为注册(即通过@app.task装饰)的任务,这个时候worker会忽略这个消息
定时任务
上面部分讲了怎么启动worker和调用worker的相关函数,这里再讲一下celery的定时任务。
爬虫由于其特殊性,可能需要定时做增量抓取,也可能需要定时做模拟登陆,以防止cookie过期,而celery恰恰就实现了定时任务的功能。在上述基础上,我们将tasks.py文件改成如下内容
然后先通过ctrl+c停掉前一个worker,因为我们代码改了,需要重启worker才会生效。我们再次以celery
-A
tasks
worker
-l
info这个命令开启worker。
这个时候我们只是开启了worker,如果要让worker执行任务,那么还需要通过beat给它定时发送,我们再开一个命令行,切换到项目根目录,通过
这样就表示定时任务已经开始运行了。
眼尖的同学可能看到我这里celery的版本是3.1.25,这是因为celery支持的windows最高版本是3.1.25。由于我的分布式微博爬虫的worker也同时部署在了windows上,所以我选择了使用
3.1.25。如果全是linux系统,建议使用celery4。
此外,还有一点需要注意,在celery4后,定时任务(通过schele调度的会这样,通过crontab调度的会马上执行)会在当前时间再过定时间隔执行第一次任务,比如我这里设置的是60秒的间隔,那么第一次执行add会在我们通过celery
beat
-A
tasks
-l
info启动定时任务后60秒才执行;celery3.1.25则会马上执行该任务

热点内容
用什么dns服务器地址快 发布:2025-05-15 04:52:59 浏览:26
手机端so反编译 发布:2025-05-15 04:50:55 浏览:610
linuxlamp安装 发布:2025-05-15 04:50:45 浏览:578
sqlplus缓存区怎么设置 发布:2025-05-15 04:50:44 浏览:857
shell脚本环境变量 发布:2025-05-15 04:45:18 浏览:693
安卓nba2k18什么时候出 发布:2025-05-15 04:38:42 浏览:393
王者安卓转苹果为什么显示失败 发布:2025-05-15 04:35:49 浏览:18
手机优酷缓存视频格式 发布:2025-05-15 04:13:45 浏览:210
公益电影分镜头脚本插画 发布:2025-05-15 04:08:37 浏览:961
数据压缩编码 发布:2025-05-15 03:58:44 浏览:726