使用Golang实现站点搜索功能有哪些方案?
使用Golang实现站点搜索功能有哪些方案? 几乎所有网站都有一个"搜索"按钮,用于在网站内搜索内容。
作为临时解决方案,我目前正在使用Google自定义搜索,但我想知道是否有一种使用Go实现此功能的简单方法。比如:
- 抓取所有HTML网站(是否已在使用Golang Web服务器时将其加载到内存中?)
- 根据表单输入搜索和分析标签内的内容。
- 将结果以列表形式呈现在网页上。
我已经进行了大量搜索,但一无所获。
对于如何寻找思路或具体方法,您有什么建议或线索吗?
更多关于使用Golang实现站点搜索功能有哪些方案?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
通常我会直接查询数据库,如果你正确设置了索引,即使是全文搜索也相当高效。
但一般来说,解决方案很大程度上取决于服务器本身的实现方式……如果你没有在数据库中存储某些内容,就无法真正查询它 😉
更多关于使用Golang实现站点搜索功能有哪些方案?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
NobbZ:
通常人们想要搜索的是填充到模板中的动态数据,而不是围绕它的静态内容。
如果我没有理解错的话,我需要构建一个类似CMS的网站?
acim:
如果你有大量数据,可以考虑使用 ElasticSearch 或 Solr。还有两个 Golang 项目:
这对于大约 100 个 HTML 页面来说是不是太夸张了?
NobbZ:
但通常解决方案很大程度上取决于服务器本身的实现方式……如果你没有在数据库中存储某些内容,就无法真正查询它
有一堆HTML模板。可以将其视为包含两列的数据库:URL和文本。难道没有办法在HTML模板中进行搜索吗?
Sibert:
这对于大约100个HTML页面来说是不是有点小题大做了?
在这种情况下确实如此,但你也应该考虑结果的质量。不过根据你的情况,我会选择Bleeve或Riot,因为你的数据量较小。就像有人说的,你应该只索引内容,或者在索引前先去除HTML标签。
从长远来看,这种方法会增加维护难度。使用包含内容的数据库可能会更简单。但正如我之前所说,如果坚持当前的方法,您需要手动维护页面可搜索内容的索引,因为当您搜索"article"这个词时,可能不希望找到包含<article>标签的文章。
// 代码示例保留原样
嗯,我以为你已经构建了一些东西。
但我不知道你构建了什么以及它是如何工作的,因此我告诉你通常的做法。
你说你有"模板",这些模板本身通常不包含有用的可搜索数据,而是页面框架。内容来自其他地方。你仍然没有说明这个"其他地方"是什么。
// 代码示例保留原样
func main() {
fmt.Println("hello world")
}
1 个赞
如果你有大量数据,可以考虑使用 ElasticSearch 或 Solr。还有两个 Golang 项目:
Go 开源、分布式、简单高效的搜索引擎 - go-ego/riot
一个现代化的 Go 文本索引库。通过在 GitHub 上创建账户来为 blevesearch/bleve 的开发做贡献。
Sibert:
有一堆HTML模板。
通常人们希望搜索填充到模板中的动态数据,而不是围绕它的静态内容。
所以从我的角度来看,搜索模板没有意义。但如果你真的想这样做,那么你需要为模板的可搜索区域构建一个索引,并使用该索引进行搜索。你可能需要研究数据库实现来正确处理这部分。
// 示例代码:构建模板索引
func buildTemplateIndex(templates []Template) map[string]int {
index := make(map[string]int)
for i, template := range templates {
index[template.Name] = i
}
return index
}
NobbZ:
通常这些内容本身不包含有用的可搜索数据,而是页面骨架。实际内容来自其他地方。
嗯,你可以通过至少两种方式使用模板。数据驱动或"纯HTML"。我决定从"纯HTML"开始。所以我想我确实选择了一种传统方式。数据就在模板本身中。
<div class="container">
<div class="submenu">
{{template "sub_form"}}
</div>
<div class="content">
<h1>Lorem ipsum</h1>
<p>Lorem ipsum etc Lorem ipsum etc Lorem ipsum etc Lorem ipsum etc </p>
<br><br>
</div></div>
在Golang中实现站点搜索功能有多种方案,以下是几种实用的方法:
方案一:使用Bleve全文搜索引擎
Bleve是Go语言中最流行的全文搜索引擎库,特别适合站内搜索:
package main
import (
"fmt"
"log"
"net/http"
"html/template"
"github.com/blevesearch/bleve/v2"
)
var index bleve.Index
func init() {
// 创建或打开索引
mapping := bleve.NewIndexMapping()
var err error
index, err = bleve.New("site.bleve", mapping)
if err != nil {
index, err = bleve.Open("site.bleve")
if err != nil {
log.Fatal(err)
}
}
}
// 索引页面内容
func indexPage(id, title, content, url string) error {
doc := map[string]interface{}{
"title": title,
"content": content,
"url": url,
}
return index.Index(id, doc)
}
// 搜索处理
func searchHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("q")
if query == "" {
http.ServeFile(w, r, "search.html")
return
}
searchQuery := bleve.NewMatchQuery(query)
search := bleve.NewSearchRequest(searchQuery)
search.Fields = []string{"title", "content", "url"}
searchResults, err := index.Search(search)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl := template.Must(template.ParseFiles("results.html"))
tmpl.Execute(w, searchResults)
}
func main() {
http.HandleFunc("/search", searchHandler)
http.ListenAndServe(":8080", nil)
}
对应的HTML模板示例:
<!-- search.html -->
<form action="/search" method="get">
<input type="text" name="q" placeholder="搜索网站内容...">
<button type="submit">搜索</button>
</form>
<!-- results.html -->
<h2>搜索结果 ({{.Total}})</h2>
{{range .Hits}}
<div class="result">
<h3><a href="{{.Fields.url}}">{{.Fields.title}}</a></h3>
<p>{{.Fields.content}}</p>
<small>相关度: {{.Score}}</small>
</div>
{{end}}
方案二:使用GoQuery进行HTML解析和内存搜索
对于小型网站,可以使用内存中的简单搜索:
package main
import (
"bytes"
"fmt"
"log"
"net/http"
"strings"
"sync"
"github.com/PuerkitoBio/goquery"
)
type Page struct {
URL string
Title string
Content string
}
var (
pages []Page
mutex sync.RWMutex
)
// 抓取和解析页面
func crawlPage(url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return err
}
title := doc.Find("title").Text()
content := doc.Find("body").Text()
// 清理内容,移除多余空白
content = strings.Join(strings.Fields(content), " ")
mutex.Lock()
pages = append(pages, Page{
URL: url,
Title: title,
Content: content,
})
mutex.Unlock()
return nil
}
// 简单文本搜索
func searchPages(query string) []Page {
var results []Page
query = strings.ToLower(query)
mutex.RLock()
defer mutex.RUnlock()
for _, page := range pages {
if strings.Contains(strings.ToLower(page.Title), query) ||
strings.Contains(strings.ToLower(page.Content), query) {
results = append(results, page)
}
}
return results
}
func searchHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("q")
if query == "" {
fmt.Fprint(w, `
<form method="get">
<input type="text" name="q">
<button type="submit">搜索</button>
</form>
`)
return
}
results := searchPages(query)
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("<h2>找到 %d 个结果</h2>", len(results)))
for _, result := range results {
buf.WriteString(fmt.Sprintf(`
<div style="margin: 20px 0;">
<h3><a href="%s">%s</a></h3>
<p>%s...</p>
</div>
`, result.URL, result.Title, truncateText(result.Content, 200)))
}
w.Header().Set("Content-Type", "text/html")
w.Write(buf.Bytes())
}
func truncateText(text string, length int) string {
if len(text) <= length {
return text
}
return text[:length] + "..."
}
func main() {
// 预先抓取网站页面
urls := []string{
"https://yoursite.com/page1",
"https://yoursite.com/page2",
// 添加更多URL
}
for _, url := range urls {
go crawlPage(url)
}
http.HandleFunc("/search", searchHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
方案三:集成SQLite的FTS5扩展
对于需要持久化存储的场景:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
_ "github.com/mattn/go-sqlite3"
)
func setupSearchDB() *sql.DB {
db, err := sql.Open("sqlite3", "./search.db")
if err != nil {
log.Fatal(err)
}
// 创建FTS5虚拟表
_, err = db.Exec(`
CREATE VIRTUAL TABLE IF NOT EXISTS pages USING fts5(
url,
title,
content,
tokenize = 'porter unicode61'
)
`)
if err != nil {
log.Fatal(err)
}
return db
}
func searchSQLite(db *sql.DB, query string) ([]map[string]string, error) {
rows, err := db.Query(`
SELECT url, title, snippet(pages, 2, '<b>', '</b>', '...', 10)
FROM pages
WHERE pages MATCH ?
ORDER BY rank
`, query)
if err != nil {
return nil, err
}
defer rows.Close()
var results []map[string]string
for rows.Next() {
var url, title, snippet string
if err := rows.Scan(&url, &title, &snippet); err != nil {
return nil, err
}
results = append(results, map[string]string{
"url": url,
"title": title,
"snippet": snippet,
})
}
return results, nil
}
这些方案从简单到复杂,可以根据网站规模和性能需求选择合适的实现方式。Bleve方案提供了最完整的搜索功能,包括相关性排序和高亮显示,适合大多数应用场景。

