一种提取HTML网页正文的方法

1 篇文章 0 订阅
1 篇文章 0 订阅

这里所说的正文提取主要是针对新闻页面等网页的主体是文字的HTML页面。在做一些与文本处理相关的实验时往往需要大量的文本,虽然网络上已经存在了一些开放数据集如搜狗语料库,但是有的时候也需要根据具体的需求来爬取特定的网站。在我们通过算法获得了需要的HTML页面以后,如何获取页面的正文是一个需要考虑的问题。如果是针对某一个网站的爬取工作,同一网站编码风格往往是一致的,这时只需要简单的浏览一下包含正文的标签,就能发现规律(一个网站的网页正文一般都包含在具有指定id或class的标签下,或者可以根据多个属性的组合来定位正文,因为所有的包含正文的标签结构是一致的)。但是如果在像百度新闻这样的搜索平台中爬新闻,不同的网页来自于不同的网站,没有固定的结构,就需要一种通用的方法。这里我介绍一种自己想到的方法,针对绝大多数的新闻网站都可以有效的提取正文。

这个方法其实思路很简单,分为以下三个步骤:

1. 提取所有的div标签。

2. 提取每一个div标签里包含的p标签内容并保存在变量content中,对于div标签中嵌套的div标签直接过滤掉。

3. 保存具有最长size的content作为该网页的正文。

在研究了很多HTML页面之后,我发现所有的正文都包含在div标签中,而且他们之间的层次关系很近,所以以div标签为出发点可以最大程度减小搜索范围。由于正文多被一个或多个p标签包含,所以单纯的搜索p标签是不切实际的,但是p标签可以作为一种最细化的条件来进行div标签内正文的提取,因为有的时候div标签里还会有其他的标签包括文本内容,比如a标签等,这些是不算正文内容的。当然如果一个div中嵌套了包含正文的div,假如这个div中又恰好包含了含p标签的无用文本,那么这个算法会把正文以及无用文本都提取出来,为了避免这种情况的发生,我们在遍历div标签时如果遇到嵌套的div会直接跳过(反正之后这个嵌套div还是会被我们搜索的)。最后,由于该页面是以文字内容为主体的,所以包含最多字符的div标签,也就是最长size的content为该网页的正文。

下面贴一下demo代码:

package getContent;

import java.io.IOException;
import java.util.Stack;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class getContent {

	static int index;
	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args){
		String url = "http://www.taiwan.cn/taiwan/tw_Story/201301/t20130125_3590110.htm";
		Document doc = null;
		try {
			doc = Jsoup.connect(url).get();
		} catch (IOException e) {
			e.printStackTrace();
		} 
		String content = GetDocContent(doc);
		System.out.println("网页正文如下:\n"+content);
		
	}

	private static String GetDocContent(Document doc) {
		Elements divs =  doc.body().getElementsByTag("div");
		int max = -1;
		String content = null;
		for (int i=0; i<divs.size(); i++) {
			Element div = (Element)divs.get(i);
			String divContent = GetDivContent(div);
			if (divContent.length() > max) {
				max = divContent.length();
				content = divContent;
			}
		}
		return content;
	}
	
	private static String GetDivContent(Element div) {
		StringBuilder sb = new StringBuilder();
		//考虑div里标签内容的顺序,对div子树进行深度优先搜索
		Stack<Element> sk = new Stack<Element>();
		sk.push(div);
		while (!sk.empty()) {
			//
			Element e = sk.pop();
			//对于div中的div过滤掉
			if (e != div && e.tagName().equals("div")) continue;
			//考虑正文被包含在p标签中的情况,并且p标签里不能含有a标签
			if (e.tagName().equals("p") && e.getElementsByTag("a").size() == 0) {
				String className = e.className();
				if (className.length() != 0 && className.equals("pictext")) continue;
				sb.append(e.text());
				sb.append("\n");
				continue;
			} else if (e.tagName().equals("td")) {
			//考虑正文被包含在td标签中的情况
				if (e.getElementsByTag("div").size() != 0) continue;
				sb.append(e.text());
				sb.append("\n");
				continue;
				
			}
			//将孩子节点加入栈中
			Elements children = e.children();
			for (int i=children.size()-1; i>=0; i--) {
				sk.push((Element)children.get(i));
			}
		}
		
		return sb.toString();
	}

}
上面的代码使用了JSoup这个包,这是一个用来解析HTML的第三方开源包,网上有很多介绍JSoup的资料。另外对于div标签的遍历我采用的是深度优先搜索,这里主要是考虑到多个p标签情况时,保证这些p标签的相对位置不变,从而能保证正文被正常提取处理,这里记得每个p标签内容提取之后加一个回车。代码里还考虑了正文被包含在td标签的情况,这是我遇到的一些特殊网页出现的情况,一般并不常见。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值