当前位置: 首页 > news >正文

网站自动化锚文本的实现逻辑

锚文本,‌即超链接的文本部分,‌它在网页中扮演着至关重要的角色。‌通过点击锚文本,‌用户可以方便地在网页间进行跳转,‌从而极大地提升了用户体验。‌同时,‌在搜索引擎优化(‌SEO)‌领域,‌锚文本也发挥着不可忽视的作用。‌搜索引擎会通过分析锚文本的内容,‌来判断链接页面的主题和相关性,‌进而影响页面的排名。‌因此,‌合理地设置锚文本,‌对于提升网站的SEO效果具有重要意义。‌

自动化锚文本实现的背景和需求

随着网站内容的不断增多,‌手动设置锚文本变得愈发繁琐和耗时。‌为了提高工作效率,‌减少人力成本,‌自动化锚文本的实现成为了迫切的需求。‌通过自动化手段,‌可以快速地生成大量的锚文本,‌提高网站的内链建设效率,‌进而提升网站的SEO效果。‌

锚文本的收集

在实现自动化锚文本之前,‌首先需要收集网站中的文本内容。‌下面以安企CMS的自动化锚文本功能为例,‌介绍锚文本的收集策略。‌

安企CMS提供了自动提取锚文本和手动提取锚文本两种收集锚文本的方式。如果选择了自动提取锚文本,那么程序就会在用户添加文章的时候,自动解析文章的关键词,并将关键词自动添加为当前文章的锚文本。手动处理的话,则提供了逐个关键词填写以及批量导入关键词的两种方式。
请添加图片描述

自动化锚文本的策略

制定自动化锚文本的策略是关键步骤之一。‌这包括确定锚文本的生成规则,‌如关键词的选择、‌锚文本的长度、‌出现的位置等。
请添加图片描述

  • 自定义锚文本密度
    安企CMS在处理锚文本的生成策略时,‌提供了自定义锚文本密度的选项,用户可以自主选择锚文本的密度。

  • 按关键词由长到短匹配
    安企CMS采用了长度优先的策略,就是说,如果内容中不同锚文本,优先使用长度最长的锚文本。内容里出现AAB这样的关键词时,锚文本会给AAB,而不是AA,或AB

  • 仅匹配一次
    如果文章中有多个相同的锚文本关键词,则只给第一个关键词添加上锚文本,而后续的关键词只进行加粗处理,保证一个内容里同一个关键词锚文本仅出现一次,同一个URL也只做一次锚文本,其它的则会加粗显示。

  • 锚文本生成方式
    安企CMS采用了长度优先的策略。就是说,如果有相同链接的不同锚文本,优先使用长度最长的锚文本。如果文章中有多个相同的锚文本关键词,则只给第一个关键词添加上锚文本,而后续的关键词只进行加粗处理。

安企CMS采用了自动插入关键词以及手动批量更新关键词两种方式,如果选择了自动插入关键词,则会在发布文章的时候,自动将文章中合适的关键词用锚文本来替代,实现锚文本的生成。如果选择了手动批量更新关键词,则会在锚文本页面中,提供批量更新锚文本的功能,用户可以按照自己的需求,手动更新锚文本。

自动化锚文本的实现代码

说明:由于安企CMS 使用的是 GoLang 开发,因此以下代码为 GoLang 语言的实现方式。

func AutoInsertAnchors(anchors []*model.Anchor, content string, link string) string {if len(anchors) == 0 {//没有关键词,终止执行return ""}//获取纯文本字数stripedContent := library.StripTags(content)contentLen := len([]rune(stripedContent))// 获取锚文本密度if PluginAnchor.AnchorDensity < 20 {//默认设置200PluginAnchor.AnchorDensity = 200}// 判断是否是Markdown,如果开头是标签,则认为不是MarkdownisMarkdown := falseif !strings.HasPrefix(strings.TrimSpace(content), "<") {isMarkdown = true}//计算最大可以替换的数量maxAnchorNum := int(math.Ceil(float64(contentLen) / float64(PluginAnchor.AnchorDensity)))// 定义一个替换结构体,用于存储替换的内容type replaceType struct {Key   stringValue string}// 记录已存在的关键词和链接existsKeywords := map[string]bool{}existsLinks := map[string]bool{}var replacedMatch []*replaceTypenumCount := 0//所有的a标签计数,并临时替换掉,防止后续替换影响reg, _ := regexp.Compile("(?i)<a[^>]*>(.*?)</a>")content = reg.ReplaceAllStringFunc(content, func(s string) string {reg := regexp.MustCompile("(?i)<a\\s*[^>]*href=[\"']?([^\"']*)[\"']?[^>]*>(.*?)</a>")match := reg.FindStringSubmatch(s)if len(match) > 2 {existsKeywords[strings.ToLower(match[2])] = trueexistsLinks[strings.ToLower(match[1])] = true}key := fmt.Sprintf("{$%d}", numCount)replacedMatch = append(replacedMatch, &replaceType{Key:   key,Value: s,})numCount++return key})//所有的strong标签替换掉reg, _ = regexp.Compile("(?i)<strong[^>]*>(.*?)</strong>")content = reg.ReplaceAllStringFunc(content, func(s string) string {key := fmt.Sprintf("{$%d}", numCount)replacedMatch = append(replacedMatch, &replaceType{Key:   key,Value: s,})numCount++return key})// 匹配 Markdown 格式的锚文本,同时要考虑别替换掉图片// [keyword](url)reg, _ = regexp.Compile(`(?i)(.?)\[(.*?)]\((.*?)\)`)content = reg.ReplaceAllStringFunc(content, func(s string) string {match := reg.FindStringSubmatch(s)if len(match) > 2 && match[1] != "!" {existsKeywords[strings.ToLower(match[2])] = trueexistsLinks[strings.ToLower(match[3])] = true}key := fmt.Sprintf("{$%d}", numCount)replacedMatch = append(replacedMatch, &replaceType{Key:   key,Value: s,})numCount++return key})// Markdown 格式的加粗// **Keyword**reg, _ = regexp.Compile(`(?i)\*\*(.*?)\*\*`)content = reg.ReplaceAllStringFunc(content, func(s string) string {key := fmt.Sprintf("{$%d}", numCount)replacedMatch = append(replacedMatch, &replaceType{Key:   key,Value: s,})numCount++return key})//过滤所有属性,防止在自动锚文本的时候,会将标签属性也替换reg, _ = regexp.Compile("(?i)</?[a-z0-9]+(\\s+[^>]+)>")content = reg.ReplaceAllStringFunc(content, func(s string) string {key := fmt.Sprintf("{$%d}", numCount)replacedMatch = append(replacedMatch, &replaceType{Key:   key,Value: s,})numCount++return key})if len(existsLinks) < maxAnchorNum {//开始替换关键词for _, anchor := range anchors {if anchor.Title == "" {continue}if strings.HasSuffix(anchor.Link, link) {//遇到当前url,跳过continue}//已经存在存在的关键词,或者链接,跳过if existsKeywords[strings.ToLower(anchor.Title)] || existsLinks[strings.ToLower(anchor.Link)] {continue}//开始替换replaceNum := 0replacer := strings.NewReplacer("\\", "\\\\", "/", "\\/", "{", "\\{", "}", "\\}", "^", "\\^", "$", "\\$", "*", "\\*", "+", "\\+", "?", "\\?", ".", "\\.", "|", "\\|", "-", "\\-", "[", "\\[", "]", "\\]", "(", "\\(", ")", "\\)")matchName := replacer.Replace(anchor.Title)reg, _ = regexp.Compile(fmt.Sprintf("(?i)%s", matchName))content = reg.ReplaceAllStringFunc(content, func(s string) string {replaceHtml := ""key := ""if replaceNum == 0 {//第一条替换为锚文本if isMarkdown {replaceHtml = fmt.Sprintf("[%s](%s)", s, anchor.Link)} else {replaceHtml = fmt.Sprintf("<a href=\"%s\" data-anchor=\"%d\">%s</a>", anchor.Link, anchor.Id, s)}key = fmt.Sprintf("{$%d}", numCount)//加入计数existsLinks[anchor.Link] = trueexistsKeywords[anchor.Title] = true} else {//其他则加粗if isMarkdown {replaceHtml = fmt.Sprintf("**%s**", s)} else {replaceHtml = fmt.Sprintf("<strong data-anchor=\"%d\">%s</strong>", anchor.Id, s)}key = fmt.Sprintf("{$%d}", numCount)}replaceNum++replacedMatch = append(replacedMatch, &replaceType{Key:   key,Value: replaceHtml,})numCount++return key})//判断数量是否达到了,达到了就跳出if len(existsLinks) >= maxAnchorNum {break}}}//关键词替换完毕,将原来替换的重新替换回去,需要倒序for i := len(replacedMatch) - 1; i >= 0; i-- {content = strings.Replace(content, replacedMatch[i].Key, replacedMatch[i].Value, 1)}// 返回替换后的内容return content
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Spring websocket并发发送消息异常的解决
  • 保研考研机试攻略:第三章——数学(3)
  • 基于Arch的轻量级发行版Archcraft结合内网穿透实现远程SSH连接
  • Python居然有这么多文件扩展
  • Docker手动在虚拟机上部署前端、后端和数据库
  • SAP LE学习笔记04 - MM与WM跨模块收货到仓库的流程中 如何既创建TR又同时立即在前台创建TO
  • jmeter安装及环境变量配置、Jmeter目录介绍和界面详解
  • Pcie学习笔记(24)
  • Mysql原理与调优-Mysql的内存结构
  • Flask框架探索:轻量级与灵活性的完美结合
  • 入门mysql数据库
  • 空状态设计教程:连接用户体验的桥梁
  • 制造企业MES系统质检管理的应用
  • 【杂乱算法】前缀和与差分
  • [Linux#42][线程] 锁的接口 | 原理 | 封装与运用 | 线程安全
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 07.Android之多媒体问题
  • 4个实用的微服务测试策略
  • Javascript Math对象和Date对象常用方法详解
  • JavaScript类型识别
  • js数组之filter
  • mysql中InnoDB引擎中页的概念
  • npx命令介绍
  • Sass Day-01
  • 聚簇索引和非聚簇索引
  • 聊聊directory traversal attack
  • 数据结构java版之冒泡排序及优化
  • 微服务框架lagom
  • 移动端唤起键盘时取消position:fixed定位
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • #微信小程序:微信小程序常见的配置传旨
  • $.ajax中的eval及dataType
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (02)Unity使用在线AI大模型(调用Python)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (done) 两个矩阵 “相似” 是什么意思?
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (十七)Flink 容错机制
  • (转)ABI是什么
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • .NET Micro Framework初体验
  • .net Stream篇(六)
  • .NET企业级应用架构设计系列之开场白
  • /proc/vmstat 详解
  • ::before和::after 常见的用法
  • ;号自动换行
  • @Value获取值和@ConfigurationProperties获取值用法及比较(springboot)
  • [100天算法】-不同路径 III(day 73)
  • [Algorithm][动态规划][01背包问题][目标和][最后一块石头的重量Ⅱ]详细讲解
  • [Android Studio] 开发Java 程序
  • [BIZ] - 1.金融交易系统特点
  • [C#][opencvsharp]opencvsharp sift和surf特征点匹配
  • [CLIP-VIT-L + Qwen] 多模态大模型源码阅读 - 语言模型篇(4)
  • [cocos2d-x]关于CC_CALLBACK