Почему EvaluateScriptAsync возвращает пустой объект расширения в CefSharp

Я пытаюсь написать приложение на С#, используя CefSharp. Мое намерение состоит в том, чтобы получить все ссылки на данной странице, например,

https://wixlabs---dropbox-folder.appspot.com/index?instance=lp5CbqBbK6JUFzCW2hXENEgT4Jn0Q-U1-lIAgEbjeio.eyJpbnN0YW5jZUlkIjoiYjNiNzk5YjktNjE5MS00ZDM0LTg3ZGQtYjY2MzI1NWEwMDNhIiwiYXBwRGVmSWQiOiIxNDkyNDg2NC01NmQ1LWI5NGItMDYwZi1jZDU3YmQxNmNjMjYiLCJzaWduRGF0ZSI6IjIwMTgtMDEtMjJUMTg6Mzk6MjkuNjAwWiIsInVpZCI6bnVsbCwidmVuZG9yUHJvZHVjdElkIjpudWxsLCJkZW1vTW9kZSI6ZmFsc2V9&target=_top&width=728&compId=comp-j6bjhny1&viewMode=viewer-seo

Когда я загружаю страницу, открываю инструменты разработчика и выполняю

document.getElementsByTagName('a');

в инструментах разработчика я получаю 374 результата. Затем я выполняю следующий код из BrowserLoadingStateChanged:

private async Task ProcessLinksAsync()
        {
            var frame = browser.GetMainFrame();
            var response = await frame.EvaluateScriptAsync("(function() { return document.getElementsByTagName('a'); })();", null);
            ExpandoObject result = response.Result as ExpandoObject;

            Console.WriteLine("Result:" + result);//What do I do here?
        }

Я получаю объект Expando, который, кажется, ничего не содержит. Я говорю это, потому что я использовал точку останова и проверил объект. Я прошел через https://keyholesoftware.com/2019/02/11/create-your-own-web-bots-in-net-with-cefsharp/ , https://github.com/cefsharp/CefSharp/wiki/General-Usage#javascript-integration и вопросы по SO но не смог решить мою проблему. Я делаю что-то не так здесь? Мое фактическое намерение состоит в том, чтобы получить ссылки, а затем перейти к ним. Заранее спасибо.

РЕДАКТИРОВАТЬ: я использовал следующий скрипт в инструментах браузера и разработчика, оба возвращают 187 результатов, что является правильным.

(function() { 
    var links=document.getElementsByClassName('file-link'); 
    var linksArray = new Array(); 
    for (var i = 0; i < links.length; i++) { 
        linksArray[i] = String(links[i].href); 
    } 
    return linksArray; 
})(); 

Но в моем приложении я получаю массив нулевой длины.

РЕДАКТИРОВАТЬ-2: я использовал следующий код для получения DOM: -

public void OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
        {
            ContextCreated?.Invoke(this, frame);
            const string script = "document.addEventListener('DOMContentLoaded', function(){ alert(document.links.length); });";

            frame.ExecuteJavaScriptAsync(script);
        }

Для каждого другого сайта, который я пробовал, код был успешным, за исключением URL-адреса, упомянутого выше. Может ли кто-нибудь сказать мне, что может быть не так, поскольку DOM загружается в инструменты разработки и полностью доступен. Итак, я думаю, что-то может отсутствовать в моем коде. Спасибо еще раз.


person Pankaj    schedule 31.08.2019    source источник
comment
Каков тип возврата getElementsByTagName? Является ли тип массивом? Я спрашиваю об этом, зная ответ на оба вопроса, я думаю, важно, чтобы вы исследовали ответы, если вы еще этого не знаете.   -  person amaitland    schedule 01.09.2019
comment
Пожалуйста, простите мое невежество. Я в основном разработчик настольных компьютеров, плохо разбираюсь в веб-технологиях. Но этот проект требует от меня использования веб-технологий. Поэтому мне было интересно, не могли бы вы немного направить меня. Пожалуйста, смотрите редактирование выше. У меня такое чувство, что я делаю что-то не так, но я совершенно уверен, где. Спасибо еще раз.   -  person Pankaj    schedule 01.09.2019
comment
Я только что узнал, что предполагаемая страница не загружается. Я только что нашел этот Gist, который кажется актуальным.   -  person Pankaj    schedule 01.09.2019
comment
Поведение выглядит немного странно. Я инициализирую браузер URL-адресом и получаю сообщение о том, что страница загружена. Затем я делаю browser.Load() и получаю ранее загруженную страницу, которая, вероятно, не была загружена в этот момент. Есть ли что-то особенное, что мне нужно сделать?   -  person Pankaj    schedule 01.09.2019
comment
Я попробовал код по ссылке выше, но не смог найти NavStateChangedEventArgs и browser.NavStateChanged.   -  person Pankaj    schedule 01.09.2019
comment
Я думаю, что есть проблема с доступом к DOM, потому что когда я выполняю 2+2, я получаю 4, но когда я пытаюсь получить доступ к DOM, я ничего не получаю. Есть ли какие-то настройки, которые мне нужно установить?   -  person Pankaj    schedule 02.09.2019


Ответы (1)


Вам нужно дождаться загрузки страницы. Также, если страница загружает данные с помощью ajax, вам нужно немного подождать, чтобы данные также загрузились. Затем вам нужно преобразовать результат в пользовательский объект javascript.

ChromiumWebBrowser browser;
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    browser = new ChromiumWebBrowser(
        "https://google.com/"); // Tried with your URL. 
    browser.LoadingStateChanged += Browser_LoadingStateChanged;
    browser.Dock = DockStyle.Fill;
    Controls.Add(browser);
}

private async void Browser_LoadingStateChanged(object sender,
    LoadingStateChangedEventArgs e)
{
    if (!e.IsLoading)
    {
        await Task.Delay(5000); //Just for pages which use ajax loading data
        var script = @"
        (function () {
            var data = document.getElementsByTagName('a');
            return Array.from(data, a => ({href:a.href, innerText:a.innerText}));
        })();";
        var result = await browser.EvaluateScriptAsync(script);
        var data = (IEnumerable<dynamic>)result.Result;

        MessageBox.Show(string.Join("\n", data.Select(x=>$"{x.href}").Distinct()));
    }
}
person Reza Aghaei    schedule 08.12.2019
comment
Просто чтобы добавить немного больше деталей, CEF поддерживает возвращаемые массивы, вызов getElementsByTagName возвращает HTMLCollection, который похож на массив, а не на массив, строго говоря, он динамически обновляется по мере изменения DOM. Вы не можете вернуть HTMLCollection напрямую, HTMLCollection необходимо превратить в массив, как показано в этом ответе. Для справки developer.mozilla.org/en-US/docs/ Веб/API/Элемент/ - person amaitland; 10.12.2019
comment
Я также добавлю, что использование задержки фиксированной длины не обязательно является лучшим вариантом, использование MutationObserver в сочетании с вызовом CefSharp.PostMessage из JavaScript может использоваться там, где требуется более точное время. Для справки developer.mozilla.org/en-US/docs/Web/ API/MutationObserver и github.com/cefsharp/CefSharp/issues/2775 - person amaitland; 10.12.2019