-2

Не могу решить одну задачу

Есть текст: ...

Время 01:45 Страна JPY Факт.Базовые заказы в машиностроении (г/г) (апр) Прогноз 9,6% Пред. 3,9% Факт -2,4% Время 02:50 Страна JPY Факт.Базовые заказы в машиностроении (м/м) (апр) Прогноз 10,1% Пред. 2,8% Факт -3,9% Время 02:50 Страна JPY Факт.Денежный агрегат М2 (г/г) Прогноз 3,2% Пред. 3,3% Факт 3,2% Время 02:50 Страна GBP Факт.Индекс GBP/USD Прогноз 57,3% Пред.   Факт 57,1% Время 08:30 Страна USD Факт.Индекс золота Прогноз 64,2% Пред.   Факт 68,5% Время 08:30 Страна USD Факт.Индекс S&P 500 Прогноз 68,1% Пред.   Факт 55,5%

...

Перебираю текст построчно:

            String[] s = textBox1.Text.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
        int w = Convert.ToInt32(col_vo);
        for (int i = 0; i < w; i++)
        {
            label3.Text += s[i] + "\r\n";

        }

И вот в чем я не могу разобраться. Как в цикле выбирать например: В строчке Время 01:45 Страна JPY Факт.Базовые заказы в машиностроении (г/г) (апр) Прогноз 9,6% Пред. 3,9% Факт -2,4%

Только: 1:45 JPY Базовые заказы в машиностроении (г/г) (апр)

Если бы текст был статичным было бы проще, но он всегда меняется. Мысли есть, что можно по слову Время, найти время. Но не хватает знаний.
Подскажите какой метод использовать ?

Добавляю исходный файл: Яндекс Диск

  • Что у вас между Время 01:45, Страна JPY и другими полями? При редактировании вопроса видно, что там либо несколько пробелов либо TAB, и вообще очень похоже на CSV – rdorn Jun 16 '18 at 22:55
  • Вы издеваетесь? Я вам уже дал полный ответ того, как получать с того сайта все данные, вам осталось бы по аналогии сделать также и вытянуть все <td>. Вы не хотите слушать, либо это ваши два аккаунта сыграли злую шутку и вы не увидели тот ответ. Сейчас вы делаете полнейший бред! – EvgeniyZ Jun 16 '18 at 22:57
  • @EvgeniyZ Причем здесь тот вопрос ? Я уже давно решил ту проблему, спасибо Вам за тот ответ. Этот вопрос не как не относится к тому. Я извиняюсь если принес Вам какие либо не удобства. – Егор Глухов Jun 16 '18 at 23:02
  • @rdorn Между Время и 01:45 есть 1 пробел. А между Время 01:45 и Страна JPY 3 пробела. – Егор Глухов Jun 16 '18 at 23:05
  • @ЕгорГлухов Ну как он не относится, если у вас те же данные, что и в том вопросе? Только не правильно полученные... Хорошо, пойдём по другому, от куда у вас этот набор текста? – EvgeniyZ Jun 16 '18 at 23:09
  • @EvgeniyZ Думаю проще полностью объяснить. – Егор Глухов Jun 16 '18 at 23:11
  • 1
    @ЕгорГлухов Да, было бы. Пока я вижу только то, что вы пытаетесь спарсить опять тот сайт, выдираете из него не html, а текст и с этим текстом пытаетесь возиться. – EvgeniyZ Jun 16 '18 at 23:13
  • @EvgeniyZ Думаю проще полностью объяснить. Задался целью создать приложение которое выводить мне информацию онлайн. Первая проблема которая возникла, была решена очень большими усилиями это парсинг. После парсинга информации, которая кстати правильно получена, я записываю ее в string. Потом я использовал многопоточность, к счастью про это очень много книг, и я смог в это разобраться. После того как информация получена. Работает другой класс. Который отвечает за то. Что бы создавать пользовательский интерфейс, с нужной информацией. – Егор Глухов Jun 16 '18 at 23:21
  • @ЕгорГлухов После парсинга информации, которая кстати правильно получена, я записываю ее в string - либо не правильно сохраняете, либо вовсе неверно парсите. Текст из вопроса почти не реально нормально прочесть, ибо у него нету четкой структуры (где 1 пробел, где 2, а где то и вовсе 3). Вот вам к примеру разбить я пытался по 3-ем пробелам - результат. Тут либо много костылей изобретать, либо как то регулярками проходиться (и то и то не очень хорошо). – EvgeniyZ Jun 16 '18 at 23:26
  • @EvgeniyZ так вот. В цикле я перебираю по строчно каждую ставку. Как только выбрана страка, Я нужно получить время, страну и т.д. из это строчки и записать её в переменную. После того как было все записано. Я создаю новую копию пользовательского интерфейса, в котором указываю, Позицию XY, дату, время, цвет и все все. Что в Form, появилась не обычный тест. А например каждый отдельный Пользовательский интерфейс, нарисованный на том же фотошопе. Тем самым что бы было все комфортно. НО так как я не могуполучить инфу, я нем могу создать Пользовательский интерфейс.Если у Вас есть свои идеи.... – Егор Глухов Jun 16 '18 at 23:28
  • @EvgeniyZ Давайте Я просто отправлю вам свой проект. ? И вы поймете о чем я говорю. ПАРСИНГ работает ! – Егор Глухов Jun 16 '18 at 23:29
  • 1
    А зачем вы после парсинга в стринг то сохраняете? Заведите класс с соответствующими полями и заполняйте поля результатами парсинга. В строку собрать всегда успеете. – rdorn Jun 16 '18 at 23:33
  • 1
    @ЕгорГлухов У меня нету сейчас студии, но вы все, что относится к вопросу добавьте пожалуйста в вопрос, что бы не только я понимал ваш замысел, а и другие пользователи тоже! – EvgeniyZ Jun 16 '18 at 23:34
  • @rdorn Можете более подробно объяснить ? – Егор Глухов Jun 16 '18 at 23:44
  • @ЕгорГлухов Файл Parser.cs, строка 69 _Text += "Время " + row[0] + - вот все это переделывайте, делайте класс с определёнными полями/свойствами, делайте коллекцию этого класса и в неё все заносите. Сейчас вы делаете совершенно нечитаемые данные. Также, я вам уже сотню раз говорил - вы все прибили гвоздями //tr/td[5]/text(), ну нельзя так, нельзя! Объектов может быть больше 7 или нужный объект может быть смещён на 3 место к примеру, что тогда? – EvgeniyZ Jun 16 '18 at 23:59
  • @EvgeniyZ Спасибо. Сейчас начну. Я учту про "гвозди" – Егор Глухов Jun 17 '18 at 00:14

1 Answers1

1

Все началось, как я понял, с этого вопроса: Индекс за пределами диапазона C# XPath. Потом продолжилось здесь, а результатом должен был быть банальный парсинг сайта. Проблема эта не нова. Если поискать по SO, то можно найти кучу вопросов и ответов.

Только ради того, чтоб закончить эпопею с этим сайтом, решил помочь. Таблица на этом сайте строится динамически с помощью javascript. Потому либо нужено использовать что-то типа Selenium, либо, если не охота париться, просто открываем сайт в браузере, а потом сохраняем страницу в файл html. А дальше работаем с файлом пример работы

Создадим пару классов для хранения этой информации

public class EconomicCalendar
{
    public string Title { get; set; }
    public List<CalendarRow> Rows { get; } = new List<CalendarRow>();
}

public class CalendarRow
{
    public string ID { get; set; }
    public string EventAttrId { get; set; }
    public string DataEventDatetime { get; set; }
    public string FirstLeftTile { get; set; }
    public string CountryAbbr { get; set; }
    public string CountryRus { get; set; }
    public string CountryEng { get; set; }
    public string Forecast { get; set; }
    public string Previous { get; set; }
    public string Alert { get; set; }
    public string Volatility { get; set; }
}

Чтобы преобразовать HTML в XML установим такую библ. отображение название нужной библиотеки

Напишем такой подсобный класс

public class HtmlToXmlService
{
    /// <summary>
    /// Получение экземпляра XDocument на основе html файла
    /// </summary>
    /// <param name="pathToFile">путь к файлу html</param>
    /// <returns>экземпляр XDocument</returns>
    public static XDocument GetXDocument(string pathToFile)
    {
        if (String.IsNullOrEmpty(pathToFile)) throw new ArgumentNullException(nameof(pathToFile));
        if (!File.Exists(pathToFile)) throw new ArgumentException("Файл не найден!");

        //читаем файл и получаем документ
        XDocument result = null;
        using (StreamReader sr = File.OpenText(pathToFile))
        {
            result = FromHtml(sr);
        }

        //удаление пространства имен
        RemoveNamespaces(result);

        return result;
    }

    private static void RemoveNamespaces(XDocument result)
    {
        //удаление namespaces у элементов
        result.Descendants()
              .Attributes()
              .Where(x => x.IsNamespaceDeclaration)
              .Remove();

        foreach (var elem in result.Descendants())
            elem.Name = elem.Name.LocalName;

        //удаление namespaces у атрибутов элементов
        foreach (var attr in result.Descendants().Attributes())
        {
            var elem = attr.Parent;
            attr.Remove();
            elem.Add(new XAttribute(attr.Name.LocalName, attr.Value));
        }
    }

    private static XDocument FromHtml(TextReader reader)
    {
        // setup SgmlReader
        SgmlReader sgmlReader = new SgmlReader();
        sgmlReader.DocType = "HTML";
        sgmlReader.WhitespaceHandling = WhitespaceHandling.All;
        sgmlReader.CaseFolding = CaseFolding.ToLower;
        sgmlReader.InputStream = reader;

        return XDocument.Load(sgmlReader);
    }
}

В этом классе будем парсить xml с помощью LINQ (может не самым удобным образом..., критика приветствуется)

/// <summary>
/// Обработка страницы сайта полученной с адреса https://ru.investing.com/economic-calendar/
/// </summary>
public class EconomicCalendarService
{
    /// <summary>
    /// Парсинг файла HTML сохраненной страницы
    /// </summary>
    /// <param name="pathToHtmlFile">путь к файлу html</param>
    /// <returns>экземпляр EconomicCalendar </returns>
    public static Task<EconomicCalendar> GetEconomicCalendar(string pathToHtmlFile)
    {
        if (String.IsNullOrEmpty(pathToHtmlFile)) throw new ArgumentNullException(nameof(pathToHtmlFile));

        EconomicCalendar result = new EconomicCalendar();

        //получаем из Html файла -> XDocument
        XDocument document = HtmlToXmlService.GetXDocument(pathToHtmlFile);

        //название таблицы
        result.Title = document.Descendants("table")
                        .Where(table => table.Attributes("id").Any())
                        .Where(t => t.Attribute("id").Value.StartsWith("economicCalendarData"))
                        .Descendants("td")
                        .Where(td => td.Attributes("class").Any())
                        .Where(td => td.Attribute("class").Value.StartsWith("theDay"))
                        .First().Value;

        //извлекаем строки таблицы
        var crs = document.Root.Element("body")
                       .Descendants("tr")
                       .Where(tr => tr.Attributes("class").Any())
                       .Where(tr => tr.Attribute("class").Value.StartsWith("js-event-item"))
                       .Select(tr => new CalendarRow
                       {
                           ID = tr.Attribute("id")?.Value,
                           EventAttrId = tr.Attribute("event_attr_id")?.Value,
                           DataEventDatetime = tr.Attribute("data-event-datetime")?.Value,
                           FirstLeftTile = tr.Descendants("td")
                                   .FirstOrDefault(td => td.Attribute("class").Value.StartsWith("first left"))
                                   ?.Value,
                           CountryAbbr = tr.Descendants("td")
                                   .FirstOrDefault(td => td.Attribute("class").Value.StartsWith("left flagCur"))
                                   ?.Value,
                           CountryRus = tr.Descendants("td")
                                   .Where(td => td.Attribute("class").Value.StartsWith("left flagCur"))
                                   .Select(td => td?.Descendants().First()?.Attribute("title")?.Value)
                                   .FirstOrDefault(),
                           CountryEng = tr.Descendants("td")
                                   .Where(td => td.Attribute("class").Value.StartsWith("left flagCur"))
                                   .Select(td => td?.Descendants().First()?.Attribute("data-img_key")?.Value)
                                   .FirstOrDefault(),
                           Forecast = tr.Descendants("td")
                                   .FirstOrDefault(td => td.Attribute("class").Value.StartsWith("fore"))
                                   ?.Value,
                           Previous = tr.Descendants("td")
                                   .Where(td => td.Attribute("class").Value.StartsWith("prev"))
                                   .Select(td => td?.Descendants().First()?.Value)
                                   .FirstOrDefault(),
                           Alert = tr.Descendants("td")
                                   .Where(td => td.Attribute("class").Value.StartsWith("alert"))
                                   .Select(td => td.Attribute("data-name")?.Value)
                                   .FirstOrDefault(),
                           Volatility = tr.Descendants("td")
                                   .Where(td => td.Attribute("class").Value.StartsWith("left textNum"))
                                   .Select(td => td.Attribute("title")?.Value)
                                   .FirstOrDefault(),
                       });

        //вносим строки
        result.Rows.AddRange(crs);

        return Task.FromResult(result);
    }
}

Как-то так. Построение граф интерфейса банально, код приводить не буду. Только как пользоваться написанным выше классом

try
{
    EconomicCalendar calendar = await EconomicCalendarService.GetEconomicCalendar(File);

    Title = calendar.Title;
    Rows = calendar.Rows;
}
catch (Exception ex)
{
    _mainWindow.ShowMessage($"Возникла ошибка: {ex.Message}");
}

Пример можно скачать здесь

Bulson
  • 9,411