Pack HTMLAgility: Scraping Screen Impossible de trouver une div avec un trait d'union dans la classe Nom?

c# html-agility-pack screen-scraping

Question

C'est un peu un exercice d'apprentissage, mais aussi une partie de plaisir. En gros, j'essaie d'analyser le prix du prix d'une chambre d'état 'Balcony' (actuellement à 1039 $) dans une application console C #. L'URL est:

Méthodes

J'ai l'URL ci-dessus chargé bien dans:

var document = getHtmlWeb.Load(web_address);

Le conteneur pour les prix Balcony est une division avec la classe ' col ' et constitue la 3ème column-container clearfix classe column-container clearfix . Je pensais que tout ce dont j'avais besoin serait d'amender toutes les divs avec classe par:

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

puis sélectionnez le 3ème nœud pour obtenir les prix Balcon. Mais la variable low_price renvoie toujours null. Je sais que le document lui-même est chargé et je peux voir à l'intérieur du " col " si je sélectionne " col ". Est-ce le trait d'union dans la col-bottom qui empêche la découverte de cette div?

Un autre moyen d’y arriver? Comme je l'ai dit, c'est surtout un exercice d'apprentissage. Mais je dois créer des solutions de surveillance personnalisées qui nécessitent un nettoyage d’écran. Ce n’est donc pas uniquement amusant.

Merci!

EDITER extrait de code HTML contenant les informations pertinentes:

    <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>

Réponse populaire

Il n’ya rien de mal avec les tirets dans les noms ou les valeurs qui valident le HTML valide, le problème avec votre source est qu’ils utilisent javascript sur le client pour rendre le html, pour vérifier que vous pouvez télécharger la page html sont à la recherche n'existent pas.

Pour analyser les pages dans lesquelles JavaScript doit d'abord être exécuté, vous pouvez utiliser un contrôle de navigateur Web, puis transmettre le code HTML à HAP.

Voici un exemple simple d'utilisation du contrôle de navigateur Web WinForms:

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

Vous pouvez également consulter Awesomium et d’autres contrôles WebBrowser intégrés.

En outre, si vous souhaitez exécuter l'application WebBrowser dans la console, voici un exemple. Si vous ne l'utilisez pas, utilisez cet outil à l'aide de cette réponse SO Contrôle WebBrowser dans un nouveau fil

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


Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi