mirror of
https://github.com/karust/openserp.git
synced 2026-09-08 12:43:56 +08:00
refactor: extract shared RankState, unify rod/goquery parse paths
This commit is contained in:
+64
-76
@@ -21,9 +21,7 @@ func ParseHTML(r io.Reader) ([]core.SearchResult, error) {
|
||||
|
||||
func parseBingDocument(doc *goquery.Document) []core.SearchResult {
|
||||
var results []core.SearchResult
|
||||
rank := 1
|
||||
adRank := 1
|
||||
absoluteRank := 1
|
||||
rank := core.NewRankState(0)
|
||||
|
||||
doc.Find(Selectors.ResultItems).Each(func(_ int, item *goquery.Selection) {
|
||||
isAd := item.Is(Selectors.Ads)
|
||||
@@ -41,99 +39,89 @@ func parseBingDocument(doc *goquery.Document) []core.SearchResult {
|
||||
if titleTag.Length() == 0 {
|
||||
return
|
||||
}
|
||||
href, _ := titleTag.Attr("href")
|
||||
title := bingDocumentTitle(item, titleTag)
|
||||
desc := bingDocumentDescription(item, title)
|
||||
|
||||
link, exists := titleTag.Attr("href")
|
||||
if !exists || link == "" || link == "#" {
|
||||
return
|
||||
if res, ok := assembleBingRow(href, title, desc, isAd, rank); ok {
|
||||
results = append(results, res)
|
||||
}
|
||||
|
||||
title := firstNonEmptyAttr(titleTag, "aria-label", "title")
|
||||
if title == "" {
|
||||
title = normalizeWhitespace(titleTag.Text())
|
||||
}
|
||||
if title == "" {
|
||||
title = extractFirstText(item, Selectors.TitleFallbacks)
|
||||
}
|
||||
if title == "" {
|
||||
return
|
||||
}
|
||||
|
||||
desc := descriptionFromItem(item, title)
|
||||
|
||||
resultRank := rank
|
||||
if isAd {
|
||||
resultRank = adRank
|
||||
adRank++
|
||||
} else {
|
||||
rank++
|
||||
}
|
||||
|
||||
results = append(results, core.SearchResult{
|
||||
Rank: resultRank,
|
||||
AbsoluteRank: absoluteRank,
|
||||
URL: link,
|
||||
Title: title,
|
||||
Description: desc,
|
||||
Ad: isAd,
|
||||
})
|
||||
absoluteRank++
|
||||
})
|
||||
|
||||
return core.AttachFeaturesToFirstResult(core.DeduplicateResults(results), extractBingFeatures(doc))
|
||||
}
|
||||
|
||||
func extractFirstText(item *goquery.Selection, selectors []string) string {
|
||||
for _, selector := range selectors {
|
||||
if tag := item.Find(selector).First(); tag.Length() > 0 {
|
||||
if text := normalizeWhitespace(tag.Text()); text != "" {
|
||||
return text
|
||||
}
|
||||
if label := firstNonEmptyAttr(tag, "aria-label", "title"); label != "" {
|
||||
return label
|
||||
}
|
||||
// assembleBingRow validates an already-extracted Bing row and assigns ranks.
|
||||
// Shared by the rod (browser) and goquery (raw / parse) parsers, which differ
|
||||
// only in how they pull title/href/desc out of the DOM.
|
||||
func assembleBingRow(href, title, desc string, isAd bool, rank *core.RankState) (core.SearchResult, bool) {
|
||||
url := strings.TrimSpace(href)
|
||||
if url == "" || url == "#" || strings.HasPrefix(url, "javascript:") {
|
||||
return core.SearchResult{}, false
|
||||
}
|
||||
if title == "" {
|
||||
return core.SearchResult{}, false
|
||||
}
|
||||
|
||||
resultRank, absoluteRank := rank.Next(isAd)
|
||||
return core.SearchResult{
|
||||
Rank: resultRank,
|
||||
AbsoluteRank: absoluteRank,
|
||||
URL: url,
|
||||
Title: title,
|
||||
Description: desc,
|
||||
Ad: isAd,
|
||||
}, true
|
||||
}
|
||||
|
||||
// bingDocumentTitle reproduces the rod path's title fallback for goquery: the
|
||||
// title anchor's aria-label/title attribute, then its text, then any fallback
|
||||
// selector's text or aria-label.
|
||||
func bingDocumentTitle(item, titleTag *goquery.Selection) string {
|
||||
if title := firstNonEmptyAttr(titleTag, "aria-label", "title"); title != "" {
|
||||
return title
|
||||
}
|
||||
if title := core.NormalizeWhitespace(titleTag.Text()); title != "" {
|
||||
return title
|
||||
}
|
||||
for _, selector := range Selectors.TitleFallbacks {
|
||||
tag := item.Find(selector).First()
|
||||
if tag.Length() == 0 {
|
||||
continue
|
||||
}
|
||||
if text := core.NormalizeWhitespace(tag.Text()); text != "" {
|
||||
return text
|
||||
}
|
||||
if label := firstNonEmptyAttr(tag, "aria-label", "title"); label != "" {
|
||||
return label
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// bingDocumentDescription reproduces the rod path's 3-selector description
|
||||
// fallback plus the strip-title structural fallback. Bing renders snippet text
|
||||
// with heavy source-indentation whitespace, so each candidate is collapsed.
|
||||
func bingDocumentDescription(item *goquery.Selection, title string) string {
|
||||
for _, selector := range []string{Selectors.DescPrimary, Selectors.DescFallback, Selectors.DescAny} {
|
||||
if tag := item.Find(selector).First(); tag.Length() > 0 {
|
||||
if text := core.NormalizeWhitespace(tag.Text()); text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
}
|
||||
return core.NormalizeWhitespace(strings.Replace(item.Text(), title, "", 1))
|
||||
}
|
||||
|
||||
func firstNonEmptyAttr(item *goquery.Selection, attrs ...string) string {
|
||||
for _, attr := range attrs {
|
||||
value, exists := item.Attr(attr)
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if value = normalizeWhitespace(value); value != "" {
|
||||
if value = core.NormalizeWhitespace(value); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// descriptionFromItem extracts a description using the same 4-step fallback
|
||||
// chain as the rod-based browser parser. Bing renders snippet text with heavy
|
||||
// source-indentation whitespace, so each candidate is whitespace-collapsed.
|
||||
func descriptionFromItem(item *goquery.Selection, title string) string {
|
||||
if descTag := item.Find(Selectors.DescPrimary).First(); descTag.Length() > 0 {
|
||||
if text := normalizeWhitespace(descTag.Text()); text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
if descTag := item.Find(Selectors.DescFallback).First(); descTag.Length() > 0 {
|
||||
if text := normalizeWhitespace(descTag.Text()); text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
if descTag := item.Find(Selectors.DescAny).First(); descTag.Length() > 0 {
|
||||
if text := normalizeWhitespace(descTag.Text()); text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
// Structural fallback: strip title from full text
|
||||
return normalizeWhitespace(strings.Replace(item.Text(), title, "", 1))
|
||||
}
|
||||
|
||||
// normalizeWhitespace collapses Bing's snippet-markup whitespace into single
|
||||
// spaces (see core.NormalizeWhitespace).
|
||||
func normalizeWhitespace(s string) string {
|
||||
return core.NormalizeWhitespace(s)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user