基于业务需要,记录由Go实现的网页内容相似度检测算法
算法思想参考:一种通用的网页相似度检测算法
作者使用JAVA实现,这里用Go复现一遍~
网页内容相似度检测算法
一、提取网页文本
为方便记录网页信息,写一个 WebPage 类型:
type WebPage struct {
url string
title string
content string
}
网页标题及内容爬取借助工具chromedp("github.com/chromedp/chromedp"
):
ps:chromedp的源码阅读 蠢作者还在龟速更新。。。
// GetWebPage 提取网页文本
func GetWebPage(url string) WebPage {
// create context
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
// run task list
var res string
var title string
err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.Title(&title),
chromedp.Sleep(1*time.Second),
chromedp.WaitVisible(`body`, chromedp.ByQuery),
chromedp.InnerHTML(`body`, &res, chromedp.ByQuery),
)
if err != nil {
log.Fatal(err)
}
//log.Println(strings.TrimSpace(title))
//log.Println(strings.TrimSpace(res))
return WebPage{
url: url,
title: title,
content: res,
}
}
二、文本分词
学习大家用Go编写的分词器,测试了 Sego 和 gse ,后者是前者和 jieba 的改进,后者字典更全!接下来我们就用gse来分词:
var (
seg gse.Segmenter
posSeg pos.Segmenter
)
func score(webPage1, webPage2 WebPage) float64 {
// 加载默认词典
seg.LoadDict()
posSeg.WithGse(seg)
//分词
webPage1Words := posSeg.Cut(webPage1.title+"\n"+webPage1.content, true)
webPage2Words := posSeg.Cut(webPage2.title+"\n"+webPage2.content, true)
//去掉空格及标点
webPage1Words = posSeg.TrimWithPos(webPage1Words, "x")
webPage2Words = posSeg.TrimWithPos(webPage2Words, "x")
//词频统计
webPage1WordsFre := frequence(webPage1Words)
webPage2WordsFre := frequence(webPage2Words)
//输出详细信息
//fmt.Println(webPage1WordsFre)
//fmt.Println(webPage2WordsFre)
//使用简单共有词判定
return simpleScore(webPage1WordsFre, webPage2WordsFre)
}
使用的是带pos标志的Cut
输出,这是为了方便我们去除一些不必要的输出。观察输出信息,可以发现空格、符号一般都输出x
,因此我们使用TrimWithPos
方法截去所有pos为x
的词组。
整理分词后就可以网页相似度比对了!等等,做词频统计可以降低之后词组比对的工作量。
三、词频统计
直接用map,既可以保证键唯一性,又可以统计词频:
// 词频统计
func frequence(words []gse.SegPos) map[string]int {
var fre map[string]int
fre = make(map[string]int)
for _, word := range words {
text := word.Text
fre[text]++
}
return fre
}
四、网页内容相似度检测
一般来说,相似度分值超过 0.5
的就被视为 这两个网页拥有相同的内容 。
参考的算法思想作者表示,可以通过 计算词向量的余弦相似度 或 判定简单共有词 来实现目的,但蠢作者没看懂余弦相似度算法/(ㄒ…ㄒ)/~~(求大神指教…),这里展出用简单共有词判定相似性的方法:
// 简单共有词判定相似性
func simpleScore(webPage1WordsFre, webPage2WordsFre map[string]int) float64 {
res := 0
//判断有几个相同的词
for key, _ := range webPage1WordsFre {
if webPage2WordsFre[key] > 0 {
res++
}
}
log.Println("网页1有的词数:", len(webPage1WordsFre))
log.Println("网页2有的词数:", len(webPage2WordsFre))
log.Println("网页1和2共有的词数:", res)
webScore := float64(res) / math.Min(float64(len(webPage1WordsFre)), float64(len(webPage2WordsFre)))
log.Println("相似度分值 =", webScore)
return webScore
}
最后验证一下就行啦~
func IsSimilar(url1, url2 string) bool {
return similarScore(url1, url2) >= 0.5
}
func main() {
similar := IsSimilar(`https://pkg.go.dev/crypto/rand`, `https://pkg.go.dev/math/rand`)
if similar {
fmt.Println("两个网页拥有相同的内容")
} else {
fmt.Println("两个网页拥有不同的内容")
}
}
结果如下:
显然Go官方的两个rand算法网页非常相似~
【不断进取,继续探索】