起源于一个神奇的项目,内容就是爬取搜索引擎的搜索重新建立一个搜索入口.然后就碰到了一些问题:
1. 一次性爬取所有页面的话爬取速度是一个问题,能不能爬一页的内容先展示出来,后台继续爬取第二页及以后的页面,在继续展示?
2. 爬取的结果展示出来是需要进行分页的,如何分页?
这里首先讲解一下如何分页
在django中分页采用的是Paginator,举个例子
from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger
from hello.models import Topic
def index(request):
limit = 3 # 每页显示的记录数
topics = Topic.objects.all()
paginator = Paginator(topics, limit) # 实例化一个分页对象
page = request.GET.get('page') # 获取页码
try:
topics = paginator.page(page) # 获取某页对应的记录
except PageNotAnInteger: # 如果页码不是个整数
topics = paginator.page(1) # 取第一页的记录
except EmptyPage: # 如果页码太大,没有相应的记录
topics = paginator.page(paginator.num_pages) # 取最后一页的记录
return render_to_response('index.html', {'topics': topics})
然后在模板里面的稍作修改即可
{% for topic in topics.object_list %}
{{ topic.title }}
{% endfor %}
{# topics.paginator.page_range 这个函数返回包含一个所有页码数的 range 对象 #}
{# 即 range(1, topics.paginator.num_pages + 1) #}
{% for page_number in topics.paginator.page_range %}
{% ifequal page_number topics.number %}
{{ page_number }}
{% else %}
"?page={{ page_number }}">{{ page_number }}
{% endifequal %}
{% endfor %}
{% if topics.has_previous %}
"?page={{ topics.previous_page_number }}">Previous
{% endif %}
{# topics.paginator.number_pages 返回总页数 #}
Page {{ topics.number }} of {{ topics.paginator.num_pages }}.
{% if topics.has_next %}
"?page={{ topics.next_page_number }}">Next
{% endif %}
但是在这里的解决方案不太适用,当前的结果并不是存在数据库里面的,而是实时查询爬取解析得来的,那么,这种分页方案似乎是不太适用,没办法,最后采取的方式是爬取特定或全部的页面,存入数据库,然后进一步的分页展示.这里就到了第一个问题:能否后台运行爬取任务,翻页时减少等待.
这里就需要用到celery,注意的一点是以前的版本需要一个单独的django-celery保障django和celery的联合使用,现在不需要了,一个celery库即可.
按照官方教程配置,建议不懂celery的,先看一下这个,如果出现server问题,解决方案一般是需要安装对应的server,如redis等. 另外重要的一点为了能够使用后台运行的结果,按照教程中需要安装django-celery-results,同时在后台task函数里面,将结果保存在django_celery_results.models.TaskResult里面,
from __future__ import absolute_import, unicode_literals
from django-celery-results import models
from celery import shared_task
@shared_task
def func(x, y):
result = somefunction(x+y)
id = anotherfunction(x+y)
django_result = models.TaskResult(task_id = id,result=result)
django_result.save()
return id
在后台运行的结果就可以task_id重新查询到,当然在这里如果考虑到实时和数据库容量的问题,则需要定时或者在sseion结束时删除掉对应task_id的运行结果,这里同样需要用celery制定一个定时任务
Tips:
1. 在运行celery -A proj worker –loglevel=info出现问题”server channel error 406, message: PRECONDITION_FAILED”的一个原因可能是你在另外一个地方运行了同样的celery 任务,这里需要的就是关掉其他运行的celery任务,就没有问题了.
2. 查看文档比查看博客来的准确,查github作为具体项目的借鉴.