Xamarin에서 HtmlAgilityPack으로 AJAX 기다리고 있습니다.

ajax c# html-agility-pack xamarin

문제

나는 이전에 물어 보았던 질문이 있지만 조금 다릅니다. 이 웹 사이트 에서 데이터를 긁어 내려고 노력하고 있지만 문제는 그것이 AJAX로로드 된 것처럼 보입니다. 그 때문에 내 응용 프로그램은 내가 찾고있는 HTML에서 ID와 클래스를 찾을 수 없습니다.

요소를 검사하거나 소스를 보면이를 재현 할 수 있습니다. 소스를 보는 동안 요소를 검사하는 것보다 훨씬 적게 보입니다.

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

수락 된 답변

웹 요청을 사용하여 ajax 메소드를 호출하는 솔루션.

그래서 나는 지루해하고 대부분을 알아 냈습니다. 아래에 누락 된 점은 이드의 클라 스를 식별하는 방법입니다. 아래 예제는 klase '1GLD'를 가져옵니다. 쿠키를 필요로하는 이유는 우리가 클라스를 가져 오는 학교를 알기위한 요청 때문입니다. 또한 아래 코드는 JSON을 반환합니다. 우리가 호출하는 아약스 메서드이므로 HTML이 아닙니다.

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

마지막 업데이트

Selenium 솔루션을 사용하여 모든 과정에 대한 ID를 얻을 수있었습니다. 여기 에 파일을 포함 시켰으므로 아약스 및 웹 요청과 함께 사용할 수 있습니다.


인기 답변

나는 이것을 코멘트로 남겨 둘 예정이었다. 하지만 너무 크고 너무 형식이 잘못되었습니다. 그래서 여기에 우리가 간다.

첫째로. 이 사이트는 ajax 명령으로 호출 된 javascript를 사용하여 동적으로 업데이트됩니다.

세션을 열고 SESSIONID와 현재 "암호화 된"학교 이름이 들어있는 쿠키를 저장하면 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에서 데이터를 받게된다는 것입니다.

원하는 것을하기위한 가장 쉬운 방법은 모든 클래스를 별도의 파일에 저장하는 것 입니다. 그리고 이것을 기준점으로 사용하십시오. 다른 옵션에도 동일하게 적용됩니다.

그리고 다음과 같이 헤더없는 브라우저를 사용 phantomjs.org셀레늄 . 이 방법을 사용하면 스크래핑하려는 클래스를 찾아 클릭 할 수 있습니다. HTML을 HtmlAgilityPack.HtmlDocument에로드 한 다음 수행해야 할 작업을 수행하십시오. 셀레늄 / PhantomJS 귀하의 쿠키를 추적 할 때까지. 이 방법은 느리지 만 훨씬 쉽습니다.

EDIT Webrequest에서 쿠키 저장하기 - 쉬운 방법.

나는이 주제에 열중하지 않는다. 그러나 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();


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.