【问题标题】:HTMLAgility Pack: Screen Scraping Unable to Find a Div with Hyphen in Class Name?HTMLAgility Pack:屏幕抓取无法在类名中找到带有连字符的 Div?
【发布时间】:2015-03-27 13:49:35
【问题描述】:

这是一种学习练习,但也很“有趣”。基本上,我试图在 C# 控制台应用程序中解析“阳台”国家房间的价格(目前为 1039 美元)。网址是:

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

我已经很好地加载了上面的网址:

var document = getHtmlWeb.Load(web_address);

Balcony 价格的容器是一个类为“col”的 div,是 column-container clearfix 类中的第三个 div。我想我所需要的就是用每个类对所有 div 进行罚款:

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

然后选择第 3 个节点以获取阳台价格。但是最低价格变量一直返回空值。我知道文档本身已加载,如果我选择“col”,我可以看到“col”内部。是不是 col-bottom 中的连字符阻止了该 div 的查找?

还有其他方法可以解决这个问题吗?正如我所说,这主要是一种学习练习。但是我必须创建一些需要屏幕抓取的自定义监控解决方案,所以这不仅仅是有趣的。

谢谢!

EDIT HTML sn-p 包含相关信息:

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

【问题讨论】:

  • 你能不能只提取 HTML 的 sn-p 并将其包含在问题中,而不是指向将在一个月内失效的链接?
  • 好的。完毕。其他 div 中的信息过多。
  • 可以这样:stackoverflow.com/questions/5139564 吗?解析中的某些内容可能会以某种方式重命名属性。你可以通过加载 HTML 来确定,然后编写代码来遍历它,看看 Load() 是否以其他方式改变了破折号。
  • 谢谢。会调查它;只是没有意义,因为加载与在浏览器中加载相同?如果是这种情况,那么所有带有破折号的类/ID 在所有应用程序中都会受到影响?

标签: c# screen-scraping html-agility-pack


【解决方案1】:

属性名称或有效 html 值中的连字符没有任何问题,您的源代码的问题是他们在客户端使用 javascript 来呈现 html,以验证您是否可以下载 html 页面,您会注意到您要查找的元素不存在。

要解析需要首先执行 javascript 的此类页面,您可以使用 Web 浏览器控件,然后将 html 传递给 HAP。

下面是一个简单的例子,说明如何使用 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);
    }

您还可以查看Awesomium 和其他一些嵌入式 WebBrowser 控件。

另外,如果您想在控制台应用程序中运行 WebBrowser,这里有一个示例,如果您没有使用 Windows 表单,则此示例借助此 SO 答案 WebBrowser Control in a new thread

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

【讨论】:

  • 是的,你可能是对的!但什么是 HAP?以及开始使用这种方法的任何相关链接?谢谢。
  • @Meengla Html 敏捷包!
  • 哦,我已经在使用它了。好的,那么通过“网络浏览器控件”加载?我正在使用 var getHtmlWeb = new HtmlWeb(); ;不对吗?
  • @Meengla 这不会执行 javascript ,它只会在服务器呈现它时加载初始 html,给我几分钟我会写一个例子
  • 谢谢!我将不得不离开计算机,但稍后会回来查看。
【解决方案2】:

@Decoherence 的答案没有起作用——从上面的聊天中可以看出。基本上,使用他的代码,我仍然以col-bottomnull。所以我最终使用了以下网址:http://www.icruise.com/8-night-southern-caribbean-cruise_carnival-sunshine_8-8-2015.html?refPage=src

并且能够很好地解析它。虽然我可以稍后将解析问题中的 url 作为学习练习/挑战。 也欢迎其他人/免费接受挑战!

仅供参考。

【讨论】:

    猜你喜欢
    • 2014-10-12
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多