原理:
类似于,电话降噪(噪音处理)。将多余的杂音(垃圾内容)去除,尽可能过滤为原始内容。
【核心】:贝叶斯过滤算法
叶斯过滤算法是一种典型的基于统计的垃圾邮件过滤技术,
这种理论的基础是通过对大量垃圾邮件的常见关键词进行分析后得出其分布的统计模型,
并由此推算目标是垃圾邮件的概率,再根据所设阈值来判断是否接受邮件。
如文章一共100行。贝叶斯算法:
(spam*legitAll)/( spam*legitAll+ legit*spamAll)
概率 = (当前行垃圾信息量 * 整文正确量) / ( 当前行垃圾信息量 * 整文正确量 + 当前行正确量 * 整文垃圾信息量)
【其次】:
规律性法则 (内容存在一定的规律,比如:“电子商务|淘宝|百度推广”等,视为噪音)
标识性内容 (弃用:改为 【贝叶斯过滤算法】 代替)
超链接内容 (内容是否为超链接,视为噪音)
【切糕】测试结果:
public class BayesSpam {
private float legitAll = 0;
private float legit = 0;
private float spamAll = 0;
private float spam = 0;
String[] keyWords=PropertiesUtil.getPropertiesUtilInstance().getValue("SpamKeyWord").split(",");
public BayesSpam(Elements eles){
calculateLegitAll(eles, keyWords);
}
public boolean isSpamProbability(Element element){
float pbanword=spamProbability(element);
if (Float.parseFloat(PropertiesUtil.getPropertiesUtilInstance().getValue("SpamProbabilityMax"))<pbanword) {
return true;
}
return false;
}
public float spamProbability(Element element){
spam=0;
String content=element.text();
legit=content.length()-1;
filterKeyWord(content, keyWords);
if (0<spam) {
float pbanword = (spam*legitAll)/( spam*legitAll+ legit*spamAll);
return pbanword;
}
return 0;
}
private void filterKeyWord(String strContent, String[] keyWords) {
for (String strKeyWord : keyWords) {
for(int i=0;i<=(strContent.length()-strKeyWord.length());i++) {
int end = i+strKeyWord.length();
if(strContent.substring(i, end).equalsIgnoreCase(strKeyWord)) {
spam++;
}
}
}
}
private void calculateLegitAll(Elements eles, String[] keyWords) {
for (Element element : eles) {
String strContent=element.text();
for (String strKeyWord : keyWords) {
for(int i=0;i<=(strContent.length()-strKeyWord.length());i++) {
legitAll++;
int end = i+strKeyWord.length();
if(strContent.substring(i,end).equalsIgnoreCase(strKeyWord)) {
spamAll++;
}
}
}
}
}
}
public class ContentTacticsUtil {
private static final int maxPageCount=15;//内容最小长度
private static final String[] splitKeys=PropertiesUtil.getPropertiesUtilInstance().getValue("LawDelimiter").split(",");;//规律分隔符
private static final int splitCount=3;//重复次数
private static final String[] markStrs=new String[]{"copyright"};//版权标识
private static final String baiduDomain="baidu.com";//百度域名
private static final String[] timeStrs=new String[]{"yyyyMMdd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy-MM-dd HH:mm"};
public static boolean isContent(String url, String content, Element element){
if (!isContentCount(content)) {
return false;
}
if (isSplit(content)) {
return false;
}
if (isMark(content)) {
//return false;
}
if (isBaidu(url)) {
//return false;
}
if (isHyperlink(element)) {
// return false;
}
if (isValidDate(content)) {
return false;
}
return true;
}
//内容长度法则 (设置阀值,内容小于阀值视为噪音)
private static boolean isContentCount(String content){
if (maxPageCount<content.length()) {
return true;
}
return false;
}
//规律性法则 (内容存在一定的规律,比如:“电子商务|淘宝|百度推广”等,视为噪音)
private static boolean isSplit(String content){
for (String splitKey : splitKeys) {
if (splitCount<content.split(splitKey).length) {
return true;
}
}
return false;
}
//标识性内容 (内容是否为标识,如:版权等)
private static boolean isMark(String content){
for (String markStr : markStrs) {
if (0<=content.toLowerCase().indexOf(markStr)) {
return true;
}
}
return false;
}
//百度
private static boolean isBaidu(String baseurl){
return 0<baseurl.indexOf(baiduDomain);
}
//超链接内容 (内容是否为超链接,视为噪音)
private static boolean isHyperlink(Element content){
for (Node node : content.childNodes()) {
if ("a".equals(node.nodeName())) {
return true;
}
}
return false;
}
public static boolean isValidDate(String sDate) {
for (String timeStr : timeStrs) {
try {
toDate(timeStr, sDate);
return true;
} catch (Exception e) {
}
}
return false;
}
public static Date toDate(String pattern, String dateStr){
DateFormat format1 = new SimpleDateFormat(pattern);
Date reDate;
try {
reDate = format1.parse(dateStr);
} catch (ParseException e) {
throw new RuntimeException(e.getMessage());
}
return reDate;
}
}
/**
* <p>
* 在线性时间内抽取主题类(新闻、博客等)网页的正文。 采用了<b>基于行块分布函数</b>的方法,为保持通用性没有针对特定网站编写规则。
* </p>
*
* @version 1.0, 2009-11-11
*/
public class TextExtractUtil {
private static List<String> lines;
private final static int blocksWidth;
private static int threshold;
private static String html;
private static boolean flag;
private static int start;
private static int end;
private static StringBuilder text;
private static ArrayList<Integer> indexDistribution;
static {
lines = new ArrayList<String>();
indexDistribution = new ArrayList<Integer>();
text = new StringBuilder();
blocksWidth = 3;
flag = false;
/* 当待抽取的网页正文中遇到成块的新闻标题未剔除时,只要增大此阈值即可。 */
/* 阈值增大,准确率提升,召回率下降;值变小,噪声会大,但可以保证抽到只有一句话的正文 */
threshold = 86;
}
public static void setthreshold(int value) {
threshold = value;
}
/**
* 抽取网页正文,不判断该网页是否是目录型。即已知传入的肯定是可以抽取正文的主题类网页。
*
* @param _html
* 网页HTML字符串
*
* @return 网页正文string
*/
public static String parse(String _html) {
return parse(_html, false);
}
/**
* 判断传入HTML,若是主题类网页,则抽取正文;否则输出<b>"unkown"</b>。
*
* @param _html
* 网页HTML字符串
* @param _flag
* true进行主题类判断, 省略此参数则默认为false
*
* @return 网页正文string
*/
public static String parse(String _html, boolean _flag) {
flag = _flag;
html = _html;
preProcess();
// System.out.println(html);
return getText();
}
private static void preProcess() {
html = html.replaceAll("(?is)<!DOCTYPE.*?>", "");
html = html.replaceAll("(?is)<!--.*?-->", ""); // remove html comment
html = html.replaceAll("(?is)<script.*?>.*?</script>", ""); // remove
// javascript
html = html.replaceAll("(?is)<style.*?>.*?</style>", ""); // remove css
html = html.replaceAll("&.{2,5};|&#.{2,5};", " "); // remove special
// char
html = html.replaceAll("(?is)<.*?>", "");
// <!--[if !IE]>|xGv00|9900d21eb16fa4350a3001b3974a9415<![endif]-->
}
private static String getText() {
lines = Arrays.asList(html.split("\n"));
indexDistribution.clear();
for (int i = 0; i < lines.size() - blocksWidth; i++) {
int wordsNum = 0;
for (int j = i; j < i + blocksWidth; j++) {
lines.set(j, lines.get(j).replaceAll("\\s+", ""));
wordsNum += lines.get(j).length();
}
indexDistribution.add(wordsNum);
// System.out.println(wordsNum);
}
start = -1;
end = -1;
boolean boolstart = false, boolend = false;
text.setLength(0);
for (int i = 0; i < indexDistribution.size() - 1; i++) {
if (indexDistribution.get(i) > threshold && !boolstart) {
if (indexDistribution.get(i + 1).intValue() != 0
|| indexDistribution.get(i + 2).intValue() != 0
|| indexDistribution.get(i + 3).intValue() != 0) {
boolstart = true;
start = i;
continue;
}
}
if (boolstart) {
if (indexDistribution.get(i).intValue() == 0
|| indexDistribution.get(i + 1).intValue() == 0) {
end = i;
boolend = true;
}
}
StringBuilder tmp = new StringBuilder();
if (boolend) {
// System.out.println(start+1 + "\t\t" + end+1);
for (int ii = start; ii <= end; ii++) {
if (lines.get(ii).length() < 5)
continue;
tmp.append(lines.get(ii) + "\n");
}
String str = tmp.toString();
// System.out.println(str);
if (str.contains("Copyright") || str.contains("版权所有"))
continue;
text.append(str);
boolstart = boolend = false;
}
}
return text.toString();
}
}