Java で wget をやったらどうなるのか考えたら、めんどくさそうだった。
どこまで長文になるのか試しにやってみた
java で ネットワークのファイルを取得
WEB ページを取得して title 要素抜き出す
import java.net.URL; import java.net.URLConnection; import java.net.Proxy; import java.net.InetSocketAddress; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; class Title{ public static void main(String[] argv) throws Exception { String title = Title.getTitle(argv[0]); System.out.println(title); } public static String getTitle(String page_url) throws Exception { //アクセスしたいページpage_url URL url = new URL(page_url); URLConnection conn = url.openConnection(); String charset = Arrays.asList(conn.getContentType().split(";") ).get(1); String encoding = Arrays.asList(charset.split("=") ).get(1); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), encoding )); StringBuffer response = new StringBuffer(); String line; while ((line= in.readLine()) != null) response.append(line+"\n"); in.close(); Pattern title_pattern1 = Pattern.compile("<title>([^<]+)</title>",Pattern.CASE_INSENSITIVE); Matcher matcher1 = title_pattern1.matcher(response.toString()); if(matcher1.find() ) { return matcher1.group(1); } return null; } }
使い方
javac Title.java java Title https://mixi.jp java Title http://www.yahoo.co.jp/
解説
色々と面倒なことがありそうなので、とりあえず、あとためにメモっておこう。
ネットワークをOpenする。
URL url = new URL(page_url);
URLConnection conn = url.openConnection();
InputStreamとして読み込む。
InputStream は InputStreamReaderを使って、Reader(いつものファイル)と同じように扱って、 BufferedReader で、読み込みをバッファリングしている。
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), encoding )); StringBuffer response = new StringBuffer(); String inputLine; while ((inputLine = in.readLine()) != null) response.append(inputLine+"\n"); in.close();
文字コードを変換するための情報を取得
Content-Typeの文字コードを取得して、InputStreamReaderを開くときに文字コード指定に使う
String charset = Arrays.asList(conn.getContentType().split(";") ).get(1); String encoding = Arrays.asList(charset.split("=") ).get(1);
Array.asList で、Listとして配列を扱う。
StringをSplitすると、String[] (String の配列)が戻されるので、それは扱いにくいのでCollection 対応させる・。
charset.split("=") // => String[] Arrays.asList(charset.split("=") ) //=> List<String> Arrays.asList(charset.split("=") ).get(1); //=> String
StringBuffer response
文字列結合を連続して行うときの定番
StringBuffer response = new StringBuffer(); response.append(line);
StringBufferでも、StringBuilderでも構わない。
正規表現で、マッチして、後方参照
HTMLのTitleタグ中から、正規表現でテキストノードを正規表現で取得する。
Pattern title_pattern1 = Pattern.compile("<title>([^<]+)</title>",Pattern.CASE_INSENSITIVE); Matcher matcher1 = title_pattern1.matcher(response.toString()); if(matcher1.find() ) { return matcher1.group(1); }
大文字小文字を無視した正規表現パターンを作る。
Pattern title_pattern1 = Pattern.compile("<title>([^<]+)</title>",Pattern.CASE_INSENSITIVE);
マッチさせる
Matcher matcher1 = title_pattern1.matcher(response.toString());
正規表現にマッチした箇所があったか?あれば、中身を取得
if(matcher1.find() ) { return matcher1.group(1); }
引数が取れる。
public static void main(String[] argv) throws Exception { String title = Title.getTitle(argv[0]); System.out.println(title); }
引数の argv の配列には、コマンドの引数が入るが、argv[0]が自分自身のファイル名になることはない。
感想
いーかげんJavaの顔も見たくないと思った。もっと簡単なやり方あるんですかね? 仮にあったとしても、一番最初に習う書き方で、教科書的に書いた結果がこれでは・・・
でも、いい感じに抽象化されていて、 https/http も問題なく URLで扱えるのとかいいなと思った。
URL classで抽象化してるわりに、このへんはストリームが見えてて、意図が良くわかなんない。
getInputStream()は気持ち悪い。HTTPのプロトコル的にkeep-alive を指定しない限りCloseだし。中身とったら終わりだから。ストリームは見えなくていいような。
URL.Connection やinputstreamがあるのは、Javaの基本APIの設計思想に理由があるのかな。JavaはOSのシステムコールに近いものを意識してるから仕方ないよね・・・
バッファリングとか必要なんだろうかと思いながらも、教科書通りってことでいつも書くんだけど。やっぱよくわからん。
String とか Object はJavaのなかでも一番わかりくく使いにくい。
StringBuilderとか実際は、String += String と書いてたら、javacが最適化して内部的に使ってたから僕らは気にしなくてよかったような。
自分自身のファイル名の取得はどうすんだろうな。Runtimeあたりかな。
StringBufferとStringBuilderの違い
http://www.javainthebox.net/laboratory/J2SE1.5/TinyTips/StringBuilder/StringBuilder.html