2017-11-25 13 views
3

私は以前に尋ねられたような質問がありますが、少し異なります。私はthis websiteからデータを削り取ろうとしていますが、問題はそれがAJAXでロードされているようです。そのため、私のアプリケーションでは、私が探しているHTMLのIDとクラスを見つけることができません。XamarinのHtmlAgilityPackでAJAXを待つ

要素を調べたり、ソースを表示することでこれを再現できます。ソースを見ている間、私は要素を調べている間よりずっと少なく見ています。

私は、F12キーを押してネットワークタブに行き、XHRを選択してこのhtmlを読み込むために、AJAXを含むファイルを追跡できたと思っていましたが、それを見つけることができません。

私の質問は:どのように私はこのデータを取得するか、またはデータを収集するために使用されたファイル を見つけるにはどうすればよいですか?

私のコードの例(私はTimetable_toolbar_elementSelect_popup0を見つけることができませんよ):

private async Task GetHtmlDocument(string url) 
     { 
      HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); 
      //request.Credentials = new LoginCredentials().Credentials; 

      try 
      { 
       WebResponse myResponse = await request.GetResponseAsync(); 
       HtmlDocument htmlDoc = new HtmlDocument(); 
       htmlDoc.OptionFixNestedTags = true; 
       htmlDoc.Load(myResponse.GetResponseStream()); 
       var test = htmlDoc.GetElementbyId("Timetable_toolbar_elementSelect_popup0"); 
      } 
      catch (Exception e) 
      { 
      } 
     } 
+0

正確に何か掻き出したいですか?私はこのウェブサイトにアクセスしており、Timetable_toolbar_elementSelect_popup0は表示されません。 – derloopkat

+0

@derloopkat申し訳ありませんが、あなたが正しいページに表示されるメニューの "Lesrooster"と "Klassen"をklikすれば、悪いです。しかし、明らかに、IDを持つコンテナが表示されるようにするには、最初に「Klas」の下にあるドロップダウンをクリックする必要があります。 – user3478148

+0

まだコメントを確認する機会がありませんでした。私は自分のプロジェクトを続けます。 – user3478148

答えて

0

Webリクエストを使用してajaxメソッドを呼び出すソリューションです。

だから私は退屈して、それのほとんどを考え出した。以下に欠けているのは、idによってKlaseを特定する方法です。以下の例は、クラース '1GLD'をフェッチします。私たちがクッキーを必要とする理由は、クラースを取り出す学校を知りたいという要求に応えるためです。また、以下のコードはJSONを返します。これはHTMLではなく、私たちが呼び出すajaxメソッドです。

CookieContainer cookies = new CookieContainer(); 
try 
{ 
    string webAddr = "https://roosters.windesheim.nl/"; 
    var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr); 
    httpWebRequest.ContentType = "application/json; charset=utf-8"; 
    httpWebRequest.Method = "POST"; 
    httpWebRequest.CookieContainer = cookies;   
    httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 
    httpWebRequest.Headers.Add("X-Requested-With", "XMLHttpRequest"); 

    var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); 
    using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) 
    { 
     cookies.Add(httpWebRequest.CookieContainer.GetCookies(httpWebRequest.RequestUri)); 
    } 
} 
catch (WebException ex) 
{ 
    Console.WriteLine(ex.Message); 
} 

//According to my web debugger the cookie will last until the 10th of December. So need to fix a new cookie until then. 
//I noticed the url used unixtimestamps at the end of the url. So we just add the unixtimestamp at the end for each request. 
long unixTimeStamp = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds() - 100; 

//we are now ready to call the ajax method and get the JSON. 
try 
{ 
    string webAddr = "https://roosters.windesheim.nl/WebUntis/Timetable.do?request.preventCache="+unixTimeStamp.ToString(); 
    var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr); 
    httpWebRequest.ContentType = "application/x-www-form-urlencoded; charset=utf-8"; 
    httpWebRequest.Method = "POST"; 
    httpWebRequest.CookieContainer = cookies; 
    httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 
    httpWebRequest.Headers.Add("X-Requested-With", "XMLHttpRequest"); 

    using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) 
    { 
     string json = "ajaxCommand=getWeeklyTimetable&elementType=1&elementId=13090&date=20171126&formatId=7&departmentId=0&filterId=-2"; 

     //The command below will return a JSON datastructure containing all the klases and their relevant ID. 
     //string otherJson = "ajaxCommand=getPageConfig&type=1&filter=-2" 


     streamWriter.Write(json); 
     streamWriter.Flush(); 
    } 


    var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); 
    using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) 
    { 
     var responseText = streamReader.ReadToEnd(); 
     //THE RESULTS GETS PRINTED HERE. 
     Console.Write(responseText); 
    } 
} 
catch (WebException ex) 
{ 
    Console.WriteLine(ex.Message); 
} 

その他のFirefox用Seleniumソリューションがあります。

これは簡単な方法です。それには時間がかかります。すべてのスレッドがスリープする必要はありません。これはあなたが要求したのと同じようにisnteadで動作するHTMLを与えます。しかし、私はそれが最後のforeachループで必要であることが分かった。

public static void Main(string[] args) 
{ 
    HtmlDocument doc = new HtmlDocument(); 
    //According to my web debugger the cookie will last until the 10th of December. So need to fix a new cookie until then. 
    //I noticed the url used unixtimestamps at the end of the url. So we just add the unixtimestamp at the end for each request. 
    long unixTimeStamp = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds() - 100; 
    string webAddr = "https://roosters.windesheim.nl/WebUntis/Timetable.do?request.preventCache="+unixTimeStamp.ToString(); 
    var ffOptions = new FirefoxOptions(); 
    ffOptions.BrowserExecutableLocation = @"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"; 
    ffOptions.LogLevel = FirefoxDriverLogLevel.Default; 
    ffOptions.Profile = new FirefoxProfile { AcceptUntrustedCertificates = true }; 
    var service = FirefoxDriverService.CreateDefaultService(); 

    var driver = new FirefoxDriver(service, ffOptions, TimeSpan.FromSeconds(120)); 


    driver.Navigate().GoToUrl(webAddr); 


    driver.FindElement(By.XPath("//input[@id='school']")).SendKeys("Windesheim"+Keys.Enter); 
    Thread.Sleep(2000); 
    driver.FindElement(By.XPath("//span[@id='dijit_PopupMenuBarItem_0_text' and text() ='Lesrooster']")).Click(); 

    driver.FindElement(By.XPath("//td[@id='dijit_MenuItem_0_text' and text() ='Klassen']")).Click(); 
    Thread.Sleep(2000); 

    driver.FindElement(By.XPath("//div[@id='widget_Timetable_toolbar_elementSelect']//input[@class='dijitReset dijitInputField dijitArrowButtonInner']")).Click(); 

    //we get all the options for Klase 
    doc.LoadHtml(driver.PageSource); 
    HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//div[@id='Timetable_toolbar_elementSelect_popup']/div[@item]"); 
    List<String> options = new List<String>(); 
    foreach (HtmlNode n in nodes) 
    { 
     options.Add(n.InnerText); 
    } 

    foreach(string s in options) 
    { 
     driver.FindElement(By.XPath("//input[@id='Timetable_toolbar_elementSelect']")).Clear(); 
     driver.FindElement(By.XPath("//input[@id='Timetable_toolbar_elementSelect']")).SendKeys(s); 
     Thread.Sleep(2000); 
     driver.FindElement(By.XPath("//body")).SendKeys(Keys.Enter); 
     Thread.Sleep(2000); 
     doc.LoadHtml(driver.PageSource); 
     //Console.WriteLine(driver.Url); //Now we can see the id of the current Klase 
    } 

    Console.WriteLine(doc.DocumentNode.InnerHtml); 

    Console.ReadKey(); 
} 

最終更新

私はすべてのコースのためのIDを取得することができたセレンのソリューションを使用します。私はファイルhereを添付して、あなたのajaxとウェブリクエストでそれを使うことができます。

1

私はコメントとしてこれを残すつもりでした。しかし、それはあまりにも大きくなり過ぎてひどくフォーマットされました。だからここに行く。

まず、このサイトは、ajaxコマンドで呼び出されるjavascriptを使用して動的に更新されます。

セッションを開き、SESSIONIDと現在「暗号化された」スクールネームを含むCookieを保存できる場合は、ajaxコマンドをそのように呼び出すことができます。

https://roosters.windesheim.nl/ajaxCommand=getWeeklyTimetable&elementType=1&elementId=13090&date=20171126&formatId=7&departmentId=0&filterId=-2 

これは、elementTypeがどの要素であり、どのelementIdが何であるかを知る必要があります。

この場合、elementIdは1GLDに等しいときにKlasを参照します。また、formatID(7)はRoosterformaatが "Beknopt"に等しいときに参照します。残りの変数が何をしているのか把握しなければなりません。さらに重要な点は、サーバーに有効なajaxコマンドを作成することができれば、htmlを応答として取得できないことです。これはJSONでデータを受け取ることになります。

すべてのクラスを別々のfileにすることが、最も簡単な方法です。それを基準点として使用してください。他のオプションも同じです。

phantomjs.orgSeleniumのようなヘッドレスブラウザを使用してください。この方法で、スクラップしたいクラスを見つけてクリックすることができます。 HTMLをHtmlAgilityPack.HtmlDocumentにロードしてから、必要な処理を行います。セレン/ PhantomJSあなたのクッキーを追跡するまで。 この方法は遅くなりますが、やりやすくなります。

EDIT Webリクエストからのクッキーの保存 - 簡単な方法。

私はこの件に熱心ではありません。しかし、OPは尋ねた。もし誰かがそれを行う良い方法があれば、編集してください。

CookieContainer cookies = new CookieContainer(); 
try 
{ 
    string webAddr = "https://roosters.windesheim.nl/WebUntis/"; 

    var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr); 
    httpWebRequest.ContentType = "application/json; charset=utf-8"; 
    httpWebRequest.Method = "POST"; 
    httpWebRequest.CookieContainer = cookies; 

    httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 
    httpWebRequest.Headers.Add("X-Requested-With", "XMLHttpRequest"); 
    using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) 
    { 
     string json = "ajaxCommand=getWeeklyTimetable&elementType=1&elementId=13092&date=20171126&formatId=7&departmentId=0&filterId=-2"; 

     streamWriter.Write(json); 
     streamWriter.Flush(); 
    } 


    var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); 
    using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) 
    { 
     cookies.Add(httpWebRequest.CookieContainer.GetCookies(httpWebRequest.RequestUri)); 
     //cookies.Add(httpResponse.Cookies); 
     var responseText = streamReader.ReadToEnd(); 
     doc.LoadHtml(responseText); 
     foreach(Cookie c in httpResponse.Cookies) 
     { 
      Console.WriteLine(c.ToString()); 
     } 
    } 
} 
catch (WebException ex) 
{ 
    Console.WriteLine(ex.Message); 
} 
    Console.WriteLine(doc.DocumentNode.InnerHtml); 

    Console.ReadKey(); 
+0

コメントの最後の段落に関しては、Seleniumを使用している場合、HtmlAgilityPackを使用してドキュメントを読み込む必要はありません。セレンは、xpath、css、idセレクタをサポートしています。 HtmlAgilityPackは、HTMLを解析するための単なるライブラリであり、xpathをサポートしていますが、ブラウザはバックグラウンドで実行されません。 – derloopkat

+0

ありがとうございます。これは私が望むよりもはるかに複雑なようです。質問:「セッションを開き、SESSIONIDと現在の「暗号化された」schoolnameが含まれているCookieを保存できれば、これを行う方法がわかりません。正しい方向に向けることができますか?私はSelenium/PhantomJSを調べます – user3478148

関連する問題