package whu.util.tools;
import java.util.LinkedHashSet;
import java.util.Stack;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.PrototypicalNodeFactory;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.http.ConnectionManager;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import whu.common.Global;
import whu.util.tags.FontTag;
public class FindTag {
/**
* 通过给定的初始节点集合和指定的匹配tag序列,依次遍历初始节点集合的每一个元素,从每一个初始节点中过滤出相应的唯一一个节点,并把所有的返回数组
* @param sourceList
* @param sequence
* @return根据我们要求而过滤得出的节点
*/
public static Node[] getTargetNodeArray(NodeList sourceList,String[] sequence){
if(sourceList==null||sourceList.size()==0)
{
return null;
}
else
{
Node[] roots = sourceList.toNodeArray();
int cNum = roots.length;
LinkedHashSet found = new LinkedHashSet();
for (int i = 0; i < cNum; i++) {
Node newNode = findNode(roots[i], sequence);
if(newNode!=null)
found.add(newNode);
}
return (Node[])found.toArray(new Node[found.size()]);
}
}
/**
* 通过给定的初始节点集合和指定的匹配tag序列,依次遍历初始节点集合的每一个元素,从中过滤出相应的所有节点,并把所有的返回数组
* @param sourceList
* @param sequence
* @return根据我们要求而过滤得出的节点
*/
public static Node[] getAllTargetNodeArray(NodeList sourceList,String[] sequence){
if(sourceList==null||sourceList.size()==0)
{
return null;
}
else
{
Node[] roots = sourceList.toNodeArray();
int cNum = roots.length;
LinkedHashSet found = new LinkedHashSet();
for (int i = 0; i < cNum; i++) {
Node[] newNodes = findNodes(roots[i], sequence);
if(newNodes!=null&&newNodes.length!=0)
for(Node n:newNodes)
found.add(n);
}
return (Node[])found.toArray(new Node[found.size()]);
}
}
/**
* 通过给定的一个初始节点和指定的匹配tag序列,依次遍历初始节点的孩子集合的每一个元素,从中过滤出相应的唯一一个节点,并返回该节点
* @param sourceList
* @param sequence
* @return根据我们要求而过滤得出的节点
*/
public static Node getTargetNode(Node sourceNode,String[] sequence){
if(sourceNode!=null)
{
//System.out.println(" sourceNode not null------ ");
Node newNode = findNode(sourceNode, sequence);
if(newNode!=null)
return newNode;
else
{
//System.out.println("result Node null ------ ");
return null;
}
}
else
{
//System.out.println(" sourceNode null------ ");
return null;
}
}
/**
* 通过给定的一个初始节点和指定的匹配tag序列,找出初始节点的子节点中符合要求的所有节点,并把所有的那些相似节点以数组的形式返回
* @param sourceList
* @param sequence
* @return根据我们要求而过滤得出的节点
*/
public static Node[] getSimilarNodeArray(Node sourceNode,String[] sequence){
if(sourceNode!=null)
{
Node[] similarNodes = findNodes(sourceNode,sequence);
return similarNodes;
}
else
return null;
}
/**
* 通过给定的一个节点,按匹配序列进行查找,返回符合条件的唯一一个节点
* @param source
* @param sequence
* @return
*/
public static Node findNode(Node source, String[] sequence) {
Stack curNode = new Stack();
curNode.push(source);
return matchTags(curNode, sequence);
}
/**
* 通过给定的一个节点,按匹配序列进行查找,返回符合条件的所有相似节点
* @param source
* @param sequence
* @return
*/
public static Node[] findNodes(Node source, String[] sequence) {
Stack curNode = new Stack();
curNode.push(source);
return matchTags(curNode, sequence,true);
}
public static final int FIND_SUB = 0; // 找子节点
public static final int FIND_SIB = 1; // 找同级节点
public static final int FIND_END = 2; // 结束
/**
* 本方法必须要求每个初始根节点必须有children。该方法返回符合条件的唯一一个节点
* @param curNode
* @param sequence
* @return
*/
public static Node matchTags(Stack curNode, String[] sequence) {
int state = FIND_SUB; // 开始
int i=0; //记录匹配的tag序号
int depth = sequence.length; //记录查找的深度
int[] index = new int[depth]; //记录每级匹配的序列索引,即那一级的所有孩子的序列号
while (state != FIND_END) {
Node cNode = (Node) curNode.pop(); // 当前节点
if (state == FIND_SUB) { // 查找子节点
if(i<depth)
{
//下面这一步的getChildren可能会报错
NodeList cList = cNode.getChildren();
if(cList!=null)
{
Node[] subNodes = cNode.getChildren().extractAllNodesThatMatch(new TagNameFilter(sequence[i])).toNodeArray();
if (subNodes == null || subNodes.length == 0) { // 没有子节点
curNode.push(cNode);
state = FIND_SIB; // 下一次需要找同级节点
}
else
{
curNode.push(cNode);
curNode.push(subNodes[0]);
index[i]=0;//第i级的当前测试节点索引为0
i++;
state = FIND_SUB;
}
}
else
{
curNode.push(cNode);
state = FIND_SIB; // 下一次需要找同级节点
}
}
else if(i==depth)//说明已经匹配到设定的深度了,可以取出该节点了
return cNode;
}
else if (state == FIND_SIB) { // 查找同级节点
if (curNode.isEmpty()) {
state = FIND_END; // 已经没有可以找的了,需要退出查找过程,反之栈里面一定含有父节点,所以i>0
}
else {
Node parentNode = (Node) curNode.peek();
Node[] sibNodes = parentNode.getChildren().extractAllNodesThatMatch(new TagNameFilter(sequence[i-1])).toNodeArray();
int sibNum = sibNodes.length;
if(index[i-1]+1<sibNum){ //存在下一个同级节点
curNode.push(sibNodes[index[i-1] + 1]);
index[i-1]+=1;
state = FIND_SUB; // 需要查找子节点
}
else{ // 这就是最后一个同级节点,故要返回上一级
state = FIND_SIB;
index[i-1]=0; //第i级匹配索引重设为0
i--;
}
}
}
}
return null;
}
/**
* 本方法必须要求初始根节点必须有children。该方法返回符合条件的一批相似节点
* @param curNode
* @param sequence
* @param similar 为true标识是查找一个相似的序列
* @return
*/
public static Node[] matchTags(Stack curNode, String[] sequence,Boolean similar) {
int state = FIND_SUB; // 开始
int i=0; //记录匹配的tag序号
int depth = sequence.length; //记录查找的深度
int[] index = new int[depth]; //记录每级匹配的序列索引,即那一级的所有孩子的序列号
LinkedHashSet found = new LinkedHashSet(); //记录查出符合要求的节点数组
if(true==similar)
{
while (state != FIND_END) {
Node cNode = (Node) curNode.pop(); // 当前节点
if (state == FIND_SUB) { // 查找子节点
if(i<depth-1)
{
//下面这一步的getChildren可能会报错,不过又可能不会报错,因为我压进栈的节点都是在那一级符合我的要求的节点,就肯定是有子节点的,除非到了匹配的最后一级最后
NodeList cList = cNode.getChildren();
if(cList!=null)
{
Node[] subNodes = cList.extractAllNodesThatMatch(new TagNameFilter(sequence[i])).toNodeArray();
if (subNodes == null || subNodes.length == 0) { // 没有子节点
curNode.push(cNode);
state = FIND_SIB; // 下一次需要找同级节点
}
else
{
curNode.push(cNode);
curNode.push(subNodes[0]);
index[i]=0;//第i级的当前测试节点索引为0
i++;//进入下一级
state = FIND_SUB;
}
}
else
{
curNode.push(cNode);
state = FIND_SIB; // 下一次需要找同级节点
}
}
else if(i==depth-1)
{
NodeList cList = cNode.getChildren();
if(cList!=null)
{
Node[] subNodes = cList.extractAllNodesThatMatch(new TagNameFilter(sequence[i])).toNodeArray();
if (subNodes != null && subNodes.length != 0) { // 有子节点,由于是最后一级,故全部采集
for(int j=0;j<subNodes.length;j++)
found.add(subNodes[j]);
}
}
curNode.push(cNode);
state = FIND_SIB; // 下一次需要找同级节点
}
}
else if (state == FIND_SIB) { // 查找同级节点
if (curNode.isEmpty()) {
state = FIND_END; // 已经没有可以找的了,需要退出查找过程,反之栈里面一定含有父节点,所以i>0
}
else {
Node parentNode = (Node) curNode.peek();
Node[] sibNodes = parentNode.getChildren().extractAllNodesThatMatch(new TagNameFilter(sequence[i-1])).toNodeArray();
int sibNum = sibNodes.length;
if(index[i-1]+1<sibNum){ //存在下一个同级节点
curNode.push(sibNodes[index[i-1] + 1]);
index[i-1]+=1;
state = FIND_SUB; // 需要查找子节点
}
else{ // 这就是最后一个同级节点,故要返回上一级
state = FIND_SIB;
index[i-1]=0; //第i级匹配索引重设为0
i--;
}
}
}
}
return (Node[])found.toArray(new Node[found.size()]);
}
return null;
}
/**
* 根据给定的节点名字、标签属性、标签值提取出符合条件的所有tag节点
* @param url
* @param tagName
* @param attributeName
* @param attributeValue
* @return符合条件的List
*/
public static NodeList getNodeList(String url,String tagName,String attributeName,String attributeValue)
{
ConnectionManager manager;
manager = org.htmlparser.lexer.Page.getConnectionManager();
Parser parser;
try
{
parser = new Parser(manager.openConnection(url));
parser.setEncoding(Global.PAGE_ENCODING);
//下面的节点注册一定要放在最前面,才能把指定节点的所有孩子节点都按我们的要求解析(有些自定义标签必须能够解析)
//注册新的结点解析器,其实我觉得在htmlparser的源码里面可以直接编写新的节点类,然后重新编译
PrototypicalNodeFactory factory = new PrototypicalNodeFactory ();
factory.registerTag(new FontTag());
parser.setNodeFactory(factory);
NodeFilter filterAttribute = new HasAttributeFilter(attributeName,attributeValue);
NodeFilter filterTag = new TagNameFilter(tagName);
NodeFilter andFilter = new AndFilter(filterAttribute, filterTag);
return parser.parse(andFilter);//如果没有对应的节点,则会返回size=0的NodeList
}
catch(ParserException e)
{
e.printStackTrace();
return null;
}
}
/**
* 根据给定的节点名字提取出符合条件的所有tag节点
* @param url
* @return符合条件的List
*/
public static NodeList getNodeList(String url,String tagName)
{
ConnectionManager manager;
manager = org.htmlparser.lexer.Page.getConnectionManager();
Parser parser;
try
{
parser = new Parser(manager.openConnection(url));
parser.setEncoding(Global.PAGE_ENCODING);
//下面的节点注册一定要放在最前面,才能把指定节点的所有孩子节点都按我们的要求解析(有些自定义标签必须能够解析)
//注册新的结点解析器,其实我觉得在htmlparser的源码里面可以直接编写新的节点类,然后重新编译
PrototypicalNodeFactory factory = new PrototypicalNodeFactory ();
factory.registerTag(new FontTag());
parser.setNodeFactory(factory);
NodeFilter filterTag = new TagNameFilter(tagName);
return parser.parse(filterTag);//如果没有对应的节点,则会返回size=0的NodeList
}
catch(ParserException e )
{
e.printStackTrace();
return null;
}
}
}
自定义的标签,某些不常用的标签htmlparser并不支持,需要自己拓展,比如EM 、 FONT等
package whu.util.tags;
import org.htmlparser.tags.CompositeTag;
public class FontTag extends CompositeTag{
private static final String[] mIds = new String[] {"FONT"};
public String[] getIds (){
return (mIds);
}
public String[] getEnders (){
return (mIds);
}
}
分享到:
相关推荐
HTMLParser通常会提供一个解析器类,通过这个类,我们可以读取HTML字符串或文件,并将其转换为可操作的对象模型。例如,我们可以使用解析器的`ParseDocument`方法解析HTML源代码,然后通过这个文档对象来访问DOM...
在Java开发中,如果你需要处理或分析HTML内容,HTMLParser是一个非常有用的工具。这个库提供了一套API,使得开发者能够方便地遍历、修改或者提取HTML文档中的信息。 在描述中提到的“org.htmlparser.Node”和其他的...
1. **创建自定义解析器**: 首先,我们需要继承Python的`HTMLParser`类,并重写其方法,如`handle_starttag`、`handle_endtag`和`handle_data`,以便在遇到HTML标签开始、结束或数据时执行特定操作。 2. **启动爬虫*...
API文档是使用HTMLParser的关键,它详尽地解释了库中每个类、方法和接口的用途。通过API文档,开发者可以了解如何初始化解析器,如何遍历HTML元素,以及如何处理各种HTML标签。例如,HTMLParser库可能会提供如`...
`htmlparser.dll`是这个库的动态链接库文件,它包含了编译好的类和方法,可以直接在C#项目中引用以使用HTMLParser的功能。在C#项目中,我们可以通过添加对dll的引用来调用库中的方法,比如解析HTML字符串、查找特定...
1. **安装和引入HTMLParser**:首先,用户需要将htmlparser.jar添加到项目构建路径中,无论是通过IDE(如Eclipse或IntelliJ IDEA)还是通过构建工具(如Maven或Gradle)。 2. **基本概念**:理解HTMLParser的核心类...
7. **源代码(src)**:`src`目录通常包含项目的源代码,可能包括HTMLParser的类和接口,以及示例代码的Java文件。 8. **JAR文件**:`jar`文件可能是编译后的HTMLParser库,开发者可以直接引入到自己的项目中使用。...
9. 结合其他工具:HTMLParser可以与其他工具结合使用,如Jsoup,后者是一个更现代的HTML解析库,提供更友好的API和更强的CSS选择器支持。两者结合可以提高处理复杂HTML文档的效率。 10. 性能考虑:虽然HTMLParser...
Objective-C的HTMLParser类代码是iOS和Mac应用开发中用于解析HTML文档的一种工具。这个开源项目为开发者提供了方便,能够高效地处理HTML内容并从中提取有用的信息。在iOS和Mac平台,Objective-C作为主要的编程语言,...
总的来说,HTMLParser是一个强大且灵活的工具,适用于需要解析和操作HTML的Java应用,如爬虫、数据提取、网页自动化等场景。通过理解这两个jar包的功能和交互,我们可以有效地利用HTMLParser来处理复杂的HTML解析...
在很多Web抓取、信息提取以及网页分析的项目中,HTMLParser都是一个理想的工具。 HTMLParser库的设计考虑了易用性和灵活性,它提供了丰富的API,允许开发者通过编程方式来访问和操作HTML文档的各个元素,如标签、...
总之,HTMLParser 1.6作为一个强大且灵活的HTML解析库,对于需要处理HTML内容的Java开发者来说,是一个不可或缺的工具。通过熟练掌握其API和事件模型,开发者可以轻松地实现复杂的网页信息提取和解析任务。
- Java项目中,HTMLParser通常作为依赖项添加到构建工具(如Maven或Gradle)的配置文件中,通过`import`导入库的类。 5. 示例代码: - C#示例: ```csharp using HtmlAgilityPack; ...
总的来说,HTMLParser 2.0是Python中一个实用的HTML解析工具,虽然相对基础,但能帮助开发者完成许多基本的HTML解析任务。通过熟练掌握其使用,可以更高效地处理HTML文档,从而在数据提取和信息分析中发挥重要作用。
1. 将htmlparser-2.1.jar文件添加到你的项目类路径中。 2. 引入必要的import语句,如`import org.htmlparser.*;` 3. 创建解析器实例,配置解析选项,然后解析目标URL或HTML字符串。 4. 使用提供的API遍历DOM或监听...
1. 引入依赖:将HTMLParser的JAR包添加到项目的类路径中,或者在Maven或Gradle项目中配置相应的依赖。 2. 创建解析器:实例化HTMLParser对象,可以设置不同的解析策略,如严格解析或宽容解析。 3. 配置处理器:...
HTMLParser 是一个C#编写的库,用于解析HTML文档,提取和处理其中的数据。这个库可能包含了一...通过学习源码和示例,开发者可以更好地理解和应用这个工具,同时也可以根据需要对其进行定制,以适应不同的项目需求。
总的来说,HTMLParser是一个强大且灵活的工具,能够帮助Java开发者轻松地处理和解析HTML文档,无论是为了数据抓取还是其他Web相关的任务。了解和掌握这个库的使用,对于从事相关工作的IT专业人士来说是非常有价值的...
在提供的压缩包中,"lib"目录下的jar文件就是HTMLParser库,将其添加到项目的类路径中,这样就可以在代码中使用这个库了。 接下来,我们需要编写爬虫的核心部分,即解析和提取数据的逻辑。以下是一个简单的示例: ...