博客的搜索功能是怎么实现的

2024-11-19 建站

之前我介绍了我的博客的实现,其中最后的demo里面有个搜索功能,今天就来介绍下这个搜索功能的实现吧。其实静态博客的搜索功能实现起来挺简单的

思路

假设我们有一个posts 的包含所有文章信息的列表, 通过generate_search_xml函数处理传入的posts参数,将文章的标题、内容等信息写入一个search.xml文件内,后续在通过搜索页面对该文件内容进行搜索实现搜索展示。这里涉及到两个部分:

  • search.xml文件的生成,由generate_search_xml函数实现
  • 搜索页面对search.xml文件进行搜索,并展示搜索结果

generate_search_xml函数实现

import xml.etree.ElementTree as ET
def generate_search_xml(posts):
    """根据文章信息生成 search.xml"""
    search_xml = ET.Element('search')

    for post in posts:
        title = post['title']
        html_content = post['html_content']
        metadata = post['metadata']

        # 生成描述,提取文章正文的前200个字符作为描述
        description = html_content[:200].strip()  # 提取内容的前200个字符作为描述
        if len(description) == 200 and html_content[200] not in [' ', '<']:  # 防止截断在单词或标签中间
            description = description.rsplit(' ', 1)[0]

        item = ET.SubElement(search_xml, 'item')
        title_elem = ET.SubElement(item, 'title')
        title_elem.text = title
        description_elem = ET.SubElement(item, 'description')
        description_elem.text = description
        # metadata_elem = ET.SubElement(item, 'metadata')
        # metadata_elem.text = metadata
        html_content_elem = ET.SubElement(item, 'html_content')
        html_content_elem.text = html_content

    # 写入到 search.xml 文件
    tree = ET.ElementTree(search_xml)
    output_path = os.path.join(OUTPUT_DIR, 'search.xml')
    tree.write(output_path, encoding='utf-8', xml_declaration=True)
    # print(f"search.xml 文件已保存至 {output_path}")
    print(f"Generated {search_xml}")

上面这个函数,将每篇文章的信息以<item>元素添加到search.xml文件的根元素<search>当中,每个 <item> (也就是每篇文章)包含:

  • <title>: 文章标题
  • <description>: 文章描述(前200个字符)
  • <html_content>: 文章的完整 HTML 内容

搜索页面的实现

首先通过loadXML()函数加载search.xml文件到searchData列表,然后通过id="search-input"获取搜索关键字query,通过方法filter查找包含关键字query的内容。最后对查找结果使用displayResults方法进行生成展示

  • 搜索页模板search.html
{% extends "base.html" %}

{% block title %}搜索页面{% endblock %}

{% block content %}
    <!-- 页面主要内容 -->
    <div class="search-box content">
{#        <h1>搜索页面</h1>#}

        <div id="search-container">
            <img src="../static/img/搜索_search.png" alt="Search Icon" class="search-icon"> <!-- 这里是你的搜索图标图片路径 -->
            <input type="text" id="search-input" placeholder="请输入搜索关键字" oninput="search()">
        </div>

        <div id="search-results"></div>
    </div>
    <script>
        // 初始化变量
        let searchData = [];

        // 加载 search.xml 数据
        function loadXML() {
            fetch('search.xml')
                .then(response => response.text())
                .then(data => {
                    const parser = new DOMParser();
                    const xmlDoc = parser.parseFromString(data, 'text/xml');
                    const items = xmlDoc.getElementsByTagName('item');
                    searchData = [];

                    for (let i = 0; i < items.length; i++) {
                        let title = items[i].getElementsByTagName('title')[0].textContent;
                        let description = items[i].getElementsByTagName('description')[0].textContent;
                        let html_content = items[i].getElementsByTagName('html_content')[0].textContent;
                        searchData.push({ title, description,html_content });
                    }
                })
                .catch(error => console.error('加载 XML 数据失败:', error));
        }

        function search() {
            const query = document.getElementById('search-input').value.trim().toLowerCase();

            // 如果搜索框为空,清空结果并返回
            if (query === '') {
                document.getElementById('search-results').innerHTML = ''; // 清空搜索结果
                return;
            }

            // 如果有搜索内容,执行搜索操作
            const results = searchData.filter(item =>
                item.title.toLowerCase().includes(query) || item.description.toLowerCase().includes(query) || item.html_content.toLowerCase().includes(query)
            );
            displayResults(query,results);
        }

        // 显示搜索结果
        function displayResults(query,results) {
            const resultsContainer = document.getElementById('search-results');
            resultsContainer.innerHTML = '';

            if (results.length === 0) {
                resultsContainer.innerHTML = '<p>没有找到相关结果</p>';
                return;
            }

            results.forEach(item => {
                const resultTitle = document.createElement('h1');
                resultTitle.innerHTML=`包含关键字<span class="query-color">${query}</span>的文章:`;
                resultsContainer.appendChild(resultTitle);
                const resultItem = document.createElement('div');
                resultItem.className = 'result-item';
                resultItem.innerHTML = `
                    <div class="result-title">
                            <img src="../static/img/文字_text.png">
                            <a href="article/${item.title}.html">${item.title}</a>
                    </div>
                    <div class="result-description">${item.description}</div>
                `;
                resultsContainer.appendChild(resultItem);
            });
        }

        // 初始化时加载 XML 数据
        window.onload = loadXML;
    </script>

{% endblock %}
  • 搜索页面渲染函数实现
def generate_search_pages():
    """ 根据search_template 生成搜索页面"""
    search_template = env.get_template('search.html')
    search_output_file = os.path.join(OUTPUT_DIR, f"search.html")
    # 创建目录(如果不存在)
    # os.makedirs(os.path.dirname(share_output_file), exist_ok=True)
    with open(search_output_file, 'w', encoding='utf-8') as file:
        file.write(search_template.render())
    print(f"Generated {search_output_file}")

这样搜索功能就完成了。