Java folgt nicht einer redirected URL

Beim parsen einer Webseite findet sich ein Link dem ich mit einem Java-Client nicht folgen kann, weil er redirected wird. Im Browser macht dieser Link kein Problem - er wird automatisch durch die Redirection ersetzt und dann die entsprechende Seite auch richtig dargestellt. So wird der Link „http://www.onvista.de/aktien/snapshot.html?ID_OSI=36714349“ über „Adidas Aktie (A1EWWW | DE000A1EWWW0) • onvista“ aufgelöst.

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class GetRedirected {

    public GetRedirected() throws MalformedURLException, IOException {
        String url="http://www.onvista.de/aktien/snapshot.html?ID_OSI=36714349";
        URLConnection con = new URL( url ).openConnection();
        System.out.println( "orignal url: " + con.getURL() );
        con.connect();
        System.out.println( "connected url: " + con.getURL() );
        InputStream is = con.getInputStream();
        System.out.println( "redirected url: " + con.getURL() );
        is.close();
    }

    public static void main(String[] args) throws Exception {
        new GetRedirected();
    }
}

endet an der Zeile „InputStream is = …“ mit der folgenden Meldung.

orignal url: www.onvista.de/aktien/snapshot.html?ID_OSI=36714349
connected url: www.onvista.de/aktien/snapshot.html?ID_OSI=36714349
Exception in thread „main“ java.io.IOException: Server returned HTTP
response code: 403 for URL: www.onvista.de/aktien/snapshot.html?ID_OSI=36714349
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at de.gombers.broker…

Ich habe es auch mit " (HttpURLConnection) url.openConnection(); und dort mit der Methode „setFollowRedirects(True)“ probiert - ebenfalls ohne Erfolg.

Über einen helfenden Hinweis würde ich mich sehr freuen. Neben den beiden erwähnten Herangehensweisen habe ich im Netz bisher nicht gefunden.

Ohne dir jetzt konkret weiterzuhelfen, empfehle ich für solche Aufgabe den HttpClient von den Apache HttpComponents zu benutzen. Zumindest, wenn das was du machen möchtest über einfachste Requests hinausgeht (Authentifizierung, Header parsen, Kompression…). Sowas geht zwar auch mit den Basisfunktionen, ist aber deutlich unhandlicher und unflexibler.

Zu deinem Fehler: du bekommst einen 403er Fehler, also die Aussage, dass du unzureichende Berechtigungen hast. Da ich die Meldung im Browser nicht bekomme (die Request-Header in Chrome anzeigen lassen, da sieht man das), läge es nahe, dass der Server auf die Anfrage anders reagiert. Häufige Filter sind Filter nach dem UserAgent. Ich habe den Request mit curl probiert, das funktioniert problemlos. Bei der Java-Standardbibliothek weiß ich nicht, wie man den UserAgent ändert - mit dem HttpClient geht das ganz einfach.

Funktioniert hat folgender Request:
[spoiler]

$ curl -L -vvv 'http://www.onvista.de/aktien/snapshot.html?ID_OSI=36714349'
* About to connect() to www.onvista.de port 80 (#0)
*   Trying 217.11.205.10...
* connected
* Connected to www.onvista.de (217.11.205.10) port 80 (#0)
> GET /aktien/snapshot.html?ID_OSI=36714349 HTTP/1.1
> User-Agent: curl/7.26.0
> Host: www.onvista.de
> Accept: */*
>
* additional stuff not fine transfer.c:1037: 0 0
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 301 Moved Permanently
< Server: nginx
< Date: Sat, 15 Feb 2014 07:56:38 GMT
< Content-Type: text/html
< Content-Length: 0
< Connection: keep-alive
< Location: http://www.onvista.de/aktien/Adidas-Aktie-DE000A1EWWW0
< X-Varnish: 1253356308 1253355966
< Age: 42
< Via: 1.1 varnish
< X-Cache: HIT
< X-UA-Compatible: IE=Edge
<
* Connection #0 to host www.onvista.de left intact
* Issue another request to this URL: 'http://www.onvista.de/aktien/Adidas-Aktie-DE000A1EWWW0'
* Re-using existing connection! (#0) with host (nil)
* Connected to (nil) (217.11.205.10) port 80 (#0)
> GET /aktien/Adidas-Aktie-DE000A1EWWW0 HTTP/1.1
> User-Agent: curl/7.26.0
> Host: www.onvista.de
> Accept: */*
>
* additional stuff not fine transfer.c:1037: 0 0
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Server: nginx
< Date: Sat, 15 Feb 2014 07:56:39 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
< Expires: Sat, 15 Feb 2014 07:57:08 GMT
< Cache-Control: no-cache,s-maxage=30,must-revalidate
< X-Varnish: 1865865595
< Age: 0
< Via: 1.1 varnish
< X-Cache: MISS
< X-UA-Compatible: IE=Edge
<

[/spoiler]

*** Edit ***

Es liegt definitiv am Useragent:

$ curl -L -vvv -A 'Java/1.8.0' 'http://www.onvista.de/aktien/snapshot.html?ID_OSI=36714349'
* About to connect() to www.onvista.de port 80 (#0)
*   Trying 217.11.205.10...
* connected
* Connected to www.onvista.de (217.11.205.10) port 80 (#0)
> GET /aktien/snapshot.html?ID_OSI=36714349 HTTP/1.1
> User-Agent: Java/1.8.0
> Host: www.onvista.de
> Accept: */*
>
* additional stuff not fine transfer.c:1037: 0 0
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 403 Forbidden
< Server: nginx
< Date: Sat, 15 Feb 2014 08:05:54 GMT
< Content-Type: text/html
< Content-Length: 0
< Connection: keep-alive
< X-Varnish: 1253360534
< Age: 0
< Via: 1.1 varnish
< X-Cache: MISS
<
* Connection #0 to host www.onvista.de left intact
* Closing connection #0

*** Edit ***

Kurzes googlen zeigt, dass der UserAgent sich scheinbar nur durch setzen einer Systemproperty richtig setzen lässt, was nicht sonderlich schön ist:

Also: HttpClient nutzen.

Herzlichen Dank, auch für die ausfühliche Erläuterung - das war die Lösung. Ich bin mit dem HTTP-Protokoll nicht so besonders vertraut; deshalb fehlte mir jede Idee wie man hier weiter vorgehen könnte.

[QUOTE=cmrudolph]Bei der Java-Standardbibliothek weiß ich nicht, wie man den UserAgent ändert - mit dem HttpClient geht das ganz einfach.
// …
Kurzes googlen zeigt, dass der UserAgent sich scheinbar nur durch setzen einer Systemproperty richtig setzen lässt, was nicht sonderlich schön ist:

Also: HttpClient nutzen.[/QUOTE]

also entweder rall ichs nich … oder du hast eine andere vorstellung von “einfach”

so weit ich mir jetzt mal spontan die DOC angesehen habe hat sich eigentlich nur eines mal wieder bestätigt : apache commons sind der größte scheiß
das package org.apache.http.client besteht erstmal NUR aus interfaces und exceptions … keine factory … kein gar nichts
stattdessen findet man einen HttpClientBuilder im package org.apache.http.impl.client … ähm … ok … wer bitte hat hier schon wieder nicht nachgedacht ?
wenn man ein package schon IMPL nennt … und den user dazu zwingt dies dann auch direkt zu nutzen … hat man mehr als nur einen gewaltigen fehler in seiner struktur … denn dieser factory-klasse gehört eigentlich in org.apache.http.client … um eben die implementierung nich dem user aufzubürgen … aber sowas findet sich ja überall bei den apache commons

und dann muss ich dir sagen : so wie es diese lib macht ist es ja mal mehr als nur umständlich “vernünftig” den user-agent zu setzen

folgendes muss man mit apache machen

builder.setUserAgent("BANANA");
CloseableHttpClient client=builder.build();
HttpHost host=new HttpHost("http://forum.byte-welt.net");
HttpRequest request=new BasicHttpRequest("GET", "/threads/11475-Java-folgt-nicht-einer-redirected-URL");
CloseableHttpResponse response=client.execute(host, request);
// ... verarbeitung des response```

mit der SE-API gehts einfacher

```URL url=new URL("http://forum.byte-welt.net/threads/11475-Java-folgt-nicht-einer-redirected-URL");
URLConnection con=url.openConnection();
con.setRequestProperty("User-Agent", "BANANA");
con.connect();
// ... verarbeitung```

und das wars ... DAS nenne ich einfach ... anstatt sich da erstmal mit ner komplett verwursteten lib was zusammenstecken zu müssen



und zu deinem LINK : dir ist aber schon aufgefallen das der user mit Java/1.5.0_19 unterwegs war ... und das im märz 2010 ... WTF ?=! hier würde ich aber mal mehr als nur n user-fehler anmerken ... ggf das ganze auch noch unter MAC mit ner AppleVM ... und das chaos ist komplett ...
ganz erlich ... 2010 mit ner 5er VM ? selbst schuld wenn irgendwas nich hinhaut
klar ... normalerweise hätte es auch mit so ner alten VM klappen sollen ... aber wer weis ... vielleicht war die IMPL ja noch so gebaut das man wirklich über ein system-property arbeiten musste ...

das hat nichts mit "nicht sonderlich schön" oder "nutz ne lib weil ich selbst keine ahnung von der SE-API habe" zu tun ... sondern einfach n unglücklicher umstand den google uns heute, knapp 4 jahre und fast 3 major-releases später, leider immer noch liefert
so nem schrott würde ich keine beachtung mehr schenken ... wer weis was da wirklich die ursache für den fehler war


@TO
man muss nicht extra HttpURLConnection.setFollowRedirects(true) setzen ... denn laut DOC

> True by default.

ergo : die methode macht nur sinn wenn man selbst explizit das auto-redirect abschalten will

Du hast recht, in diesem Fall ist es einfacher. Der User-Agent wird (zumindest unter Java 8) auch - entgegen der StackOverflow-Aussage - auch wirklich auf „BANANA“ und nicht „BANANAJava/1.8.0“ gesetzt.

Allerdings: [quote=cmrudolph;86879][…] empfehle ich für solche Aufgabe den HttpClient von den Apache HttpComponents zu benutzen. Zumindest, wenn das was du machen möchtest über einfachste Requests hinausgeht (Authentifizierung, Header parsen, Kompression…)[/quote]
Siehe zur Verwendung auch hier: https://hc.apache.org/httpcomponents-client-4.3.x/examples.html
Wie sieht es aus, wenn parallele Requests gemacht werden sollen? Wenn für die parallelen Requests eine maximale Anzahl paralleler Verbindungen festgesetzt werden soll? Wenn Authentifizierung dazu kommt? Wenn Kompression hinzukommt? Wenn man transparent Inhalte verändern möchte? Wenn man Verbindungen wiederverwenden möchte?
Bis auf bei der Kompression vielleicht ist man mit dem HttpClient besser aufgestellt als wenn man mit der Standardbibliothek arbeitet.

HttpClient ist eine Bibliothek, die eine Abstraktion zur Verfügung stellt. Dass das ein paar Minuten Einarbeitungszeit erfordert ist eben so. Und dass die Abstraktion vielleicht nicht deinen Vorstellungen einer vernünftigen Paketaufteilung entspricht (da stimme ich dir sogar zu - die Aufteilung ist nichtsdestotrotz nicht unüblich)… was will man machen?
Auf jeden Fall hat man damit eine Bibliothek, die einem sehr viel Arbeit abnimmt und sehr leicht erweiterbar ist.