静态网站通常不提供内置的搜索功能,为了为Pelican添加搜索功能,有三种常见的思路:
1.使用Google或百度等搜索引擎提供的站内搜索工具。然而,这种方法存在一些缺点,如可能无法访问Google或百度,或者这些搜索引擎可能不收录你的网站内容。因此,这种方法不是特别推荐。
2.使用第三方提供的搜索功能。这些服务通常通过爬虫程序对目标网站的内容进行抓取,并在用户搜索时调用相应的接口返回相关内容。例如,Algolia是一种流行的第三方搜索服务。然而,这种方法也存在一些缺点,如访问速度可能较慢,或者需要注册才能使用。
3.采用纯静态的方式来实现搜索功能。这种方法使用JavaScript框架如Fuse.js等,通过在客户端进行全文搜索。这种方法的优点是简单便捷,并且非常稳定。然而,如果网站的内容量非常大,这种方法的效率可能不太理想。
你可以根据自己的需求和网站的规模选择适合的搜索功能实现方式。
纯静态方式实现的搜索功能是通过将需要搜索的内容(如标题、URL、内容等)预先生成一种预定义格式的索引,并以JSON或JavaScript等方式存储在JavaScript变量中。在进行搜索时,使用类似Fuse.js等功能的库来检索并返回搜索结果。
这种方法的优点主要包括:
快速加载:由于是预先生成,不需要服务器动态生成,因此可以快速地为用户提供搜索结果。在数据量适中的情况下,加载速度非常快,用户可以在短时间内访问网站并获取所需信息。
易于维护:纯静态网站的HTML、CSS和JavaScript等文件都是独立的,只需要修改相应的文件就可以更新网站内容和样式,维护起来相对比较方便。
安全:由于没有采用动态技术,纯静态网站不容易受到攻击,相对来说更加安全。
然而,纯静态方式也存在一些缺点:
功能受限:只能提供简单的搜索功能,无法实现复杂的交互功能,对于需要交互功能的网站来说不太适合。
更新不方便:由于需要手动更新索引文件和JavaScript代码,因此更新过程比较麻烦。
数据量大的情况下性能堪忧:由于是将预定义格式的索引保存到JavaScript变量中,并通过JavaScript传输所有数据,每次访问都需要加载JavaScript代码,因此在数据量大的情况下性能可能成为问题。
纯静态方式实现的搜索功能适用于数据量适中的小型网站或只需要展示信息的网站。
纯静态方式的实现:
Pelican提供了一个插件:search,使用了stork实现。 这种方式实现搜索功能大体上都分为3步: 1.建立自己的索引。 2.在模板中加入需要的js、css,通过js等方式将索引加载到浏览器。 3、添加搜索框,使用stork、Fuse.js等方式检索索引,并显示结果。
因为Pelican search使用的stork是有rust编写的,并且不在维护,所以我自己使用Fuse.js实现了一个:
1.建立自己的索引
在项目目录下新建plugins
文件夹,新建一个插件文件:search_plugin.py
,这样做的目的是当Pelican生成时站点同时生成索引js,不必单独生成索引js
索引文件也可以是json等其他形式,相对于的js加载索引文件的代码和索引文件的传输需要做出相应的改变。
from pelican import signals
import os
import time
import logging
log = logging.getLogger(__name__)
def is_file_created_recently(file_path):
'''
主要调试用,不然pelican --autoreload --listen会不断生成search.js,从而引发pelican不断autoreload
检查文件创建日期,如果文件不存在返回false如果存在,
创建日期大于60秒,则删除,返回True
如果存在,创建日期小于60秒,则返回True
'''
try:
creation_time = os.path.getctime(file_path)
current_time = time.time()
if current_time - creation_time <= 60:
return True
else:
try:
os.remove(file_path)
log.debug("文件删除成功")
return False
except OSError as e:
log.debug(f"文件删除失败:{e}")
except Exception as e:
return False
def get_articles(generator):
log.debug("%s initialized !!", generator)
articles = []
for article in generator.articles:
#从pelican检索数据
articles.append({
'title': article.title,
# 'content': article.content,
'url': f'/{article.url}',
})
# search_dir=f'search'
#将索引文件search.js生成到主题目录:/static/js/search.js
search_dir=f'{generator.theme}{os.path.sep}static{os.path.sep}js'
# 主题路径generator.theme
try:
os.mkdir(search_dir)
except Exception as e:
pass
#搜索需要的js代码
js_str='''
// 创建Fuse实例
const fuse = new Fuse(articles, {
keys: ["title", ], // 指定搜索的字段"content"
});
// 搜索函数
function search(query) {
const result = fuse.search(query); // 执行搜索
return result.map((item) => item.item); // 返回搜索结果
}
const searchInput = document.getElementById('searchInput');
searchInput.addEventListener('input', function () {
const query = searchInput.value;
const searchResults = search(query);
displayResults(searchResults);
});
function displayResults(searchResults) {
var searchResultDiv = document.getElementById('searchResult');
const searchInput = document.getElementById('searchInput');
searchResultDiv.innerHTML = ''; // 清空搜索结果容器
if (searchResults.length === 0) {
searchResultDiv.innerHTML = '';
return;
}
for (var i = 0; i < searchResults.length; i++) {
var article = searchResults[i];
var articleDiv = document.createElement('div');
articleDiv.innerHTML = '<h2><a target="_blank" href="' + article.url + '">' + article.title + '</a></h2>';
searchResultDiv.appendChild(articleDiv);
}
}
'''
#search.js保存路径
articles_path=f'{search_dir}{os.path.sep}search.js'
if is_file_created_recently(articles_path):
pass
else:
# log.debug(f'const articles = {str(articles)};{js_str}')
#索引传入articles变量,和搜索需要的js代码整合为一个js文件
with open(articles_path, 'w') as f:
f.write(f'const articles = {str(articles)};{js_str}')
def register():
#Pelican生成时站点同时生成索引js
signals.article_generator_finalized.connect(get_articles)
# signals.page_generator_finalized.connect(get_articles)
2.在base.html模板中加入需要的js、css,通过js等方式将索引加载到浏览器
将fuse.js
文件放到模板下的static/js文件夹下。
<script src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/js/fuse.js"></script>
<script src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/js/search.js"></script>
3、添加搜索框,检索索引,并显示结果
sidebar_search.html
模板文件内容
<input type="text" id="searchInput" placeholder="输入搜索关键词">
<div id="searchResult"></div>
注意的点:
id="searchInput
对应search.js
中的:const searchInput = document.getElementById('searchInput');
id="searchResult
对应search.js
中的:var searchResultDiv = document.getElementById('searchResult');