Lea la url para encadenar en pocas líneas de código java

151

Estoy tratando de encontrar el equivalente de Java a Groovy:

String content = "http://www.google.com".toURL().getText();

Quiero leer el contenido de una URL en una cadena. No quiero contaminar mi código con secuencias y bucles almacenados en búfer para una tarea tan simple. Miré en HttpClient de apache pero tampoco veo una implementación de una o dos líneas.

Pomponio
fuente
66
¿Por qué no simplemente crear una clase de utilidad que encapsule todos los flujos y bucles almacenados "contaminados"? También podría usar esa clase para manejar cosas como el cierre del socket antes de que se complete la secuencia y para manejar bloques de E / S a través de una conexión lenta. Después de todo, esto es OO: encapsula la funcionalidad y la oculta de tu clase principal.
Jonathan B
1
No se puede hacer en una o dos líneas.
Thorbjørn Ravn Andersen

Respuestas:

130

Ahora que ha pasado un tiempo desde que se aceptó la respuesta original, hay un mejor enfoque:

String out = new Scanner(new URL("http://www.google.com").openStream(), "UTF-8").useDelimiter("\\A").next();

Si desea una implementación un poco más completa, que no es una sola línea, haga lo siguiente:

public static String readStringFromURL(String requestURL) throws IOException
{
    try (Scanner scanner = new Scanner(new URL(requestURL).openStream(),
            StandardCharsets.UTF_8.toString()))
    {
        scanner.useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }
}
ccleve
fuente
14
Simplemente no olvides que debes llamar Scanner#close()más tarde.
Marcelo
2
La expresión regular \\ A coincide con el comienzo de la entrada. Esto le dice a Scanner que tokenice toda la transmisión, desde el comienzo hasta el siguiente (ilógico) comienzo.
Runa
77
Neat, pero falla si la página web no devuelve contenido (""). Necesitas String result = scanner.hasNext() ? scanner.next() : "";manejar eso.
Nates
3
@ccleve sería útil añadir las importaciones de aquí, hay varios escáneres y URLs en Java
kiedysktos
2
@ccleve, ¿puede actualizar el enlace "Esto explica el \\ A:"?
Imaskar
95

Esta respuesta se refiere a una versión anterior de Java. Es posible que desee ver la respuesta de ccleve.


Aquí está la forma tradicional de hacer esto:

import java.net.*;
import java.io.*;

public class URLConnectionReader {
    public static String getText(String url) throws Exception {
        URL website = new URL(url);
        URLConnection connection = website.openConnection();
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                    connection.getInputStream()));

        StringBuilder response = new StringBuilder();
        String inputLine;

        while ((inputLine = in.readLine()) != null) 
            response.append(inputLine);

        in.close();

        return response.toString();
    }

    public static void main(String[] args) throws Exception {
        String content = URLConnectionReader.getText(args[0]);
        System.out.println(content);
    }
}

Como @extraneon ha sugerido, ioutils le permite hacer esto de una manera muy elocuente que todavía está en el espíritu de Java:

 InputStream in = new URL( "http://jakarta.apache.org" ).openStream();

 try {
   System.out.println( IOUtils.toString( in ) );
 } finally {
   IOUtils.closeQuietly(in);
 }
Joseph Weissman
fuente
55
Podría cambiar el nombre del método principal para, por ejemplo getText, pasar la cadena de URL como parámetro y tener una frase:String content = URLConnectionReader.getText("http://www.yahoo.com/");
Goran Jovic el
77
La cadena no contendrá ningún carácter de terminación de línea (debido al uso de BufferReader.readLine () que los elimina), por lo que no será exactamente el contenido de la URL.
Benoît Guédas
@Benoit Guedas, ¿cómo mantener los saltos de línea?
user1788736
76

O simplemente use Apache Commons IOUtils.toString(URL url), o la variante que también acepta un parámetro de codificación.

Steve
fuente
12
+1 Gracias, esto funcionó perfectamente. ¡Una línea de código Y cierra la transmisión! Tenga en cuenta que IOUtils.toString(URL)está en desuso. IOUtils.toString(URL url, String encoding)se prefiere.
gMale
1
IOUtils.toString(url, (Charset) null)para alcanzar un resultado similar.
franckysnow
3
Una línea de código y decenas de megabytes de archivos de clase extraños que ahora están en su tiempo de ejecución. Incluir una biblioteca gigantesca para evitar escribir algunas (en realidad, una) línea de código no es una buena decisión.
Jeffrey Blattman
1
@JeffreyBlattman si lo usa solo una vez en su aplicación, probablemente no sea una decisión tan inteligente, pero si lo usa con más frecuencia y otras cosas del paquete commons-io, entonces podría ser una decisión inteligente nuevamente. También depende de la aplicación que esté escribiendo. Si se trata de una aplicación móvil o de escritorio, puede pensar dos veces antes de inflar la huella de memoria con bibliotecas adicionales. Si se trata de una aplicación de servidor que se ejecuta en una máquina de 64 GB de RAM, simplemente ignore estos 10 MB: la memoria es barata hoy en día y si la huella básica es el 1,5% o el 2% de su memoria total, no importa
nerd de big data
24

Ahora que ha pasado más tiempo, aquí hay una manera de hacerlo en Java 8:

URLConnection conn = url.openConnection();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
    pageText = reader.lines().collect(Collectors.joining("\n"));
}
Jeanne Boyarsky
fuente
Cuando uso este ejemplo en el servicio http://www.worldcat.org/webservices/catalog/search/opensearchweb, obtengo solo las dos primeras líneas de xml.
Ortomala Lokni
El error 400 se debe a que necesita una clave para usar este servicio web. El problema es que este servicio web envía un poco de xml, luego toma varios segundos para realizar un procesamiento y luego envía la segunda parte del xml. InputStream se cierra durante el intervalo y no se consume todo el contenido. He resuelto el problema usando el componente http apache library hc.apache.org/httpcomponents-client-ga
Ortomala Lokni
17

Hay una forma aún mejor a partir de Java 9:

URL u = new URL("http://www.example.com/");
try (InputStream in = u.openStream()) {
    return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}

Al igual que el maravilloso ejemplo original, esto supone que el contenido está codificado en UTF-8. (Si necesita algo más inteligente que eso, debe crear una conexión URLC y usarla para descubrir la codificación).

Sean Reilly
fuente
1
Gracias, esto era exactamente lo que estaba buscando. También se puede usar getClass().getResourceAsStream(...)para abrir archivos de texto dentro del jar.
rjh
8

Ejemplo adicional usando guayaba:

URL xmlData = ...
String data = Resources.toString(xmlData, Charsets.UTF_8);
takacsot
fuente
1
Guava docs dice link : Tenga en cuenta que aunque estos métodos usan parámetros {@link URL}, generalmente no son apropiados para HTTP u otros recursos que no son de classpath
gaal
3

Lo siguiente funciona con Java 7/8, URL seguras, y muestra cómo agregar una cookie a su solicitud también. Tenga en cuenta que esta es principalmente una copia directa de esta otra gran respuesta en esta página , pero agregó el ejemplo de la cookie y una aclaración en el sentido de que también funciona con URL seguras ;-)

Si necesita conectarse a un servidor con un certificado no válido o un certificado autofirmado, esto arrojará errores de seguridad a menos que importe el certificado. Si necesita esta funcionalidad, puede considerar el enfoque detallado en esta respuesta a esta pregunta relacionada en StackOverflow.

Ejemplo

String result = getUrlAsString("https://www.google.com");
System.out.println(result);

salidas

<!doctype html><html itemscope="" .... etc

Código

import java.net.URL;
import java.net.URLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public static String getUrlAsString(String url)
{
    try
    {
        URL urlObj = new URL(url);
        URLConnection con = urlObj.openConnection();

        con.setDoOutput(true); // we want the response 
        con.setRequestProperty("Cookie", "myCookie=test123");
        con.connect();

        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));

        StringBuilder response = new StringBuilder();
        String inputLine;

        String newLine = System.getProperty("line.separator");
        while ((inputLine = in.readLine()) != null)
        {
            response.append(inputLine + newLine);
        }

        in.close();

        return response.toString();
    }
    catch (Exception e)
    {
        throw new RuntimeException(e);
    }
}
Brad Parks
fuente
3

Aquí está la encantadora respuesta de Jeanne, pero envuelta en una función ordenada para muppets como yo:

private static String getUrl(String aUrl) throws MalformedURLException, IOException
{
    String urlData = "";
    URL urlObj = new URL(aUrl);
    URLConnection conn = urlObj.openConnection();
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) 
    {
        urlData = reader.lines().collect(Collectors.joining("\n"));
    }
    return urlData;
}
Dave
fuente
0

URL a cadena en Java puro

Llamada de ejemplo

 String str = getStringFromUrl("YourUrl");

Implementación

Puede usar el método descrito en esta respuesta, en Cómo leer URL en un InputStream y combinarlo con esta respuesta en Cómo leer InputStream en String .

El resultado será algo así como

public String getStringFromUrl(URL url) throws IOException {
        return inputStreamToString(urlToInputStream(url,null));
}

public String inputStreamToString(InputStream inputStream) throws IOException {
    try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }

        return result.toString(UTF_8);
    }
}

private InputStream urlToInputStream(URL url, Map<String, String> args) {
    HttpURLConnection con = null;
    InputStream inputStream = null;
    try {
        con = (HttpURLConnection) url.openConnection();
        con.setConnectTimeout(15000);
        con.setReadTimeout(15000);
        if (args != null) {
            for (Entry<String, String> e : args.entrySet()) {
                con.setRequestProperty(e.getKey(), e.getValue());
            }
        }
        con.connect();
        int responseCode = con.getResponseCode();
        /* By default the connection will follow redirects. The following
         * block is only entered if the implementation of HttpURLConnection
         * does not perform the redirect. The exact behavior depends to 
         * the actual implementation (e.g. sun.net).
         * !!! Attention: This block allows the connection to 
         * switch protocols (e.g. HTTP to HTTPS), which is <b>not</b> 
         * default behavior. See: /programming/1884230 
         * for more info!!!
         */
        if (responseCode < 400 && responseCode > 299) {
            String redirectUrl = con.getHeaderField("Location");
            try {
                URL newUrl = new URL(redirectUrl);
                return urlToInputStream(newUrl, args);
            } catch (MalformedURLException e) {
                URL newUrl = new URL(url.getProtocol() + "://" + url.getHost() + redirectUrl);
                return urlToInputStream(newUrl, args);
            }
        }
        /*!!!!!*/

        inputStream = con.getInputStream();
        return inputStream;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Pros

  • Es puro java

  • Se puede mejorar fácilmente agregando diferentes encabezados (en lugar de pasar un objeto nulo, como lo hace el ejemplo anterior), autenticación, etc.

  • Se admite el manejo de conmutadores de protocolo

jschnasse
fuente