Odczytaj adres URL do ciągu w kilku wierszach kodu java

151

Próbuję znaleźć odpowiednik Java do Groovy'ego:

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

Chcę czytać treść z adresu URL na ciąg. Nie chcę zanieczyszczać mojego kodu buforowanymi strumieniami i pętlami dla tak prostego zadania. Zajrzałem do HttpClient Apache, ale nie widzę też implementacji jednego lub dwóch wierszy.

Pomponiusz
źródło
6
Dlaczego po prostu nie stworzyć klasy użytkowej, która zawiera wszystkie „zanieczyszczone” buforowane strumienie i pętle? Możesz również użyć tej klasy do obsługi takich rzeczy, jak zamykanie gniazda przed zakończeniem strumienia oraz do obsługi bloków we / wy przez wolne połączenie. W końcu to jest OO - hermetyzuj funkcjonalność i ukryj ją przed główną klasą.
Jonathan B
1
Nie można tego zrobić w jednym lub dwóch wierszach.
Thorbjørn Ravn Andersen

Odpowiedzi:

130

Teraz, gdy minęło trochę czasu od zaakceptowania oryginalnej odpowiedzi, istnieje lepsze podejście:

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

Jeśli chcesz nieco pełniejszą implementację, która nie jest pojedynczą linią, zrób to:

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
źródło
14
Tylko nie zapomnij, że musisz zadzwonić Scanner#close()później.
Marcelo,
2
Wyrażenie regularne \\ A dopasowuje początek wejścia. To każe Scannerowi tokenizować cały strumień, od początku do (nielogicznego) następnego początku.
Rune
7
Zgrabny, ale kończy się niepowodzeniem, jeśli strona internetowa nie zwraca treści („”). Musisz String result = scanner.hasNext() ? scanner.next() : "";sobie z tym poradzić.
NateS
3
@ccleve przydałoby się tutaj dodanie importu, jest wiele skanerów i adresów URL w Javie
kiedysktos
2
@ccleve, czy możesz zaktualizować łącze „To wyjaśnia \\ A:”?
Imaskar
95

Ta odpowiedź odnosi się do starszej wersji Java. Możesz spojrzeć na odpowiedź ccleve.


Oto tradycyjny sposób na zrobienie tego:

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);
    }
}

Jak zasugerował @extraneon , ioutils pozwala ci to zrobić w bardzo elokwentny sposób, wciąż w duchu Java:

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

 try {
   System.out.println( IOUtils.toString( in ) );
 } finally {
   IOUtils.closeQuietly(in);
 }
Joseph Weissman
źródło
5
Możesz zmienić nazwę głównej metody, aby, powiedzmy getText, przekazać ciąg adresu URL jako parametr i mieć jedną linijkę:String content = URLConnectionReader.getText("http://www.yahoo.com/");
Goran Jovic
7
Ciąg nie będzie zawierał żadnego znaku końca wiersza (ze względu na użycie metody BufferReader.readLine (), która je usuwa), więc nie będzie to dokładnie treść adresu URL.
Benoît Guédas
@Benoit Guedas, więc jak zachować podział na linie?
user1788736
76

Lub po prostu użyj Apache Commons IOUtils.toString(URL url)lub wariantu, który również akceptuje parametr kodowania.

steve
źródło
12
+1 Dzięki, to zadziałało idealnie. Jedna linia kodu ORAZ zamyka strumień! Zauważ, że IOUtils.toString(URL)jest to przestarzałe. IOUtils.toString(URL url, String encoding)jest preferowany.
gMale
1
IOUtils.toString(url, (Charset) null)osiągnąć podobny wynik.
franckysnow
3
Jedna linia kodu i dziesiątki megabajtów zbędnych plików klas, które są teraz w twoim środowisku wykonawczym. Włączenie gigantycznej biblioteki, aby uniknąć napisania kilku (właściwie jednej) linii kodu, nie jest dobrą decyzją.
Jeffrey Blattman
1
@JeffreyBlattman, jeśli używasz go tylko raz w swojej aplikacji, prawdopodobnie nie jest to taka mądra decyzja, ale jeśli używasz go częściej i innych rzeczy z pakietu commons-io, może to być znowu mądra decyzja. Zależy to również od pisanej aplikacji. Jeśli jest to aplikacja mobilna lub stacjonarna, możesz pomyśleć dwa razy o nadużywaniu pamięci przez dodatkowe biblioteki. Jeśli jest to aplikacja serwerowa działająca na maszynie 64 GB RAM, po prostu zignoruj ​​te 10 MB - pamięć jest obecnie tania i to, czy podstawowa powierzchnia zajmuje 1,5%, czy 2% całkowitej pamięci, nie ma znaczenia
big data nerd
24

Teraz, gdy minęło więcej czasu, oto sposób na zrobienie tego w Javie 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
źródło
Korzystając z tego przykładu w http://www.worldcat.org/webservices/catalog/search/opensearchusłudze sieciowej, otrzymuję tylko pierwsze dwa wiersze XML.
Ortomala Lokni
Błąd 400 wynika z tego, że do korzystania z tej usługi sieciowej potrzebny jest klucz. Problem polega na tym, że ta usługa sieciowa wysyła trochę pliku XML, a następnie zajmuje kilka sekund, aby wykonać pewne przetwarzanie, a następnie wysłać drugą część XML. InputStream jest zamykany w tym okresie i nie cała zawartość jest zużywana. Rozwiązałem problem za pomocą biblioteki apache komponentu http hc.apache.org/httpcomponents-client-ga
Ortomala Lokni
17

Jest jeszcze lepszy sposób od wersji Java 9:

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

Podobnie jak w oryginalnym groovym przykładzie, zakłada się, że zawartość jest zakodowana w UTF-8. (Jeśli potrzebujesz czegoś sprytniejszego, musisz utworzyć połączenie URL i użyć go do ustalenia kodowania).

Sean Reilly
źródło
1
Dzięki, właśnie tego szukałem. Może być również używany getClass().getResourceAsStream(...)do otwierania plików tekstowych w słoiku.
rjh
8

Dodatkowy przykład z użyciem guawy:

URL xmlData = ...
String data = Resources.toString(xmlData, Charsets.UTF_8);
takacsot
źródło
1
Dokumentacja Guava mówi link : Pamiętaj, że chociaż te metody używają parametrów {@link URL}, zwykle nie są odpowiednie dla HTTP lub innych zasobów spoza ścieżki klas
gaal,
3

Poniższe działa z Java 7/8, bezpiecznymi adresami URL i pokazuje, jak dodać plik cookie do żądania. Zauważ, że jest to głównie bezpośrednia kopia tej innej świetnej odpowiedzi na tej stronie , ale dodano przykład pliku cookie i wyjaśnienie, że działa również z bezpiecznymi adresami URL ;-)

Jeśli musisz połączyć się z serwerem z nieprawidłowym certyfikatem lub certyfikatem z podpisem własnym, spowoduje to wyświetlenie błędów zabezpieczeń, chyba że zaimportujesz certyfikat. Jeśli potrzebujesz tej funkcji, możesz rozważyć podejście opisane w tej odpowiedzi na to powiązane pytanie w StackOverflow.

Przykład

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

wyjścia

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

Kod

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
źródło
3

Oto cudowna odpowiedź Jeanne, ale zapakowana w uporządkowaną funkcję dla muppetów takich jak ja:

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
źródło
0

Adres URL do ciągu w czystej Javie

Przykładowe wezwanie

 String str = getStringFromUrl("YourUrl");

Realizacja

Możesz użyć metody opisanej w tej odpowiedzi, na temat Jak odczytać adres URL do obiektu InputStream i połączyć go z tą odpowiedzią na temat sposobu odczytywania InputStream na String .

Wynik będzie podobny

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);
    }
}

Plusy

  • To jest czysta Java

  • Można go łatwo ulepszyć, dodając różne nagłówki (zamiast przekazywania pustego obiektu, jak w powyższym przykładzie), uwierzytelnianie itp.

  • Obsługiwana jest obsługa przełączników protokołów

jschnasse
źródło