HttpUnit

 最近、色々な情報がWebで公開されています。
 時々、CGIに直接問い合わせてデータを収集した場合がありますが、HTMLドキュメントから、必要なデータ(テーブルデータ等)を抽出する必要があります。
 HttpUnitは、元々はWebアプリのテストに使用するブラウザのエミュレータみたいなものみたいですが、これを使うと簡単にHTMLを解釈し、必要データが抽出できます。

 ■ソースコード(サンプル)
 サンプルとして、気象庁アメダスデータを取得する簡単なプログラムを書いてみました。
 なお、カレンダーコンポネントは以前作ったものを使用します。

package test;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;

import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebResponse;
import com.meterware.httpunit.WebTable;

public class HttpUtiniTest {
	private Date st;
	private JTextField text;
	private List<String[][]> list;
	private JFileChooser ch;
	private File file;
	private JFrame frame;
	private CalendarPanel cp;
	
	public HttpUtiniTest(){
		frame=new JFrame();
		frame.setTitle("HttpUtiniTest");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().setLayout(new BorderLayout());
		text=new JTextField("");
		text.setBorder(BorderFactory.createLoweredBevelBorder());
		JPanel top=new JPanel(new BorderLayout());
		top.setBorder(BorderFactory.createTitledBorder(
				BorderFactory.createLineBorder(Color.BLUE),"BaseURL"));
		top.add(text,BorderLayout.CENTER);
		frame.getContentPane().add(top,BorderLayout.NORTH);
		cp=new CalendarPanel();
		JPanel panel=new JPanel(new BorderLayout());
		panel.add(cp,BorderLayout.CENTER);
		panel.setBorder(BorderFactory.createTitledBorder(
				BorderFactory.createLineBorder(Color.BLUE),"Year/Month/Day"));
		frame.getContentPane().add(panel,BorderLayout.CENTER);
		ch=new JFileChooser();
		JButton ok=new JButton("DownLoad");
		ok.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				getData();
			}
		});
		JButton canc=new JButton("End");
		canc.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				System.exit(0);
			}
		});
		JPanel bottom=new JPanel(new GridLayout(1,2));
		bottom.add(ok);
		bottom.add(canc);
		frame.getContentPane().add(bottom,BorderLayout.SOUTH);
		frame.setSize(300, 300);
		frame.setResizable(false);
		frame.setVisible(true);
	}
	
	private void getData(){
		st=cp.getDate();
		FileFilter ff=new FileFilter(){
			@Override
			public boolean accept(File arg0) {
				return arg0.getAbsolutePath().toLowerCase().endsWith(".csv");
			}
			@Override
			public String getDescription() {
				return "*.csv";
			}
		};
		ch.setFileFilter(ff);
		int type=ch.showSaveDialog(frame);
		if(type==JFileChooser.CANCEL_OPTION)return;
		file=ch.getSelectedFile();
		if(file==null)System.exit(0);
		list=new ArrayList<String[][]>();
		Calendar cal=Calendar.getInstance();
		int ct=0;
		Date dd=st;
		cal.setTime(dd);
		int year=cal.get(Calendar.YEAR);
		int month=cal.get(Calendar.MONTH);
		int day=cal.get(Calendar.DATE);
		String years=Integer.toString(year);
		String months=Integer.toString(month+1);
		if(month+1<10)months="0"+months;
		String days=Integer.toString(day);
		new Parser(ct++,years,months,days);
		try{
			BufferedWriter bw=new BufferedWriter(new FileWriter(file));
			for(String[][] s : list){
				write(bw,s);
			}
			bw.close();
			JOptionPane.showMessageDialog(frame, "completed.",
 			"情報", JOptionPane.INFORMATION_MESSAGE);
		}catch(IOException e){
			JOptionPane.showMessageDialog(frame, "exception.",
			 "情報", JOptionPane.WARNING_MESSAGE);
		}
	}
	
	private void write(BufferedWriter bw,String[][] ss) throws IOException{
		if(ss==null)return;
		for(int i=0;i<ss.length;i++){
			bw.write(ss[i][0]);
			for(int j=1;j<ss[i].length;j++){
				bw.write(","+ss[i][j]);
			}
			bw.write("\n");
		}
	}
	
	private class Parser{
		int id;
		Parser(int ii,String y,String m,String d){
			id=ii;
			String yms="year="+y+"&month="+m+"&day="+d+"&view=p1";
			String url=text.getText()+yms;
			try{
				WebConversation wc = new WebConversation();
				WebResponse wr = wc.getResponse(url);
				WebTable[] tt=wr.getTables();
				WebTable table = wr.getTables()[tt.length-1];
				String[][] num=table.asText();
				String[][] data=new String[24][9];
				int ids=0;
				for(int i=2;i<num.length;i++){
					data[ids][0]=y;
					data[ids][1]=m;
					data[ids][2]=d;
					int ix=3;
					for(int j=0;j<num[i].length-2;j++){
						if(num[i][j].isEmpty()){
							data[ids][ix++]="999";
						}else{
							data[ids][ix++]=num[i][j];
						}
					}
					ids++;
				}
				list.add(data);
				System.out.println(y+"/"+m+"/"+d);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}

	public static void  main(String[] args){
		new HttpUtiniTest();
	}
}

■実行結果
 例えば、BaseURLに「http://www.data.jma.go.jp/obd/stats/etrn/view/hourly_a1.php?prec_no=86&prec_ch=%8CF%96%7B%8C%A7&block_no=0843&block_ch=%8F%BC%93%87&」を入力し、年月を選択してDownloadボタンを押すと、熊本県・松島のアメダス観測結果がCSV形式で取得できます。

・実行画面




・取得したアメダスデータ(CSV形式)