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形式で取得できます。