2

我正在尝试使用django-rq在 Python 中创建一个网站爬虫。到目前为止,我的工人看起来像这样:

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

status=1表示页面已处理。status=0表示该页面尚未处理。

现在,这个算法对一个工人来说工作得很好。但是,当有更多工人时不会这样做,因为有时会比应有的时间更早地触发工作程序的结束。

实施这个工人的正确方法是什么?

4

2 回答 2

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 文档

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

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

管理作业将启动并跟踪所有“获取网页”作业。它会知道“工作完成”的时间,因为它的所有子工作都已完成。

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

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

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

于 2014-10-03T10:20:12.633 回答
2

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

即,有 10 名工人,状态为:0-8 - “保持” 9 - “工作”

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

需要注意的另一件事是,worker 如何确定其状态的顺序需要精确:

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

如果工作人员处于等待状态,则其操作顺序可能类似于:

  1. 醒来
  2. 如果队列中有作业,则进入“工作”状态
  3. 如果不是,请检查是否所有工作人员都处于“暂停”状态。
  4. 如果是,请退出。

任何处于“保留”状态的工人都应定期检查是否有任何工人处于“工作”状态。如果是这样,请检查队列然后休眠。如果没有,结束。

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

于 2014-10-01T20:16:17.523 回答