6 加载页面的W3C DOM访问
6.1 mozdom4java库
访问W3C DOM树比访问Mozilla的DOM树要好,因为它是一个动态访问HTML和XML的DOM树的标准。为了实现这个,我们使用从Mozilla
DOM到W3C DOM的java Bridge。有一个叫做mozdom4java的项目http://mozdom4java.mozdev.org/index.html。
下载这个包后,我们把jar包放到classpath里。例如,我们增加一个按钮来抽取HTML文档里的所有链接。
// When that button is pressed, then we obtain the HTML document corresponding to
// the URL loaded in browser. Next, we extract all its child nodes with 'a' tag name
// and print its content.
final ToolItem anchorItem = new ToolItem(toolbar, SWT.PUSH);
anchorItem.setImage(getImage("resources/anchors.png"));
anchorItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
// First, we obtain a Mozilla DOM Document representation
nsIDOMDocument doc = browser.getDocument();
// Get all anchors from the loaded HTML document
nsIDOMNodeList nodeList = doc.getElementsByTagName("a");
for ( int i = 0; i < nodeList.getLength(); i++ ){
// Get Mozilla DOM node
nsIDOMNode mozNode = nodeList.item(i);
// Get the appropiate interface
nsIDOMHTMLAnchorElement mozAnchor =
(nsIDOMHTMLAnchorElement) mozNode.queryInterface(
nsIDOMHTMLAnchorElement.NS_IDOMHTMLANCHORELEMENT_IID);
// Get the corresponding W3C DOM node
HTMLAnchorElement a = (HTMLAnchorElement)
HTMLAnchorElementImpl.getDOMInstance(mozAnchor);
// Test the HTML element
System.out.println("Tag Name: " + a.getNodeName() + " -- Text: " + a.getTextContent()
+ " -- Href: " + a.getHref());
}
}
});
...
6.2 给mozdom4j打补丁来实现mozilla DOM Tree到 W3C DOM Tree的转换
如果我们总想使用W3C DOM Tree,节点的转换可能有点麻烦。我们建议修改mozdom4java。在我们看来,这些修改简化了代码,因为
我们可以忘掉Mozilla DOM节点。最后,当我们讨论XPath时evaluator将返回一个节点的list,操作W3C element比Mozilla的node方
便,换句话说,我们的目标是构建一个可用的web browser,用标准的方法使用它而不用知道Mozilla实现的任何知识。
首先,我们需要下载Java Language Binding for DOM Level 2规范。比较好的做法是下载mozdom4java项目的jar包,
http://www.mozdev.org/source/browse/mozdom4java/src/jars/,因为他们包含了所有需要的文件,包括手工的扩展,因此我们不
需要关心任何东西。此外,我们也需要Mozilla接口。所有需要的文件:
w3chtml.jar 包含了W3C DOM HTML level 2的接口,分成两个包 org.w3c.dom.html 和 org.w3c.dom.html2
w3cextension.jar 包含 KeyEvent 类于org.w3c.dom.events包中。
MozillaInterfaces.jar
MozillaGlue.jar
当你把这些jar包扔到classpaht后,mozdom4java应该可以很好的编译(没有错误,可能有一下警告)。下面我们将修改
mozdom4java的源代码。我们将逐个文件的解释这些修改。当然,你可以直接下载修改好的jar包。
要手工patch这些库,请follow下面的步骤:
我们将要创建一个HMTL element的factory,这个类能转换Mozilla DOM element节点为相应的W3C DOM element节点。下面的类就
做了这件事情并且包含了许多注释。它使用了java反射来做前面的事情,这种方式可以让你不需要知道任何Mozilla DOM节点。
注:代码虽然很长,其实非常简单,就是用反射来调用前面的getDOMInstance方法
package es.ladyr.dom;
利用我们的HTMLElementFactory类,我们将要修改NodeFactory类。修改后你可以调用org.w3c.dom.Node getNodeInstance
(nsIDOMNode node),当输入是类型是nsIDOMNode.ELEMENT_NODE时,返回的是与之对应的W3C DOM element。
修改的代码如下:
下面是NodeFactory 类的完整代码:
最后,我们需要修改ElementImpl类。这个类有两个方法, public String getAttribute(String name) 和 public String
getTagName() ,这个两个方法最后会调用toLowerCase来把结果变成小写。这可能会带来问题,比如,一个anchor的属性可能是
onclick,这个属性的值可能包含JavaScript代码。如果我们需要执行这段JavaScript代码,那么可能会有问题。所以我们需要修改一
下ElementImpl.java文件:
...
public String getAttribute(final String name)
{
//METHOD-BODY-START - autogenerated code
Callable<String> c = new Callable<String>() { public String call() {
String result = getInstanceAsnsIDOMElement().getAttribute(name);
return result;
}};
return ThreadProxy.getSingleton().syncExec(c);
//METHOD-BODY-END - autogenerated code
}
...
public String getTagName()
{
//METHOD-BODY-START - autogenerated code
Callable<String> c = new Callable<String>() { public String call() {
String result = getInstanceAsnsIDOMElement().getTagName();
return result;
}};
return ThreadProxy.getSingleton().syncExec(c);
//METHOD-BODY-END - autogenerated code
}
...
6.3 安装我们的补丁来转换Mozilla DOM Tree成 W3CDOM Tree
解压源代码并且下载补丁http://ladyr.es/wiki/attachment/wiki/XPCOMGuide/mozdom4java_patch.diff。然后cd到包含src的目录
下,执行如下命令:
对应Linux用户
patch -p0 < moz4java_patch.diff
对应Windows用户
暂无
然后你就可以变异mozdom4java库了,当然需要如下的jar包:
w3chtml.jar
w3cextension.jar
MozillaInterfaces.jar
MozillaGlue.jar
6.4 测试补丁后的库
...
import org.mozilla.dom.NodeFactory;
import org.mozilla.interfaces.*;
import org.w3c.dom.html.HTMLAnchorElement;
import org.w3c.dom.html.HTMLElement;
...
final ToolItem anchorItem = new ToolItem(toolbar, SWT.PUSH);
anchorItem.setImage(getImage("resources/anchors.png"));
anchorItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
// First, we obtain a Mozilla DOM Document representation
nsIWebBrowser webBrowser = (nsIWebBrowser)browser.getWebBrowser();
if (webBrowser == null) {
System.out.println("Could not get the nsIWebBrowser from the Browser
widget");
}
nsIDOMWindow window = webBrowser.getContentDOMWindow();
nsIDOMDocument doc = window.getDocument();
System.out.println(doc);
// Get all anchors from the loaded HTML document
nsIDOMNodeList nodeList = doc.getElementsByTagName("a");
analyzeAnchors(nodeList);
}
private void analyzeAnchors(nsIDOMNodeList nodeList) {
for (int i = 0; i < nodeList.getLength(); i++) {
// Get Mozilla DOM node
nsIDOMNode mozNode = nodeList.item(i);
// We are supposing that the NodeList contains only HTMLElements
// because we only call this method over HTML nodes
// (NodeFactory.getNodeInstance could returns another node
// descendants, depends on the input Mozilla DOM node)
HTMLElement htmlElement = (HTMLElement) NodeFactory.getNodeInstance(mozNode);
// We only are interested in anchors
if (htmlElement instanceof HTMLAnchorElement) {
HTMLAnchorElement a = (HTMLAnchorElement) htmlElement;
// Test the HTML element
System.out.println("Tag Name: " + a.getNodeName()
+ " -- Text: " + a.getTextContent()
+ " -- Href: " + a.getHref());
}
}
}
});