MY-NOTEBOOK

Friday, June 15, 2007

ウィキペディアコンテンツの活用 XML形式のWikipediaデータからJavaを使って記事だけを取り出す方法

Wikipediaのコンテンツを活用して自前のアプリケーションを書くために、今回Javaで処理する方法を調べました。

まず下準備として、以下のWikimediaのダウンロードページからコンテンツを入手します。

XML,SQL,StaticHTMLの形式が選択できますが、今回は、XMLデータをダウンロードしてそれを活用することにします。

StaticHTMLは便利そうですが、データが古いのと、HTML中にこちらが意図しない情報も含まれていると取り除くのが面倒です。
XMLであれば、直接wiki textで書かれたコンテンツデータを入手できるので、このwiki textをこちらの都合のよいように変換して使うことができます。

Javaで辞典のタイトルを取り出す

XMLデータの準備

Wikimediaのサイトから入手した、jawiki-20081127-pages-articles.xml.bz2を用意します。

コンテンツを取り出す

XMLをパースして、該当部分を抜き出していきます。(javac 1.6.0_04 を使用しています。)

Main.java,WikidataHandler.java,WikidataHandlerImpl.java を コンパイルした上で、以下のように使います。

$ bzcat jawiki-20081127-pages-articles.xml.bz2 | java Main

結果はコンソールに標準出力されます。
取り出したデータを活用するには、Main.java内のWikidataHandler の cut() メソッドの実装を適切に書きかえます。

コード

Main.java

import java.io.InputStreamReader;
import java.io.Reader;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;


public class Main {
	
	public static void main(String[] args) {
		
		Reader reader=null;

		try{
			reader=new InputStreamReader( System.in,"UTF-8" );

			XMLReader r=XMLReaderFactory.createXMLReader();
			WikidataHandlerImpl handler=new WikidataHandlerImpl(){
				@Override
				public void cut(int count, long id, String title, String text) {
					System.out.println(count);
					System.out.println(id);
					System.out.println(title);
					//System.out.println(text);
					
				}
			};
			r.setContentHandler(handler);
			
			InputSource is=new InputSource(reader);
			r.parse(is);
		}
		catch(Exception ex){
			ex.printStackTrace();
		}
		finally{
			try{
				reader.close();
			}
			catch(Exception ex){}
		}
	}

}

標準入力から得たXMLデータをパースし、Wikipediaの記事を取り出したらcut()メソッドで受け取ります。

WikidataHandler.java

interface WikidataHandler {

	void cut(int count,long id,String title,String text);
	void stop();

}

取り出したデータを受け取るインタフェース。

WikidataHandlerImpl.java

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;


abstract class WikidataHandlerImpl extends DefaultHandler implements WikidataHandler{
	
	static final String[] SKIPWORDS=new String[]{"Wikipedia:","Template:","Category:","画像:"};
	
	static private boolean isSkip(String title){
		if(title==null){
			return false;
		}

		for(int i=0; i<SKIPWORDS.length; i++){
			String word=SKIPWORDS[i];
			if( title.startsWith(word) ){
				return true;
			}
		}
		return false;
	}

	
	
	
	private int currentCount;

	private String currentTitle;
	private StringBuffer currentText;
	private long currentPageId;

	private boolean isPage;
	private boolean isTitle;
	private boolean isText;
	private boolean isId;
	private boolean isRevision;
	private boolean skip;

	@Override
	public void startElement(String arg0, String arg1, String tagName, Attributes arg3) throws SAXException {
		
		if(pleaseStop){
			throw new SAXException("canceled");
			//return ;
		}
		
		if( tagName.equals("page") ){
			isPage=true;

			//init
			currentPageId=0;
			currentTitle="";
			currentText=new StringBuffer();

			skip=false;
		}
		if( tagName.equals("title") ){
			isTitle=true;
		}
		if( tagName.equals("id") ){
			if(isRevision==false)
				isId=true;
		}
		if( tagName.equals("text") ){
			isText=true;
		}
		if( tagName.equals("revision") ){
			isRevision=true;
		}
	}

	@Override
	public void endElement(String uri, String localName, String tagName) throws SAXException {
		
		if(pleaseStop)
			return ;

		
		if( tagName.equals("page") ){
			isPage=false;

			if(skip==false){
				cut(currentCount,currentPageId,currentTitle,currentText.toString());
				currentCount++;
			}

		}
		if( tagName.equals("text") ){
			isText=false;
		}
		if( tagName.equals("revision") ){
			isRevision=false;
		}
	}

	@Override
	public void characters(char[] ch, int start, int len) throws SAXException {
		
		if(pleaseStop)
			return ;

		
		if( isPage && isId && isRevision==false ){
			//save current pageId
			String id=new String(ch,start,len);
			currentPageId=new Long(id).longValue();

			isId=false;
		}

		if(isTitle){
			currentTitle=new String(ch,start,len);
			skip=isSkip(currentTitle);

			isTitle=false;
		}
		if(isText){
			if(skip==false){
				String str=new String(ch,start,len);
				currentText.append(str);
			}
		}
	}
	
	
	private boolean pleaseStop;
	@Override
	public void stop(){
		pleaseStop=true;
	}
	

}

パースしたXMLデータの実際の処理をします。

© 2006-2012 Tomoaki Oshima