Применение функции Text.BetweenDelimiters()
в коде Power BI/Power Query для извлечения
адреса URL на веб-странице
Текст представляет собой адаптированный перевод статьи Chris Webb (Крис Уэбб),
оригинал – Using Text.BetweenDelimiters() To Extract URLs From A Web Page In Power BI/Power Query M
Крис Вебб (Chris Webb) — независимый эксперт, консультант по технологиям Analysis Services, MDX, Power Pivot, DAX, Power Query и Power BI. Его блог — это кладезь информации на тему перечисленных технологий. Вот уже более 10 лет он пишет про BI-решения от Microsoft. Количество его статей перевалило за 1000! Также Крис выступает на большом количестве различных конференций вроде SQLBits, PASS Summit, PASS BA Conference, SQL Saturdays и участвует в различных сообществах.
Крис любезно разрешил нам переводить его статьи на русский язык. И это одна из них.

Применение функции Text.BetweenDelimiters() в коде Power BI/Power Query для извлечения адреса URL на веб странице

В апреле 2017 вышел очередной релиз Power BI Desktop в котором добавился функционал Add Column By Example. Тестируя его несколько недель, мы заметили, что для его работы были добавлены три новых функции:

Text.BetweenDelimiters() – принимает текст и извлекает часть, находящуюся между указанными разделителям.
Text.BeforeDelimiter() – принимает текст и возвращает часть, стоящую до указанного разделителя.
Text.AfterDelimiter() – принимает текст и возвращает часть, стоящую после разделителя.
Сами функции вполне понятны. К тому же в онлайн документации имеются хорошие примеры их использования. Возьмем, для примера, следующий код:

Text.BetweenDelimiters("Hello *world!??", "*", "!")
…он вернет текст "world":
Недавно был задан вопрос: "Можно ли с помощью языка М извлечь все ссылки из атрибутов href в коде веб-страницы. Используя функцию Text.BetweenDelimiters(), это сделать несложно. Для этого напишем следующую функцию:

(SourceURL as text, AttributeDelimiter as text) =>
let
    //Get HTML source
    Source = Text.FromBinary(Web.Contents(SourceURL)),
    //Function to find each link
    GetLink = (Counter as number) =>
                    let
                        CurrentLink = 
        Text.BetweenDelimiters(
            Source, 
            "href=" & AttributeDelimiter, 
            AttributeDelimiter,
            Counter
        )
                    in
                        if CurrentLink="" 
        then 
            {} 
        else 
            List.Combine({
                {CurrentLink}, 
                @GetLink(Counter+1)}
            ),
    //Call function
    Output = GetLink(0)
in
    Output
Отметим несколько вещей:

  • Здесь используется комбинация функций Text.FromBinary() и Web.Contents() для получения HTML кода страницы, с которой мы извлекаем ссылки.
  • Так как HTML позволяет применять для атрибутов как одинарные, так и двойные кавычки, был добавлен параметр AttributeDelimiter, определяющий, какие именно использованы.
  • Функция Text.BetweenDelimiters извлекает за один раз только один участок текста. Но можно указать, какое вхождение разделителей обрабатывать. В данном коде применяется рекурсия для извлечения содержимого каждого атрибута href в коде HTML. Объявляется функция GetLink, из которой происходит рекурсивный вызов. Для этого ставим знак @ перед именем функции. Возможно, здесь лучше использовать функцию List.Generate().
Назовём этот запрос GetAllLinks
…его можно использовать в другом запросе:

GetAllLinks(
    "https://msdn.microsoft.com/en-us/library/mt798303.aspx", 
    """"
)
Отметим один момент. Чтобы передать функции двойные кавычки как текст, их нужно заключить в двойные кавычки, получится четыре кавычки подряд: """"

На выходе получим список всех ссылок страницы из атрибутов href, заключенных в двойные кавычки:
Нашу функцию можно улучшить, чтобы она обходила несколько страниц и собирала ссылки с них, а потом использовать инструмент в Excel Force Directed Graph или даже NodeXL для визуализации связей страничек между собой. Но это мы оставим вам.