起源於一個神奇的項目,內容就是爬取搜索引擎的搜索重新建立一個搜索入口.然後就碰到了一些問題:
1. 一次性爬取所有頁面的話爬取速度是一個問題,能不能爬一頁的內容先展示出來,後臺繼續爬取第二頁及以後的頁面,在繼續展示?
2. 爬取的結果展示出來是需要進行分頁的,如何分頁?
這裏首先講解一下如何分頁
Paginator
在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 %}
<p>{{ topic.title }}</p>
{% endfor %}
<!-- 第一種分頁顯示方式 -->
<p>
{# 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 %}
<a href="?page={{ page_number }}">{{ page_number }}</a>
{% endifequal %}
{% endfor %}
</p>
<!-- 另一種分頁顯示方式 -->
<p>
{% if topics.has_previous %}
<a href="?page={{ topics.previous_page_number }}">Previous</a>
{% endif %}
{# topics.paginator.number_pages 返回總頁數 #}
Page {{ topics.number }} of {{ topics.paginator.num_pages }}.
{% if topics.has_next %}
<a href="?page={{ topics.next_page_number }}">Next</a>
{% endif %}
</p>
但是在這裏的解決方案不太適用,當前的結果並不是存在數據庫裏面的,而是實時查詢爬取解析得來的,那麼,這種分頁方案似乎是不太適用,沒辦法,最後採取的方式是爬取特定或全部的頁面,存入數據庫,然後進一步的分頁展示.這裏就到了第一個問題:能否後臺運行爬取任務,翻頁時減少等待.
這裏就需要用到celery,注意的一點是以前的版本需要一個單獨的django-celery保障django和celery的聯合使用,現在不需要了,一個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作爲具體項目的借鑑.