NLP分词后的文本如何在页面中高亮显示

我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!

需求

公司有个项目,是做文本语义化分析工具,采用NLP分词工具进行模型训练,前端这边的交互主要是有两点: 文本高亮显示手动训练

先说说文本高亮显示

文本高亮显示

首先文本是段落结构,存在多个段落,

其次是每段文本中,存在多句文本;

例如:‘武汉大学坐落于武汉市珞珈山

所以后端返回的数据格式形式

const data = [{
  "paraId": 0,
  "paraText": "武汉大学坐落于武汉市珞珈山",
  "paraEntity": [{
     "category": "名词",
     "labelText": "武汉大学",
    "startIndex": 0,
    "endIndex": 4,
    "color": 'rgba(240,215,12,.5)'
  }, {
     "category": "地名",
     "labelText": "武汉",
    "startIndex": 7,
    "endIndex": 9,
    "color": '#00baff'
  }]
}];
复制代码

前端如何在文本中高亮显示呢?

最开始的方法:

硬核replace

将后端返回的数据格式提取形成一个数组,里面的元素以对象的形式存储,然后循环遍历,将文本中需要高亮的词替换成

存在的问题很明显,无法针对相同的词汇给出不同的词性

比如: 

武汉大学坐落于武汉市珞珈山

这里识别前面武汉大学是一个名词,武汉是一个地名,通过上述方法,会将所有武汉标记为地名或者其它词性;

那么该如何改进呢?我想到的是拆分法

拆分法

将文本中每一个字遍历,替换成em标签;

拆分形成的em标签文本,我们可以按每个字去显示不同的颜色,也就是词性;

<em>武</em><em>汉</em><em>大</em><em>学</em><em>坐</em><em>落</em><em>于</em><em>武</em><em>汉</em><em>市</em><em>珞</em><em>珈</em><em>山</em>
复制代码

那么,如何定位呢,

这里我们用到了html 中的data属性

data属性

data-* 全局属性 是一类被称为自定义数据属性的属性,它赋予我们在所有 HTML 元素上嵌入自定义数据属性的能力,并可以通过脚本在 HTMLDOM 表现之间进行专有数据的交换。

使用js中的 getAttribute 方法这样我们可以通过后台给定的起始位置和长度,定位到相应的文本

/* 切割和拼成em */
// paranum是段落数 index是这句话中第几个字
createElement(textStr, tagName = 'em', k) {
  let NewTextStr = '';
  for (let i = 0; i < textStr.length; i += 1) {
    NewTextStr += `<${tagName} data-paranum="${k}" data-index="${i}">${textStr[i]}</${tagName}>`;
  }
  return NewTextStr;
},
复制代码

注意,由于文本中会存在特殊文号,比如书名号‘《》’, 括号‘()’,所以我们需要对此进行转义

/* 转义 */
escapeStr(str) {
  let regStr = '';
  const specialArry = ['(', ')', '[', ']', '\\', '+', '*', '?', '.', '|'];
  for (let k = 0; k < str.length; k += 1) {
    if (specialArry.indexOf(str[k]) > -1) {
      regStr += `\\${str[k]}`;
    } else {
      regStr += str[k];
    }
  }
  return regStr;
},
复制代码

效果

总结思路

1、先将段落文本每一个字切割合并成em标签,并加上data属性(段落以及字位置);

2、循环遍历数据,最小单位为每个字,所以这里会遍历3次;

data.forEach((e) => {
  for (let m = 0; m < e.paraEntity.length; m += 1) {
   for (let i = 0; i < e.paraEntity[m].labelText.length; i += 1) {
   }
  }
}
复制代码

3、在循环中拼接非高亮的字符串以及高亮的字符串

注意: notHightStrnotHightStr是在forEach中声明的变量

notHightStr = `<em data-paranum="${k}" data-index="${i + e.paraEntity[m].startIndex}">${e.paraEntity[m].labelText[i]}</em>`;

hightStr = `<em data-paranum="${k}" data-index="${i + e.paraEntity[m].startIndex}" style="background: ${ e.paraEntity[m].color};">${e.paraEntity[m].labelText[i]}</em>`;
复制代码

4、替换

注意: emStr 是在循环外

let emStr = createElement(fileText, 'em', k);

emStr = emStr.replace(notHightStr, hightStr);
复制代码

5、生成拼接后的高亮html

markTxt += `<p>${emStr}</p>`;
复制代码

完整代码

github.com/642134542/H…

最后

在项目中,因为开发者不同,返回的数据会有细微差别,除了字段名称外,数据结构以及每个字的位置都会有所偏差,所以需要根据实际进行调整,总的来说,拆分的思想便于定位

但是,相应的存在弊端,增加html标签循环嵌套过多影响性能等。

除了用语义模型进行识别外,还需要人工进行调整,一般的交互是通过右键点击,获取到data属性以及词语,再与后端交互,达到手动校正的效果。

Guess you like

Origin juejin.im/post/7068545836271009823