如何在多个工作人员完成大小未知的工作时收到通知?

时间:2014-09-21 10:56:50

标签: python web-crawler worker django-rq

我正在尝试使用django-rq在Python中创建网站抓取工具。到目前为止,我的工作人员看起来像这样:

  1. 从队列中获取下一页。
  2. 在数据库中创建页面记录。设置status=1
  3. 下载页面内容和流程。可能需要一分钟左右。
  4. 对于页面中的每个链接
    1. 检查链接是否已在数据库中注册。
    2. 如果没有,请创建新的页面记录。设置status=0并将链接添加到队列。
  5. for循环结束后,检查status = 0的页数是否为0.如果是,则完成作业。
  6. status=1表示页面已处理完毕。 status=0表示该页面尚未处理。

    现在,这个算法适用于单个工作者。但是,当有更多的工人因为工作常规的结束有时会被触发时,它就不会发生。

    实施此工作人员的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

1。调整当前系统

  • 如果您暂停将页面状态设置为1,直到 之后,您已完成处理,那么您的工作人员就不应该声明“已完成工作”。早。
  • 您的第2步仅适用于您开始抓取的第一页。

所以你的系统就像:

start job:
1. Create a page record in the database. Set status=0. Add page to queue.

worker:
1. Get the next page from the queue.
2. Download the page contents and process. Might take up to a minute or so.
3. For each link in the page
    1. Check if the link is already registered in the database.
    2. If not, create a new page record. Set status=0 and add the link to the queue.
4. After the for loop ends, set status=1 for this page.
5. Check whether the count of pages with status=0 is 0. If yes, the job is done.

如果在上一个网页抓取工作完成之前启动了后续网络抓取工作,您将只能完成工作'在最后一个结束时。 您可以在您的数据库页面记录中添加一个作业ID,并重新定义“完成工作”。就像count(status=0 and job-id=x) = 0

之类的东西

2。利用RQ的工作级

来自RQ docs

  

当作业入队时,queue.enqueue()方法返回一个Job实例。   ...它有一个方便的结果访问属性,当作业尚未完成时将返回None,或者当作业完成时返回非None值(当然,假设作业首先有一个返回值)

您可以排队两种不同类型的作业,一种是“抓取网页”。另一个用于管理抓取过程。

管理工作将启动并跟踪所有提取网页'工作。它会知道什么时候完成工作'因为它的所有子工作都已完成。

您不一定 需要向数据库写入任何内容来管理抓取过程。

您需要运行2个以上的工作人员,以便crawlfetch可以同时处理,可能在不同的队列中。

def something_web_facing():
    ...
    queue.enqueue(crawl, 'http://url.com/start_point.html')
    ...

def crawl(start_url):
    fetch_jobs = []
    seen_urls = set()

    seen_urls.add(start_url)
    fetch_jobs.append( queue.enqueue(fetch, start_url) )

    while len(fetch_jobs) > 0:

        # loop over a copy of fetch_jobs
        for job in list(fetch_jobs):

            # has this job completed yet?
            if job.result:

                # a fetch job returns a list of the next urls to crawl
                for url in job.result:

                    # fetch this url if we haven't seen it before
                    if url not in seen_urls:
                        seen_urls.add(url)
                        fetch_jobs.append( queue.enqueue(fetch, url) )

                fetch_jobs.remove(job)

        time.sleep(1)

    return "Job done!"

def fetch(url):
    """Get web page from url, return a list of links to follow next"""

    html_page = download_web_page(url)
    links_to_follow = find_links_to_follow(html_page)
    return links_to_follow

3。使用其他人的网络抓取代码

Scrapy

您可以排队使用scrapy的作业。 Run scrapy from a script

答案 1 :(得分:2)

只是一个想法,但你不能有一个额外的表来保存工人的状态吗?

即,有10名工人和以下国家: 0-8 - “持有” 9 - “工作”

从这个状态,9可能会在0-8可以处理的队列中添加更多页面(并将表中的状态更改为“正常工作”)。

另外需要注意的是,工人如何确定其状态的顺序需要准确:

  1. 从队列中获取下一页。
  2. 在数据库中创建页面记录。设置状态= 1。
  3. 下载页面内容和流程。可能需要一分钟左右。
  4. 对于页面中的每个链接
    1. 检查链接是否已在数据库中注册。 2.如果没有,请创建一个新的页面记录。设置status = 0并将链接添加到队列。
  5. 检查队列中是否有任何页面。
  6. 如果没有,请进入等待状态。
  7. 如果一个工人处于等待状态,那么它的行动顺序可能类似于:

    1. 醒来
    2. 如果队列中有作业,则进入“工作”状态
    3. 如果没有,请检查所有工人是否处于“保持”状态。
    4. 如果是,请退出。
    5. 任何处于“保持”状态的工人都应定期检查是否有任何工人处于“工作”状态。如果是,请检查队列然后再睡觉。如果没有,请结束。

      另一种方法是让一个命令和控制工作人员监视其他工作人员。如果他们都处于“保持”状态,则触发一个他们都注意结束的标志。