Laden...

Automatisierter Download von PDF-Dateien aus indirekter URL

Erstellt von Arakiss vor 12 Jahren Letzter Beitrag vor 12 Jahren 3.821 Views
A
Arakiss Themenstarter:in
5 Beiträge seit 2011
vor 12 Jahren
Automatisierter Download von PDF-Dateien aus indirekter URL

Hallo liebe myCSharp-Gemeinde,

Oft habt ihr mir schon geholfen mit Themen, die bereits behandelt wurden. Dieses Mal konnte ich leider nichts passendes finden und habe mich kurzerhand registriert. Ich hoffe, ich habe keine Ergebnisse übersehen, aber ich suche seit 2 Stunden in google und bei euch und konnte keine funktionierende Lösung finden. Ferner hoffe ich, dass dies der passende Forumsbereich ist.

Ich muss PDF-Dateien herunterladen aus einem gegebenen Link. Dieser Link funktioniert, wenn ich ihn in den Browser eingebe, aber es ist halt kein direkter Link auf die PDF-Datei. Das PDF wird nur im Browser dargestellt.
Der Link sieht zum Beispiel so aus:
http://doc.morningstar.com/LatestDoc.aspx?clientid=on-demand&key=a20d5eed49c96564&language=0L00000122&sid=F0000001W6&dt=52

Natürlich kann ich den Link mit folgendem Code runterladen:

WebClient webClient = new WebClient();
webClient.DownloadFile(@"http://doc.morningstar.com/LatestDoc.aspx?clientid=on-demand&key=a20d5eed49c96564&language=0L00000122&sid=F0000001W6&dt=52", @"c:\myfile.pdf");

Dabei kommt allerdings nur eine unbrauchbare PDF-Datei raus. In Wirklichkeit lädt er das HTML der Seite runter. Zum Beispiel:

<html>
  <head>
    <title>Morningstar Document Library</title>
    <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" Content="C#">
    <meta name=vs_defaultClientScript content="JavaScript">
    <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
  </head>
    <frameset rows="30,*">
		<frame name="DocHead" src="docheader.aspx?id=F0000001W6&type=1&dt=52&market=&language=0L00000122&format=&fl=False" frameBorder="no"
			id="DocHead" scrolling="no">
		<frame name="DocDetail" frameBorder="no" id="DocDetail">
			
		</frame>
	</frameset>
</html>

Jetzt ist meine Frage, wie ich denn an die PDF-Datei zum Download ran komme. Also im Endeffekt müsste das Programm die URL irgendwie aufrufen und dann das PDF aus dem Frame heraus finden und herunterladen.

Für eure Hilfe wäre ich sehr dankbar.

Gruß,

Arakis

*Edit: Was ich noch vergas: Der Frame scheint nach dem Laden über javascript modifiziert zu werden. Im Firefox sieht der HTML-Code (Der wenn man F12 drückt, nicht der Quellcode) des Frames so aus:

<frame id="DocDetail" frameborder="no" name="DocDetail">
<html>
<head>
<title>original (application/pdf-Objekt)</title>
</head>
<body marginwidth="0" marginheight="0"><embed width="100%" height="100%" name="plugin" src="http://doc.morningstar.com/Document/8dfc036c3f4e486e968a1f3cc026dd2c.msdoc/original?track=0" type="application/pdf">
</body>
</html>
</frame>

T
156 Beiträge seit 2010
vor 12 Jahren

Hallo,
keine Ahnung, ob es rechtlich korrekt ist, die PDF dort automatisiert runterzuladen.
Aber Du scheinst ja einen Zugang zu haben.
Habe mal etwas mit der Seite rumgespielt.
Also, bei dem Aufruf des Links, den Du gepostet hast, wird zum Client ein Cookie übertragen, in dem ein.ASPXAUTH-Wert drin steht. Dieses Cookie wird benötigt, um die Seite, dessen Link im frameset hinterlegt ist (also die docheader.aspx) aufzurufen. Ist das Cookie nicht vorhanden, so wird man automatisch auf die Startseite weitergeleitet.
Der Inhalt der docheader.aspx sieht dann so aus (gekürzt):


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
	Morningstar Document Library
.
.
.
<script type="text/javascript">
    function Init()
    {     
      parent.frames["DocDetail"].location="Document/30940840d5de0dcff1ac26ab028db07a.msdoc?track=0";
     }
</script>
</html>

Wie man sieht, ist in der Init()-Funktion, die automatisch aufgerufen wird, der direkte Link zu der PDF enthalten. Also in diesem Fall
http://doc.morningstar.com/Document/30940840d5de0dcff1ac26ab028db07a.msdoc?track=0
Der Code sieht also folgendermaßen aus:


Cookie authCookie = null;
string docHeaderUrl = null;
string key = "Dein Key";

Uri baseUri = new Uri("http://doc.morningstar.com");
Uri cookieRequestUri = new Uri(baseUri, string.Format("LatestDoc.aspx?clientid=on-demand&key={0}&language=0L00000122&sid=F0000001W6&dt=52", key));
HttpWebRequest cookieRequest = (HttpWebRequest)WebRequest.Create(cookieRequestUri);
cookieRequest.CookieContainer = new CookieContainer();
using (HttpWebResponse cookieResponse = (HttpWebResponse)cookieRequest.GetResponse())
{
   using (StreamReader cookieResponseReader = new StreamReader(cookieResponse.GetResponseStream()))
   {
      string content = cookieResponseReader.ReadToEnd();
      Regex regex = new Regex("<frame name=\"DocHead\" src=\"(?<url>.+?)\"");
      if (regex.IsMatch(content))
         docHeaderUrl = regex.Match(content).Groups["url"].Value;
   }
   authCookie = cookieResponse.Cookies.OfType<Cookie>().FirstOrDefault(c => c.Name == ".ASPXAUTH");
}
if (string.IsNullOrEmpty(docHeaderUrl))
   throw new InvalidOperationException("no docHeaderUrl was found");
if (authCookie == null)
   throw new InvalidOperationException("no cookie was received");

HttpWebRequest mainRequest = (HttpWebRequest)WebRequest.Create(new Uri(baseUri, docHeaderUrl));
mainRequest.CookieContainer = new CookieContainer();
mainRequest.CookieContainer.Add(authCookie);
mainRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

string documentUri = null;
using (HttpWebResponse mainRequestResponse = (HttpWebResponse)mainRequest.GetResponse())
{
   using (StreamReader mainRequestResponseReader = new StreamReader(mainRequestResponse.GetResponseStream()))
   {
      string content = mainRequestResponseReader.ReadToEnd();
      Regex regex = new Regex("parent\\.frames\\[\"DocDetail\"\\]\\.location=\"(?<url>.+?)\"");
      if (regex.IsMatch(content))
         documentUri = regex.Match(content).Groups["url"].Value;
   }
}
if (string.IsNullOrEmpty(documentUri))
   throw new InvalidOperationException("no documentUrl was found");

Konnte den Code nicht wirklich gut testen, da mein PC mit dem blöden Proxy (Fiddler) spinnt. Visual Studio hat sich immer richtig aufgehangen. Aber hoffe, es klappt so.

//edit: Code verbessert:

  • docHeader-Url wird automatisch ausgelesen
  • download-Url wird automatisch ausgelesen
  • Fehlerbehandlung
  • Key ausgekapselt (sollte nicht für jeden sichtbar sein!)
    LG, Marko
A
Arakiss Themenstarter:in
5 Beiträge seit 2011
vor 12 Jahren

Vielen danke trashkid,
Sieht schon mal sehr gut aus und ich werde es später gleich ausführlich testen. Rechtlich gesehen, darf das nicht jeder bei denen, aber die Firma, für die ich tätig bin und das mache, hat einen gültigen Vertrag. Insofern ist das legal und abgesprochen.

Ich werde später oder morgen noch eine Rückmeldung geben.

Grüße,

Arakis

T
156 Beiträge seit 2010
vor 12 Jahren

Hi,
so, habe nochmal meinen Beitrag oben geändert und den Code geändert/ verbessert.
Also die DL-URL wird schonmal korrekt ausgelesen, aber am Download hapert es derzeit noch. Irgendwie erhalte ich keine Response...
Mal schauen, woran es hapert.

A
Arakiss Themenstarter:in
5 Beiträge seit 2011
vor 12 Jahren

Ich habe es hinbekommen. Auch wenn es ein bisschen improvisiert ist.
Habe den folgenden Code benutzt:

string requestUri = "http://doc.morningstar.com/LatestDoc.aspx?clientid=on-demand&key=a20d5eed49c96564&language=0L00000122&sid=F0000001W6&dt=52";
Cookie cookie = null;
HttpWebRequest cookieRequest = (HttpWebRequest)WebRequest.Create(requestUri);
cookieRequest.CookieContainer = new CookieContainer();
using (HttpWebResponse cookieResponse = (HttpWebResponse)cookieRequest.GetResponse())
{
	cookie = cookieResponse.Cookies.OfType<Cookie>().FirstOrDefault();
}
if (cookie == null)
	throw new InvalidOperationException("no cookie was received");

String tempUrl = "";
String temprequestcontent;
HttpWebRequest temprequest = (HttpWebRequest)WebRequest.Create(requestUri);
using (StreamReader reader = new StreamReader(temprequest.GetResponse().GetResponseStream()))
{
	while ((temprequestcontent = reader.ReadLine()) != null)
	{
		Console.WriteLine(temprequestcontent);
		if (temprequestcontent.Contains("docheader.aspx?"))
		{
			Console.WriteLine(temprequestcontent);
			Console.WriteLine(temprequestcontent.IndexOf("docheader.aspx?", 20).ToString());
			int anf = temprequestcontent.IndexOf("docheader.aspx?", 20);
			string newS = temprequestcontent.Remove(0, anf);
			newS = newS.Remove(newS.Length - 18);
			tempUrl = @"http://doc.morningstar.com/" + newS;
			Console.WriteLine(newS);
		}
	}
}

HttpWebRequest mainRequest = (HttpWebRequest)WebRequest.Create(tempUrl);

mainRequest.CookieContainer = new CookieContainer();
mainRequest.CookieContainer.Add(cookie);
mainRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

string mainResponseContent;
using (StreamReader reader = new StreamReader(mainRequest.GetResponse().GetResponseStream()))
{
	while ((mainResponseContent = reader.ReadLine()) != null)
	{
		Console.WriteLine(mainResponseContent);
		if (mainResponseContent.Contains("DocDetail") && mainResponseContent.Contains("Document"))
		{
			Console.WriteLine(mainResponseContent);
			Console.WriteLine(mainResponseContent.IndexOf("Document", 30).ToString());
			int anf = mainResponseContent.IndexOf("Document", 30);
			string newS = mainResponseContent.Remove(0,anf);
			newS = newS.Remove(newS.Length - 2);
			tempUrl = @"http://doc.morningstar.com/" + newS;
			Console.WriteLine(newS);
		}
	}
}

mainRequest = (HttpWebRequest)WebRequest.Create(tempUrl);
mainRequest.CookieContainer = new CookieContainer();
mainRequest.CookieContainer.Add(cookie);
mainRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

Stream stream = mainRequest.GetResponse().GetResponseStream();

if (stream != null)
{
	if (File.Exists(@"c:\myfile.pdf"))
		File.Delete(@"c:\myfile.pdf");

	FileStream fs = new FileStream(@"c:\myfile.pdf", FileMode.CreateNew, FileAccess.Write);

	byte[] buffer = new byte[5000];
	int read = 0;

	while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
	{
		fs.Write(buffer, 0, read);
	}

	stream.Close();
	fs.Close();
}

Sieht ein bisschen wüst aus, aber für Schönheit hab ich erst bald wieder Zeit.

Also bei mir läuft das automatisch bis zur fertigen Datei im angegeben Verzeichnis.
Vielen Dank nochmal für die Hilfe!

Gruß,

Arakis

2.891 Beiträge seit 2004
vor 12 Jahren

Viel Aufwand könntest du dir sparen, indem du die WebClient-Klasse (System.Net) benutzt. Für Sessions/Cookies gibt's den WebClient mit HTTP-POST- und Cookie-Unterstützung.
Für das Auffinden der URL würde sich zudem die Regex-Klasse (System.Text.RegularExpressions) anbieten.

V
66 Beiträge seit 2010
vor 12 Jahren

Viel Aufwand könntest du dir sparen, indem du die
>
benutzt. [...]

Klarer Fall von Eingangsposting nicht gelesen?... 😄

A
Arakiss Themenstarter:in
5 Beiträge seit 2011
vor 12 Jahren

Danke für die Anregungen, werde ich mir auf jeden Fall genauer anschauen.
Bin aber schon heilfroh, dass es so läuft. Immerhin schon 14 Stunden fehlerfrei ein paar tausend PDFs runter geladen 😉

2.891 Beiträge seit 2004
vor 12 Jahren

Klarer Fall von selber Eingangsposting nicht verstanden: Das Problem ist nicht der WebClient, sondern das zweistufige Laden. Erst die HTML-Datei holen, dann daraus die eigentliche URL extrahieren und schließlich die richtige PDF-Datei laden.

Das ganze Rumhantiere mit WebRequest & -Response, Streams, usw. macht der WebClient für einen.

T
156 Beiträge seit 2010
vor 12 Jahren

Hi,
also gegen die Verwendung von WebRequest, WebResponse und Co kann man vielleicht streiten. Aber muss man nicht. So hat man wenigstens volle Kontrolle über das, was geschieht. Schön, dass es immer für alles eine andere Lösung gibt.

Und auch schön, dass es immer welche gibt, die es besser wissen müssen 😉
Ist nicht böse gemeint. Aber erst antwortet keiner, und wenn dann eine funktionierende Lösung gepostet wurde, wird diese auch gleich mal auseinandergenommen.

Das große Problem hierbei war das sogar 3-stufige laden der Daten. Und zu verstehen bzw. herauszubekommen, wie die Kommunikation mit der Seite (speziell dem Cookie) erfolgen muss. Mit welcher Technik man dann die Daten von der Seite zieht ist ein anderes Thema.

Das mit den Stringoperationen zum auslesen der Adressen aus den html ist wirklich nicht so schön. Aber eine Variante unter Verwendung regulärer Ausdrücke hatte ich ja oben schon gepostet.
Marko

2.891 Beiträge seit 2004
vor 12 Jahren

Aber erst antwortet keiner, und wenn dann eine funktionierende Lösung gepostet wurde, wird diese auch gleich mal auseinandergenommen

Sorry, das sollte kein auseinandernehmen werden. Das sollte lediglich eine informative Ergänzung sein.
Uns sorry, dass ich Sonntags nicht online war...

Information von MarsStein vor 12 Jahren

Nun lasst wieder gut sein und kommt bitte zum Thema zurück.

A
Arakiss Themenstarter:in
5 Beiträge seit 2011
vor 12 Jahren

Ja wie ich die Strings zerlege, ist echt hässlich, aber hab ja extra dazu geschrieben, dass ich da nur kurz improvisiert habe. Und wie man schön Strings zerlegt, war ja auch nicht das Problem, sondern eben das, wie ich an die Datei komme und das klappt ja 😉