网页正文抽取(包含提取图片)

转自:http://bbs.it-home.org/thread-12676-1-1.html

 

 

/**
 *@author Xin Chen
 *Created on 2009-11-11
 *Updated on 2010-08-09
 *Email:  [email protected]
 *Blog:   http://hi.baidu.com/爱心同盟_陈鑫
 *Modified By : Yang @ http://www.chainlt.com
 */
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import dbhelper.TextTool;

/**
 * <p>
 * 在线性时间内抽取主题类(新闻、博客等)网页的正文。 采用了<b>基于行块分布函数</b>的方法,为保持通用性没有针对特定网站编写规则。
 * </p>
 * 
 * @author Chen Xin
 * @version 1.0, 2009-11-11
 */
public class TextExtract {
    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;
    private static List<String> old_lines;
    private static String oldhtml;

    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("(?is)style=\".*?\"", "");// remove css
        html = html.replaceAll("&.{2,5};|&#.{2,5};", " ");// remove special char
        oldhtml = html;
        html = html.replaceAll("(?is)<.*?>", "");
        // <!--[if !IE]>|xGv00|9900d21eb16fa4350a3001b3974a9415<![endif]-->
    }

    private static String getText() {
        lines = Arrays.asList(html.split("\n"));
        old_lines = Arrays.asList(oldhtml.split("\n"));
        indexDistribution.clear();
        boolean haveimg_arr[] = new boolean[old_lines.size()];
        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);
            if (old_lines.get(i).toLowerCase().contains("<img")) {
                haveimg_arr[i] = true;
            }
        }
        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 (haveimg_arr[i]) {
                    continue;
                }
                if (indexDistribution.get(i).intValue() == 0
                        || indexDistribution.get(i + 1).intValue() == 0) {
                    end = i;
                    boolend = true;
                }
            }
            if (boolend) {
                StringBuilder tmp = new StringBuilder();
                // System.out.println(start+1 + "\t\t" + end+1);
                for (int ii = start; ii <= end; ii++) {
                    if (haveimg_arr[ii]) {
                        String img = getImg(old_lines.get(ii));
                        if (img == null)
                            continue;
                        tmp.append(img + "\n");
                        continue;
                    }
                    if (lines.get(ii).length() < 5)
                        continue;
                    tmp.append("<p>" + lines.get(ii) + "</p>\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();
    }

    public static String getImg(String s) {
        String img = TextTool.getBetweenOne(s, "<img", "</img>");
        if (img == null) {
            img = TextTool.getBetweenOne(s, "<img", "/>");
        } else {
            img = "<img" + img + "</img>";
        }
        if (img != null) {
            img = "<img" + img + "/>";
        }
        return img;
    }
}

 

 

 

PS:

Q:经常看见的正则前面的 (?i) (?s) (?m) (?is) (?im) 是什么意思?
A: 称为内联匹配模式,通常用内联匹配模式代替使用枚举值RegexOptions指定的全局匹配模式,写起来更简洁。
  (?i) 表示所在位置右侧的表达式开启忽略大小写模式
  (?s) 表示所在位置右侧的表达式开启单行模式。
  更改句点字符 (.) 的含义,以使它与每个字符(而不是除 \n 之外的所有字符)匹配。
  注意:(?s)通常在匹配有换行的文本时使用
  (?m) 表示所在位置右侧的表示式开启指定多行模式。
  更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,
  而不只是与整个字符串的开头和结尾匹配。
  注意:(?m)只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。
  上面的匹配模式可以组合使用,比如(?is),(?im)。
  另外,还可以用(?i:exp)或者(?i)exp(?-i)来指定匹配的有效范围。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。