Extraindo endereços de email em páginas web

Introdução

Uma das coisas mais interessantes na linguagem Javascript é a versatilidade da mesma, oferecendo aos usuários múltiplas maneiras de executar uma mesma tarefa.

Nos exemplos a seguir, demostrarei várias maneiras de extrair endereços de email (links com mailto:) de uma página web, buscando ilustrar as diferenças entre cada uma:

Dados de Referência

O exemplos apresentados neste post foram testados em um documento HTML com o seguinte formato:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<title>Example Page</title>
<meta charset="utf-8">
</head>
<body>
<div>
<a href="mailto:user1@teste.com">user1@teste.com</a>
<a href="mailto:user2@teste.com">user2@teste.com</a>
<a href="mailto:user3@teste.com">user3@teste.com</a>
<a href="mailto:user4@teste.com">user4@teste.com</a>
<a href="mailto:user5@teste.com">user5@teste.com</a>
</div>
</body>
</html>

O corpo do documento foi criado utilizando a ferramenta Emmet, presente em várias IDE’s e editores de texto, abaixo a string utilizada:

1
div>a[href="mailto:user$@teste.com"]{user$@teste.com}*5000

Códigos Utilizados

ES 5

No primeiro exemplo usaremos ES 5 e funções tradicionais. Neste cenário, o código em questão é extremamente verboso, porém, totalmente legível e auto explicativo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function isEmail(el) {
return el.getAttribute('href').indexOf('mailto') >= 0;
}

function getEmailValue(el) {
return el.getAttribute('href').substring(7);
}

function listPageEmails() {
var links = Array.from(document.querySelectorAll('a'));
links = links.filter(isEmail);
links = links.map(getEmailValue);
return links.join('\n');
}

console.log(listPageEmails());

ES2015+

No exemplo seguinte, nosso código utilizará a versão 2015 do Ecma Script e suas famosas Arrow Functions para simplificar o código, ao custo da legibilidade:

1
2
3
4
5
6
7
8
9
10
11
let links = Array.from(
document.querySelectorAll('a'))
.filter((el) => {
return (el.getAttribute('href').indexOf('mailto') >= 0)
})
.map((el) => {
return el.getAttribute('href').substring(7)
})
.join('\n');

console.log(links);

Perceba o quanto o código se aproxima dos conceitos de programação funcional, essa é uma das vantagens que o ES2015 trouxe para a linguagem, oferecendo aos desenvolvedores técnicas antes apenas disponíveis a linguagens puramente funcionais.

ES2015+ (Linha Única)

O próximo exemplo é uma variação do código anterior, porém, condensando todas as instruções em uma única linha:

1
Array.from(document.querySelectorAll('a')).filter(el => (el.getAttribute('href').indexOf('mailto') >= 0)).map(el => el.getAttribute('href').substring(7)).join('\n');

ES2015 (Variação)

No último exemplo, temos uma nova variação da versão funcional, porém ainda mais otimizada, substituindo a método filter nativo do objeto Array por otimizações nos seletores CSS.

1
Array.from(document.querySelectorAll('a[href*="mailto"]')).map(el => el.getAttribute('href').substring(7)).join('\n');

Performance

A ferramenta jsperf foi utilizada para comparar a performance das duas abordagens apresentadas, operando sobre uma massa de dados com 10000 (dez mil) elementos.

Resultado das Operações

Observando os resultados, podemos determinar que o uso de CSS expressions é mais performático do que o método filter, em aproximadamente 10%, considerando o cenário apresentado, podendo este número ser modificado em condições distintas.

O benchmark utilizado está disponível no seguinte endereço:

Conclusão

Neste artigo, foquei em demonstrar as diferentes maneiras de executar uma mesma tarefa em Javascript, além de utilizar ferramentas para monitorar a performance de um determinado método, conhecimento útil em vários casos.

Referências