HTMLAgility Pack: Screen Scraping Kann eine Div mit Bindestrich im Klassennamen nicht finden?

c# html-agility-pack screen-scraping

Frage

Dies ist eine Art Lernübung, aber auch "Spaß". Im Grunde versuche ich den Preis eines "Balkon" -Zimmerpreises (derzeit bei 1039 $) in einer C # -Konsolenanwendung zu analysieren. Die URL ist:

http://www.carnival.com/BookingEngine/Stateroom/Stateroom2/?embkCode=PCV&itinCode=SC0&durDays=8&shipCode=SH&subRegionCode=CS&sailDate=08082015&sailingID=68791&numGuests=2&showDbl=False&isOver55=N&isPastGuest=N&stateCode=&isMilitary=N&evsel=&be_version=1

Ich habe die obige URL geladen in:

var document = getHtmlWeb.Load(web_address);

Der Container für die Balcony-Preise ist ein div mit der Klasse ' col ' und ist das dritte div innerhalb der column-container clearfix Klasse. Ich dachte, alles, was ich brauche, wäre, alle Divs mit Klasse zu übertreffen.

var lowest_price = document.DocumentNode.SelectNodes("//div[@class='col-bottom']");

und wählen Sie dann den 3. Knoten, um zu den Balkonpreisen zu gelangen. Aber die Variable lowest_price gibt immer null aus. Ich weiß, dass das Dokument selbst geladen ist und ich kann innerhalb des ' col ' sehen, wenn ich ' col ' auswähle. Ist es der Bindestrich im col-bottom der das Finden dieses Div verhindert?

Irgendein alternativer Weg, um das zu erreichen? Wie gesagt, es ist meist eine Lernübung. Aber ich muss einige benutzerdefinierte Überwachungslösungen erstellen, die Screen Scraping erfordern und so ist es nicht nur Spaß.

Vielen Dank!

EDIT HTML Snippet mit den relevanten Informationen:

    <div class="col">
      <h2 data-cat-title="Balcony" class="uk-rate-title-OB"> Balcony </h2>   <p>&nbsp;</p>
        <div class="col-bottom">
        <h3> From</h3>
         <strong> $1,039.00* <span class="rate-compare-strike"> </span> </strong><a metacode="OB" href="#" class="select-btn">Select</a> </div>
    </div>

Beliebte Antwort

Nichts ist falsch mit Bindestrichen in attribute Namen oder Werte das ist gültige HTML, das Problem mit Ihrer Quelle ist, dass sie Javascript auf dem Client verwenden, um das HTML zu rendern, um zu überprüfen, dass Sie die HTML-Seite herunterladen können und Sie werden die Elemente bemerken suchen nicht existieren.

Um solche Seiten zu parsen, wo JavaScript zuerst ausgeführt werden muss, könnten Sie dafür ein Webbrowser-Steuerelement verwenden und dann das HTML an HAP übergeben.

Im Folgenden finden Sie ein einfaches Beispiel für die Verwendung des WinForms-Webbrowser-Steuerelements:

private void ParseSomeHtmlThatRenderedJavascript(){
        var browser = new System.Windows.Forms.WebBrowser() { ScriptErrorsSuppressed = true };

        string link = "yourLinkHere";

        //This will be called when the web page loads, it better be a class member since this is just a simple demonstration
        WebBrowserDocumentCompletedEventHandler onDocumentCompleted = new WebBrowserDocumentCompletedEventHandler((s, evt) => {
            //Do your HtmlParsingHere
            var doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(browser.DocumentText);
            var someNode = doc.DocumentNode.SelectNodes("yourxpathHere");
        });

        //subscribe to the DocumentCompleted event using our above handler before navigating
        browser.DocumentCompleted += onDocumentCompleted;

        browser.Navigate(link);
    }

Sie können sich auch Awesomium und einige andere eingebettete WebBrowser-Steuerelemente ansehen.

Auch wenn Sie den WebBrowser in der Konsolen-App ausführen wollen ist hier ein Beispiel, wenn Sie es nicht die Windows-Formulare verwenden, wird dieses Beispiel mit Hilfe dieser SO WebBrowser- Steuerelement in einem neuen Thread beantworten

    using System;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using HtmlAgilityPack;
    namespace ConsoleApplication276
    {

        // a container for a url and a parser Action
        public class Link
        {
            public string link{get;set;}
            public Action<string> parser { get; set; }
        }

        public class Program
        {

            // Entry Point of the console app
            public static void Main(string[] args)
            {
                try
                {
                    // download each page and dump the content
                    // you can add more links here, associate each link with a parser action, as for what data should the parser generate create a property for that in the Link container

                    var task = MessageLoopWorker.Run(DoWorkAsync, new Link() { 
                        link = "google.com", 
                        parser = (string html) => {

                            //do what ever you need with hap here
                            var doc = new HtmlAgilityPack.HtmlDocument();
                            doc.LoadHtml(html);
                            var someNodes = doc.DocumentNode.SelectSingleNode("//div");

                        } });


                    task.Wait();
                    Console.WriteLine("DoWorkAsync completed.");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("DoWorkAsync failed: " + ex.Message);
                }

                Console.WriteLine("Press Enter to exit.");
                Console.ReadLine();
            }

            // navigate WebBrowser to the list of urls in a loop
            public static async Task<Link> DoWorkAsync(Link[] args)
            {
                Console.WriteLine("Start working.");

                using (var wb = new WebBrowser())
                {
                    wb.ScriptErrorsSuppressed = true;

                    TaskCompletionSource<bool> tcs = null;
                    WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
                        tcs.TrySetResult(true);

                    // navigate to each URL in the list
                    foreach (var arg in args)
                    {
                        tcs = new TaskCompletionSource<bool>();
                        wb.DocumentCompleted += documentCompletedHandler;
                        try
                        {
                            wb.Navigate(arg.link.ToString());
                            // await for DocumentCompleted
                            await tcs.Task;
                            // after the page loads pass the html to the parser 
                            arg.parser(wb.DocumentText);
                        }
                        finally
                        {
                            wb.DocumentCompleted -= documentCompletedHandler;
                        }
                        // the DOM is ready
                        Console.WriteLine(arg.link.ToString());
                        Console.WriteLine(wb.Document.Body.OuterHtml);
                    }
                }

                Console.WriteLine("End working.");
                return null;
            }

        }

        // a helper class to start the message loop and execute an asynchronous task
        public static class MessageLoopWorker
        {
            public static async Task<Object> Run(Func<Link[], Task<Link>> worker, params Link[] args)
            {
                var tcs = new TaskCompletionSource<object>();

                var thread = new Thread(() =>
                {
                    EventHandler idleHandler = null;

                    idleHandler = async (s, e) =>
                    {
                        // handle Application.Idle just once
                        Application.Idle -= idleHandler;

                        // return to the message loop
                        await Task.Yield();

                        // and continue asynchronously
                        // propogate the result or exception
                        try
                        {
                            var result = await worker(args);
                            tcs.SetResult(result);
                        }
                        catch (Exception ex)
                        {
                            tcs.SetException(ex);
                        }

                        // signal to exit the message loop
                        // Application.Run will exit at this point
                        Application.ExitThread();
                    };

                    // handle Application.Idle just once
                    // to make sure we're inside the message loop
                    // and SynchronizationContext has been correctly installed
                    Application.Idle += idleHandler;
                    Application.Run();
                });

                // set STA model for the new thread
                thread.SetApartmentState(ApartmentState.STA);

                // start the thread and await for the task
                thread.Start();
                try
                {
                    return await tcs.Task;
                }
                finally
                {
                    thread.Join();
                }
            }
        }
    }


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum