StAXでHTMLのTITLEを取得する

Google App EngineでいつのまにかJAXBとかStAXが使えるようになってたので、StAX使ってHTMLのTITLEを取得してみました。


ただ、まず問題は、はてなのようにdoctypeにシステムIDが入ってない場合はエラーが出て処理ができなくなってしまうこと。
それと、パーサーに渡す前にエンコーディングを決めておかないといけないので、HTTPヘッダーにエンコーディングが入ってないと、文字化けします。静的なHTMLの場合は設定にエンコーディングが入ってないことが多いので、結構致命的。


実行結果はこんな感じ

Yahoo!オークション
ソーシャル・ネットワーキング サービス [mixi(ミクシィ)]
??????
http://www.cao.go.jp/ skip
http://d.hatena.ne.jp/nowokay/ skip


なので、まともにやろうと思うと、自分でパーサー作ったほうがよさげです。


ということで、ソース

public class HtmlTitle{
    public static void main(String[] args) throws IOException, XMLStreamException{
        System.out.println(getHtmlTitle("http://auctions.yahoo.co.jp/jp/"));
        System.out.println(getHtmlTitle("http://mixi.jp"));
        System.out.println(getHtmlTitle("http://www.jal.co.jp/"));
        System.out.println(getHtmlTitle("http://www.soumu.go.jp/"));//総務省
        System.out.println(getHtmlTitle("http://www.cao.go.jp/"));//内閣府
        System.out.println(getHtmlTitle("http://d.hatena.ne.jp/nowokay/"));
    }

    public static String getHtmlTitle(String url) throws IOException,XMLStreamException{
        XMLInputFactory factory = XMLInputFactory.newFactory();
        XMLStreamReader reader = null;
        Reader r = null;
        HttpURLConnection conn = null;
        try{
            URL u = new URL(url);//http://www.google.com/");
            conn = (HttpURLConnection) u.openConnection();
            String contentType = conn.getHeaderField("Content-Type");
            String encoding = "utf-8";
            if(contentType != null){
                try {
                    MimeType mt = new MimeType(contentType);
                    if (!mt.match("text/html")){
                        throw new IOException("htmlじゃないよ");
                    }
                    String s = mt.getParameter("charset");
                    if(s != null){
                        encoding = s;
                    }
                } catch (MimeTypeParseException ex) {
                }
            }
            StringBuilder buf = new StringBuilder();
            r = new InputStreamReader(u.openStream(), encoding);
            reader = factory.createXMLStreamReader( r);

            Location lastError = null;
            while(reader.hasNext()){
                try{
                    if(reader.nextTag() != XMLStreamReader.START_ELEMENT) continue;
                    String name = reader.getName().getLocalPart();
                    if("title".equalsIgnoreCase(name)){
                        if(reader.hasNext() && reader.next()==XMLStreamReader.CHARACTERS){
                            return reader.getText();
                        }
                    }
                }catch(XMLStreamException e){
                    //エラーは無視してくりかえす
                    if(lastError == null){
                        lastError = e.getLocation();
                    }else{
                        Location newLoc = e.getLocation();
                        if(lastError.equals(newLoc) ||
                                (newLoc.getLineNumber()==lastError.getLineNumber() && newLoc.getColumnNumber() == lastError.getColumnNumber())){
                            System.out.print(url + " skip");
                            break;
                        }
                        lastError = newLoc;
                    }
                }
            }
            return "";
        }finally{
            //closeでは例外でない前提
            if(reader != null) reader.close();
            if(r != null)r.close();
            if(conn != null) conn.disconnect();
        }
    }
}