我想让团队成员帮我抽取了几千条的博客文章数据,存在临时的数据库中,然后我尝试着进行处理。等待处理成功后在集成到正式的环境中。
博客标签的提取
提取博客的标签一直是一个很头疼的事情,因为自建博客,标签不一,有的博客有标签,有的博客没有标签,有的博客内容是统一方面的内容,但是标签却不太相同。
刚开始想的是,自己定义一套系统默认的博客标签,然后将抽取到的博客文章与标签对应,但是测试完之后发现,标签的种类是在太多了,自己想的几十个标签,根本无法涵盖庞杂的内容,有超过半数的博客都被分到未知标签的类别中。
后来经过在github的寻找,找到了一个计算机领域的词典库,发现里面包含了掘金博客平台的所有标签信息,我可以将这些标签,导入数据库,作为我们博客平台的标签信息。
计算机词典库为:https://github.com/blogstorm/cs-dict
我将这个词典的内容导入到了我的数据库中,作为博客系统定义好的标签星信息。
看了看词典的内容,发现有的标签可以衍生出很多的同义词,有很多词都能代表他,由于我的抽取方法是:
所以我随缘的在一些标签的描述中,随缘的加上了一些关键词,用“#”分割,只要句子中出现了相关的标签,或者有关标签的关键词,则就可以将这篇文章打上标签了。
- 提取标签
/**
* 获取博客的标签 ——有的博客本身有标签 但是需要统一成系统有的标签 有的博客没有标签,就需要构造标签
* @param tagList 系统定义好的标签
* @param articleBean 文章的信息
* @return
*/
public static List<String> getTag(List<Tag> tagList,ArticleBean articleBean)
{
String tags = articleBean.getTags();//如果文章本身有标签 存在这里
String clean_content = articleBean.getClean_content();//清洗后的文章内容
String title = articleBean.getTitle();//文章的标题
Map<String,Integer> map = new HashMap<>();
List<String> resultTag = new ArrayList<>();//存储提取标签的结果
if(!clean_content.equals("")) //当内容不为空时才能提取tag
{
//当这篇博客本身就有tag时
if(!tags.equals(""))
{
String temp_tag [] = tags.split(",");
//对比已有标签和系统定义标签的相似度标签的相似度
/**
* 对比编辑距离 对比前需要将汉字全部转化为拼音 方便比对编辑距离
*
*/
for(int i = 0 ; i < temp_tag.length ; i++)
{
for(Tag tag :tagList)
{
String default_tag = Word2PinYin(tag.getName());
String desc [] = tag.getDescription().split("#");
String now_tag = Word2PinYin(temp_tag[i]);
if(StringHasChinese(tag.getName()) || StringHasChinese(temp_tag[i]))
{
if(editDistance(default_tag,now_tag) <3)
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}
}
else
{
if(editDistance(default_tag,now_tag) ==0)
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}
}
for(int k = 0 ; k<desc.length ; k++)
{
if((StringHasChinese(now_tag) || StringHasChinese(desc[k]) )&& !desc[k].equals(""))
{
if(editDistance(Word2PinYin(desc[k]),now_tag) <3)
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}
}
else {
if(editDistance(Word2PinYin(desc[k]),now_tag) ==0)
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}
}
}
}}
}
/**
* 如果没有自带标签 或者自带标签与系统中的标签无法匹配上时,需要我们根据博客的标题和文字内容进行 字串对比 看能不能匹配到对应的标签
*/
for(Tag tag :tagList)
{
String default_tag = Word2PinYin(tag.getName());
String desc [] = tag.getDescription().split("#");
if(clean_content.toLowerCase().contains(default_tag.toLowerCase()))
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}
else if(title.toLowerCase().contains(default_tag.toLowerCase()))
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}else
{
for(int k = 0 ; k<desc.length ; k++)
{
if(clean_content.toLowerCase().contains(desc[k].toLowerCase()) && !desc[k].equals(""))
{
if(!map.containsKey(tag.getName())) map.put(tag.getName(),0);
map.put(tag.getName(),map.get(tag.getName()) +1);
resultTag.add(tag.getName());
continue;
}
}
}
}
}
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if(o2.getValue() > o1.getValue())
return 1;
else if(o2.getValue() < o1.getValue())
return -1;
else return 0;
}
});
int index = 0;
List<String> result = new ArrayList<>();
for(Map.Entry<String, Integer> t:list){
result.add(t.getKey());
if(++index >2) break;
}
return result;
}
- 计算编辑距离
/**
* 利用动态规划求编辑距离
* @param str1
* @param str2
* @return
*/
public static int editDistance(String str1, String str2) {
Preconditions.checkNotNull(str1);
Preconditions.checkNotNull(str2);
int len1 = str1.length();
int len2 = str2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= len2; j++) {
dp[0][j] = j;
}
for (int i = 0; i < len1; i++) {
char c1 = str1.charAt(i);
for (int j = 0; j < len2; j++) {
char c2 = str2.charAt(j);
//if last two chars equal
if (c1 == c2) {
//update dp value for +1 length
dp[i + 1][j + 1] = dp[i][j];
} else {
dp[i + 1][j + 1] = 1 + min(dp[i+1][j], min(dp[i][j+1], dp[i][j]));
}
}
}
return dp[len1][len2];
}
- 判断字符串时候含有中文,以及将中文转换成拼音
/**
* 将有中文的字符串转成拼音
* @param str
* @return
*/
public static String Word2PinYin(String str)
{
StringBuffer sb = new StringBuffer();
for(int i = 0 ; i<str.length() ; i++)
{
char temp = str.charAt(i);
//如果是小写字母
if(temp >=97 && temp <= 122)
{
sb.append(temp);
}
else if(temp >=65 && temp<=90) //如果是大写字母 转化成小写字母
{
sb.append(Character.toLowerCase(temp));
}
else if(isChinese(temp)) //如果是中文
{
sb.append(PinYinUtil.getStringPinYin(String.valueOf(temp)));
}
else //
{
sb.append(temp);
}
}
return sb.toString();
}
/**
* 判断一个字符串中是否含有中文
* @param str
* @return
*/
public static boolean StringHasChinese(String str)
{
for(int i = 0 ; i< str.length() ; i++)
{
if(isChinese(str.charAt(i)))
{
return true;
}
}
return false;
}
/**
* 判断一个字符是否是中文
* @param c
* @return
*/
public static boolean isChinese(char c) {
Character.UnicodeScript sc = Character.UnicodeScript.of(c);
if (sc == Character.UnicodeScript.HAN) {
return true;
}
return false;
}
// 转换一个字符串
public static String getStringPinYin(String str) {
StringBuilder sb = new StringBuilder();
String tempPinyin = null;
for (int i = 0; i < str.length(); ++i) {
tempPinyin = getCharacterPinYin(str.charAt(i));
if (tempPinyin == null) {
// 如果str.charAt(i)非汉字,则保持原样
sb.append(str.charAt(i));
} else {
sb.append(tempPinyin);
}
}
return sb.toString();
}
思路:
(1)如果这篇文章本身就有标签,则将中文标签转化为拼音,与系统自定义的标签与有关标签的关键词(也转化为拼音)依次比较编辑距离,如果相等,则说明是同一个标签,则用系统定义的标签标记它。
(2)如果这篇文章没有标签,或者自带标签与系统中的标签无法匹配上时,需要我们根据博客的标题和文字内容进行 字串对比 看能不能匹配到对应的标签。
(3)最终输出出现次数最多的三个标签(我们用hashmap来统计每个标签的出现次数)
最终将文章打上了标签,但是还有不到百分之二十的博客,可能内容比较偏,还是被标记为未分类,只能之后在进行处理了
对标签进行分类
系统定义了五百多个标签,我们想要更好的展示到前端界面上,则需要对这些标签进行分类,才可以做到界面上进行分类展示。
= =在这里没有想到什么好办法,我人工定义了二十五个分类,然后用了几个小时,人工将这些标签进行了分类,
真——人工智能
将标签写入分类的描述中,用“#”分割
然后通过给文章打的标签,进行文章的分类就好了
/**
* 有了标签之后 根据博客的标签 匹配对应的博客分类
* @param classify_list
* @param tag_list
* @return
*/
public static List<String> getClassify(List<Classify> classify_list ,List<String> tag_list)
{
List<String> classify_result = new ArrayList<>();
for(String tag :tag_list)
{
String deal_tag = Word2PinYin(filtration(tag));
for(Classify classify :classify_list)
{
String classify_desc [] = classify.getDescription().split("#");
for(int i = 0 ; i< classify_desc.length ; i++)
{
String deal_decs = Word2PinYin(filtration(classify_desc[i]));
if(editDistance(deal_decs,deal_tag) == 0)
{
classify_result.add(classify.getName());
}
}
}
}
return new ArrayList<String>(new HashSet<String>(classify_result));
}
至此,除了博客的摘要没有更好的替代方法,其他所有的数据都清洗完成了,可以进行下一步的工作了。