昔、低レベルな仕事をしていた時に、
時々、org.xml.sax.helpers.DefaultHandler でXMLパーサを書いて遊んでいた。
HTML を、これで SAXParser にパースさせていては、当然ダメである。
tagSoup というHTML用のパーサがある。
org.xml.sax.ContentHandler を実装して、org.ccil.cowan.tagsoup.Parser というパーサで解析する。
Android上で実行してみる。
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
* HtmlHandler
*/
public abstract class HtmlHandler implements org.xml.sax.ContentHandler{
private Stack<String> pathStack;
private Map<String,String> bmap;
private Map<String,Map<String,String>> attrmap;
/**
* タグ読込開始
* @param qName
* @param xpath
* @param attributes
*/
public abstract void onStartedTag(String qName,String xpath,Map<String,String> attributes);
/**
* タグ読込完了
* @param qName タグ名
* @param xpath xmlフルパス
* @param attributes 属性key="value"をMap変換後
* @param body tag body
*/
public abstract void onReadedTag(String qName,String xpath,Map<String,String> attributes,String body);
@Override
public void startDocument() throws SAXException{
pathStack = new Stack<String>();
bmap = new HashMap<String,String>();
attrmap = new HashMap<String,Map<String,String>>();
}
@Override
public void setDocumentLocator(Locator locator){
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException{
}
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
pathStack.push(qName);
Map<String,String> amap = new HashMap<String,String>();
for(int i=0;i < atts.getLength();i++){
amap.put(atts.getQName(i),atts.getValue(i));
}
String xpath = getXPath();
attrmap.put(xpath, amap);
onStartedTag(qName, xpath, amap);
}
@Override
public void characters(char ch, int start, int length) throws SAXException{
String path = getXPath();
if (bmap.containsKey(path)){
bmap.put(path, bmap.get(path)+(new String(ch,start,length).trim()));
}else{
bmap.put(path, (new String(ch,start,length).trim()));
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException{
String path = getXPath();
onReadedTag(qName,path,attrmap.get(path),bmap.get(path));
attrmap.remove(path);
bmap.remove(path);
pathStack.pop();
}
@Override
public void processingInstruction(String target, String data) throws SAXException{
}
@Override
public void ignorableWhitespace(char ch, int start, int length) throws SAXException{
}
@Override
public void skippedEntity(String name) throws SAXException{
}
@Override
public void endPrefixMapping(String prefix) throws SAXException{
}
@Override
public void endDocument() throws SAXException{
}
private String getXPath(){
StringBuffer sb = new StringBuffer();
for(String s : pathStack){
sb.append("/"+s);
}
return sb.toString();
}
}
使い方は、SAXParser で解析した時と同様のハンドラで、
似たような呼び出し方になる。
以下は、Android での実行のコードサンプル
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.ccil.cowan.tagsoup.Parser;
import org.xml.sax.InputSource;
==============================================
Parser parser = new Parser();
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(new HttpGet(url));
int httpstatus = response.getStatusLine().getStatusCode();
if (httpstatus==HttpStatus.SC_OK){
in = response.getEntity().getContent();
InputSource source = new InputSource(new InputStreamReader(in, "UTF-8"));
parser.setContentHandler(new HtmlHandler(){
@Override
public void onStartedTag(String qName, String xpath, Map<String,String> attributes){
}
@Override
public void onReadedTag(String qName, String xpath, Map<String,String> attributes, String body){
// タグ名→ qName
// XPath → xpath
// 属性Map → attributes
// タグBody → body
}
});
parser.parse(source);
}