まず事前に免責事項:投稿されたコードスニペットはすべて基本的な例です。のような簡単なIOException
とを処理する必要があり、自分でコンソートします。RuntimeException
NullPointerException
ArrayIndexOutOfBoundsException
準備
まず、少なくともURLと文字セットを知る必要があります。パラメータはオプションであり、機能要件によって異なります。
String url = "http://example.com";
String charset = "UTF-8"; // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name()
String param1 = "value1";
String param2 = "value2";
// ...
String query = String.format("param1=%s¶m2=%s",
URLEncoder.encode(param1, charset),
URLEncoder.encode(param2, charset));
クエリパラメータはのname=value
形式で、によって連結されて&
いる必要があります。通常、を使用して、指定された文字セットでクエリパラメータをURLエンコードしますURLEncoder#encode()
。
これString#format()
は便宜上のものです。文字列連結演算子+
が2回以上必要な場合は、この方法を使用します。
(オプションで)クエリパラメータを使用したHTTP GETリクエストの実行
それは簡単な仕事です。これはデフォルトのリクエストメソッドです。
URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...
クエリ文字列は、を使用してURLに連結する必要があります?
。Accept-Charset
あなたが任意のクエリ文字列を送信しない場合は、ヘッダーは、。であるパラメータをコードするものをサーバーにヒントがあり、あなたは残すことができますAccept-Charset
離れてヘッダを。ヘッダーを設定する必要がない場合は、URL#openStream()
ショートカットメソッドを使用することもできます。
InputStream response = new URL(url).openStream();
// ...
どちらの方法でも、反対側がのHttpServlet
場合、そのdoGet()
メソッドが呼び出され、パラメータはによって利用可能になりますHttpServletRequest#getParameter()
。
テストのために、以下のように応答本文をstdoutに出力できます。
try (Scanner scanner = new Scanner(response)) {
String responseBody = scanner.useDelimiter("\\A").next();
System.out.println(responseBody);
}
クエリパラメータを使用したHTTP POSTリクエストの実行
をに設定するURLConnection#setDoOutput()
とtrue
、要求メソッドが暗黙的にPOSTに設定されます。Webフォームと同様に、標準のHTTP POSTはapplication/x-www-form-urlencoded
、クエリ文字列がリクエストの本文に書き込まれるタイプです。
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
try (OutputStream output = connection.getOutputStream()) {
output.write(query.getBytes(charset));
}
InputStream response = connection.getInputStream();
// ...
注:HTMLフォームをプログラムで送信する場合name=value
は、<input type="hidden">
要素のペアをクエリ文字列に含めることを忘れないでください。もちろん、プログラムで "押し付けたい"要素のname=value
ペアも<input type="submit">
含めてください(これは通常、サーバー側でボタンが押されたかどうか、押された場合はどれを区別するために使用されます。
取得URLConnection
したHttpURLConnection
をキャストしてHttpURLConnection#setRequestMethod()
代わりに使用することもできます。ただし、接続を出力に使用する場合は、に設定URLConnection#setDoOutput()
する必要がありますtrue
。
HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...
どちらの方法でも、反対側がのHttpServlet
場合、そのdoPost()
メソッドが呼び出され、パラメータはによって利用可能になりますHttpServletRequest#getParameter()
。
実際にHTTPリクエストを発生させる
を使用してHTTPリクエストを明示的URLConnection#connect()
に起動できますが、使用している応答本文などのHTTP応答に関する情報を取得したい場合、要求はオンデマンドで自動的に起動さURLConnection#getInputStream()
れます。上記の例はまさにそれを行うので、connect()
呼び出しは実際には不必要です。
HTTP応答情報の収集
HTTP応答ステータス:
HttpURLConnection
ここが必要です。必要に応じて最初にキャストします。
int status = httpConnection.getResponseCode();
HTTP応答ヘッダー:
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + "=" + header.getValue());
}
HTTP応答エンコーディング:
にパラメータがContent-Type
含まれている場合、charset
応答の本文はテキストベースである可能性が高いため、サーバー側で指定された文字エンコードを使用して応答の本文を処理します。
String contentType = connection.getHeaderField("Content-Type");
String charset = null;
for (String param : contentType.replace(" ", "").split(";")) {
if (param.startsWith("charset=")) {
charset = param.split("=", 2)[1];
break;
}
}
if (charset != null) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) {
for (String line; (line = reader.readLine()) != null;) {
// ... System.out.println(line) ?
}
}
} else {
// It's likely binary content, use InputStream/OutputStream.
}
セッションの維持
サーバー側のセッションは通常、Cookieによってサポートされます。一部のWebフォームでは、ログインしているか、セッションによって追跡されている必要があります。CookieHandler
APIを使用してCookieを維持できます。あなたは準備する必要があるCookieManager
とCookiePolicy
のをACCEPT_ALL
、すべてのHTTPリクエストを送信する前に。
// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
これは、常にすべての状況で適切に機能するとは限らないことに注意してください。失敗した場合は、Cookieヘッダーを手動で収集して設定することをお勧めします。基本的にSet-Cookie
は、ログインまたは最初のGET
要求の応答からすべてのヘッダーを取得し、それを後続の要求に渡す必要があります。
// Gather all cookies on the first request.
URLConnection connection = new URL(url).openConnection();
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
// ...
// Then use the same cookies on all subsequent requests.
connection = new URL(url).openConnection();
for (String cookie : cookies) {
connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
// ...
split(";", 2)[0]
以下のようなサーバ側のために無関係なクッキー属性の存在を取り除くためにされたexpires
、path
など。あるいは、あなたも使用できるcookie.substring(0, cookie.indexOf(';'))
代わりにsplit()
。
ストリーミングモード
HttpURLConnection
デフォルトでは、意志がバッファ全体実際に関係なく、あなた自身が使用して固定したコンテンツの長さを設定しているかどうかに、それを送信する前にリクエストボディをconnection.setRequestProperty("Content-Length", contentLength);
。これによりOutOfMemoryException
、サイズの大きいPOSTリクエスト(ファイルのアップロードなど)を同時に送信するたびに、s が発生する可能性があります。これを回避するには、を設定しHttpURLConnection#setFixedLengthStreamingMode()
ます。
httpConnection.setFixedLengthStreamingMode(contentLength);
しかし、コンテンツの長さが本当に事前にわからない場合は、HttpURLConnection#setChunkedStreamingMode()
それに応じて設定することで、チャンクストリーミングモードを利用できます。これにより、リクエストボディがチャンクで送信されるHTTP Transfer-Encoding
ヘッダーが設定さchunked
れます。以下の例では、1KBのチャンクで本文を送信します。
httpConnection.setChunkedStreamingMode(1024);
ユーザーエージェント
リクエストが予期しない応答を返すことがありますが、実際のウェブブラウザでは正常に機能します。サーバー側は、おそらくUser-Agent
リクエストヘッダーに基づいてリクエストをブロックしています。URLConnection
デフォルトでは意志がそれを設定しJava/1.6.0_19
、最後の部分が明らかにJREのバージョンです。これは次のようにオーバーライドできます。
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); // Do as if you're using Chrome 41 on Windows 7.
最近のブラウザのUser-Agent文字列を使用します。
エラー処理
HTTP応答コードが4nn
(クライアントエラー)または5nn
(サーバーエラー)のHttpURLConnection#getErrorStream()
場合は、を読んで、サーバーが有用なエラー情報を送信したかどうかを確認してください。
InputStream error = ((HttpURLConnection) connection).getErrorStream();
HTTP応答コードが-1の場合、接続と応答の処理で問題が発生しています。HttpURLConnection
実装はアライブ接続を保ちながらややバギー古いのJREです。http.keepAlive
システムプロパティをに設定して、これをオフにすることができますfalse
。これは、アプリケーションの最初で次のようにプログラムで実行できます。
System.setProperty("http.keepAlive", "false");
ファイルをアップロードする
通常multipart/form-data
、混合POSTコンテンツ(バイナリデータと文字データ)にはエンコーディングを使用します。エンコーディングの詳細については、RFC2388で説明されています。
String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
try (
OutputStream output = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
// Send normal param.
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append(CRLF).append(param).append(CRLF).flush();
// Send text file.
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
writer.append(CRLF).flush();
Files.copy(textFile.toPath(), output);
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
// Send binary file.
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF).flush();
Files.copy(binaryFile.toPath(), output);
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
// End of multipart/form-data.
writer.append("--" + boundary + "--").append(CRLF).flush();
}
反対側がであるHttpServlet
場合、そのdoPost()
メソッドが呼び出され、パーツが利用できるようになりますHttpServletRequest#getPart()
(したがって、そうではない getParameter()
など)。getPart()
ただし、この方法は比較的新しいもので、サーブレット3.0(Glassfish 3、Tomcat 7など)で導入されています。サーブレット3.0以前は、Apache Commons FileUploadを使用してmultipart/form-data
リクエストを解析するのが最適です。FileUploadとServelt 3.0の両方のアプローチの例については、この回答も参照してください。
信頼できない、または誤って構成されたHTTPSサイトへの対処
Webスクレイパーを作成しているために、HTTPS URLに接続する必要がある場合があります。その場合、あなたはおそらく直面する可能性がありjavax.net.ssl.SSLException: Not trusted server certificate
、いくつかのHTTPS日に自分のSSL証明書を保持していないサイト、または上java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found
またはjavax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
いくつかの設定に誤りHTTPSサイトで。
static
Webスクレイパークラス内の次の1回限りの初期化子は、HttpsURLConnection
これらのHTTPSサイトに関してより寛大になり、したがってこれらの例外をスローしなくなります。
static {
TrustManager[] trustAllCertificates = new TrustManager[] {
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null; // Not relevant.
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
}
};
HostnameVerifier trustAllHostnames = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true; // Just allow them all.
}
};
try {
System.setProperty("jsse.enableSNIExtension", "false");
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCertificates, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
}
catch (GeneralSecurityException e) {
throw new ExceptionInInitializerError(e);
}
}
最後の言葉
ApacheのHttpClientをHttpComponentsがあるくらい、このすべてに、より便利:)
HTMLの解析と抽出
HTMLからデータを解析して抽出するだけの場合は、JsoupなどのHTMLパーサーを使用することをお勧めします。