Skip to content

cassiofariasmachado/webscraping-with-fsharp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 

Repository files navigation

Primeiros passos com F# e Web Scraping

Introdução

Sempre fiquei muito curioso quando ouvia sobre as linguagens funcionais e as vantagens que elas trazem ao desenvolvimento. A promessa de dar adeus ao null e outros erros de runtime que tanto nos incomodam, faz brilhar meus olhos até hoje. Então, como desenvolvedor .NET, decidi começar no mundo funcional através do F#.

Já fazia um tempo que gostaria de estudar melhor a linguagem e escrever um artigo sobre, assim resolvi começar pela prática estudando como utiliza-lá para aplicar Web Scraping. Para quem desconhece o termo, Web Scraping nada mais é do que um método de coletar dados de páginas web e F# é uma ferramenta muito poderosa para isso.

Formas de usar

Existem basicamente 3 formas de se aplicar Web Scraping com F#:

  1. Utilizar alguma biblioteca feita para isso em C#
  2. Utilizar um wrapper de Selenium para F#
  3. Ou então, utilizar a biblioteca FSharp.Data

Essa última é a que irei abordar nessa artigo, trata-se de uma biblioteca que permite trabalhar mais facilmente com os formatos CSV, XML, JSON e até, não se surpreenda, HTML.

Ela também fornece helpers para realizar requisições HTTP, conversão para os tipos já mencionados e acesso ao WorldBank, mas isso não será abordado nesse artigo.

HtmlProvider

Através do HtmlProvider é possível definir um tipo para a página que você deseja fazer o scraping. Ele espera receber um HTML de exemplo que pode ser um arquivo ou uma URL, e vai servir de base para a criação do tipo F#, dessa forma:

type DilbertSearch = HtmlProvider<"https://www.pinterest.pt/search/pins/?q=dilbert%20comic%20strip">

Assim é possível, por exemplo, pegar as primeiras imagens disponíveis da pesquisa por "dilbert comic strip" no Pinterest:

DilbertSearch().Html.CssSelect(".mainContainer img")
    |> List.map (fun d -> getUrlOfLargestImage(d.AttributeValue("srcset")))
    |> List.iter (printfn "%s")

// → https://i.pinimg.com/736x/90/38/bb/9038bbcabd5b31d6faa6705230df3a78--peanuts-comics-peanuts-gang.jpg
// → https://i.pinimg.com/736x/b4/f5/ba/b4f5bac902a421a8b2eb00f232a227e4--human-resources-online-comics.jpg
// ...

Os tipos gerados a partir do HtmlProvider indentificam automaticamente as tabelas e listas (literalmente <table>, <ul> ou <ol>) encontradas na página HTML, assim é possível, por exemplo, pegar a lista de personagens da tirinha do Hagar em sua página no Wikipedia:

type HagarWiki = HtmlProvider<"https://en.wikipedia.org/wiki/H%C3%A4gar_the_Horrible">

HagarWiki().Lists.``Cast of characters``.Values
    |> List.ofArray
    |> List.map getCharacterName
    |> List.iter (printf "%s\n")

// → Hägar the Horrible
// → Helga
// ...

O nome dado a lista ou tabela identificada é retirado dos atributos/tags HTML id, title, name, summary ou caption, se nenhum deles é encontrado então o nome dado será TableXX ou ListXX, em que o XX é um número sequencial de onde o elemento foi encontrado na página.

Mas o melhor disso é que essas tabelas e listas ficam disponíveis em tempo de desenvolvimento através do IntelliSense do Visual Studio ou Visual Studio Code:

Exemplo IntelliSense com personagens do Hagar

Nesse outro exemplo, é possível buscar os filmes da franquia Star Wars e a sua arrecadação em dólares:

type StarWarsWiki = HtmlProvider<"https://en.wikipedia.org/wiki/List_of_Star_Wars_films_and_television_series">

let filmsByRevenue = StarWarsWiki().Tables.``Box office performance``.Rows
                        |> Seq.filter (fun r -> isReleaseDate r.``Release date``)
                        |> Seq.sortBy (fun x -> convertReleaseDate x.``Release date``)
                        |> Seq.map (fun r -> r.Film, convertRevenue r.``Box office revenue - Worldwide``)
                        |> Seq.toArray

filmsByRevenue
    |> Seq.iter (fun elem -> elem ||> printf "%s - %f Billions \n")

// → Star Wars - 0.775398 Billions
// → The Empire Strikes Back - 0.547969 Billions
// ...

E então, é possível também plotar um gráfico utilizando a biblioteca FSharp.Charting, assim:

Chart.Column filmsByRevenue
    |> Chart.WithYAxis(Title = "Billions")
    |> Chart.WithXAxis(Title = "Films")
    |> Chart.Show

Gráfico da arrecadação dos filmes da franquia Star Wars

Repositório com os exemplos

Os exemplos utilizados no artigo estão disponíveis nesse repositório do GitHub.

Nele existem dois projetos de exemplo um utilizando .NET Framework e outro utilizando o .NET Core. As únicas diferenças entre eles, além da versão do .NET, é que nesse último:

  • A biblioteca FSharp.Data só é compatível com o .NET Core, se utilizada a partir da sua versão 3.0.0-beta que está em beta
  • E a biblioteca FSharp.Charting foi removida, pois tem dependência do framework

Conclusão

Assim, a biblioteca Fsharp.Data torna o F# uma ferramenta muito poderosa para fazer scraping de páginas web. Entretando, nem tudo são flores e se a página possui conteúdo muito dinâmico (utilização de javascript para renderização), existem dificuldades de se utilizar a biblioteca, mas que podem ser contornadas utilizando-a em conjunto da segunda opção apresentada no início do artigo, um wrapper de Selenium para F#.

Enfim, a primeira impressão com a linguagem e com o paradigma foi muito positiva, trata-se de uma forma diferente de desenvolvimento que torna o código muito mais claro, mas que necessita de certo aprofundamento teorico, pois exige uma mudança de mindset para quem esta acostumado com o mundo orientado a objetos. Como próximo passo, devo continuar me aprofundando na linguagem pra quem sabe trazer mais algum artigo sobre o assunto.

Referências

Seguem algumas referências utilizadas:

About

Artigo sobre web scraping com F#

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages