Existem situações onde precisamos coletar informações de uma página web e essa prática é chamada de web scraping. Geralmente esse processo é simples e, provavelmente, não se faz necessário executá-lo o tempo todo.
Atualmente temos à nossa disposição a arquitetura serverless, onde conseguimos hospedar funções sem preocupação com infraestrutura. Esse modelo de arquitetura torna bastante agradável o desenvolvimento de funcionalidades que serão executadas sob demanda, que é o caso padrão de um web scraping.
No ecossistema Node.JS, o Puppeteer é uma biblioteca bastante utilizada para a implementação de web scrapings. Para ser mais honesto, essa biblioteca é bem mais poderosa e possui muito mais funcionalidades do que simplesmente coletar informações de web sites. O Puppeteer é capaz de gerar screenshots e PDFs de páginas web, automatizar o envio de formulários e fazer testes de UI. E o que o torna tão poderoso é a sua API de alto nível capaz de controlar instâncias do Chrome ou Chromium sobre o protocolo DevTools.
Bom... agora que já mencionei sobre a arquitetura serverless e o Puppeteer, podemos meter a mão no código e escrever uma função lambda que irá acessar https://stackoverflow.com/jobs e capturar as informações das vagas de emprego e retorná-las em formato json.
Para esse exemplo iremos utilizar Serverless Framework, provider AWS Lambda com runtime Node.JS utilizando Typescript e, obviamente, o Puppeteer. Estou assumindo que você já tem o serverless instalado, bem como as credenciais da AWS configuradas na sua máquina.
Eu não gosto muito de utilizar templates, pois geralmente trazem um monte de coisas não tão necessárias assim, principalmente para esse projeto em questão. Então, eu começo criando toda lambda function com os seguintes passos:
- mkdir puppeteer-lambda
- cd puppeteer-lambda
- yarn init -y (ou npm init -y)
- criar arquivo serverless.yml
- criar arquivo app.ts
serverless.yml
Dentro do arquivo serverless.yml precisamos fornecer as informações do nosso serverless.
O service recebe o nome que queremos dar para a nossa lambda function.
O provider é basicamente a infra onde a lambda irá executar, nesse caso é a AWS, como podemos ver na propriedade name. A propriedade region recebe o valor da região da AWS que você irá executar seu código, em layers temos um arn para o pacote chrome-aws-lambda, as outras propriedades armazenam informações sobre o ambiente de execução, tamanho de memória RAM e timeout.
Os plugins são ferramentas facilitadoras e nesse caso temos dois: serverless-plugin-typescript que configura todo TS no nosso projeto e serverless-offline que irá facilitar a execução local do projeto.
Na parte de functions temos a nossa função, que pode receber qualquer nome, mas no handler deve apontar para o path do arquivo e função que irá executar. É importante notar a parte de events, que nada mais é do que o evento que irá disparar a chamada para a a nossa função, e nesse caso é um evento http com uma requisição GET para o endpoint /scraping.
app.ts
No nosso app.ts iremos escrever o código que irá executar quando a nossa função for chamada.
Nesse momento, o caminho natural ao desenvolver um projeto com Node.JS seria instalar o puppeteer no projeto, utilizando o yarn add puppeteer (ou npm install puppeteer). Porém quando estamos falando de funções lambda temos algumas limitações, e uma delas é o tamanho da aplicação, que deve ser no máximo 50MB.
Quando instalamos o Puppeteer na aplicação, ele instala também uma versão do Chrome que é utilizado para manipular o conteúdo das páginas web, sendo assim o limite de 50MB é facilmente ultrapassado, já que a versão do chrome que é instalada com o Puppeteer chega a um pouco mais de 200MB de tamanho. Mas para resolver esse problema podemos utilizar o pacote chrome-aws-lambda, que irá fornecer todo o ferramental para podermos trabalhar com o puppeteer sem que o nosso projeto fique inflado a ponto de ultrapassar o limite de 50MB. Nesse momento iremos instalar também um pacote chamado puppeteer-core que contém o puppeteer mas sem a instância do chrome embutida.
Depois de instalar os pacotes chrome-aws-lambda e puppeteer-core, podemos importar no código da nossa função. Vou explicar detalhadamente cada trecho de código da função abaixo:
-
Na linha 12 uma instância do Chrome é inicializada passando alguns parâmetros, e temos que destacar dois deles:
- executablePath: o caminho da instância do chrome instalada no projeto
- headless: que recebe true para que o navegador não seja inicializado com recursos visuais, somente a execução do processo.
Em seguida inicializamos uma nova página. Mas aqui, tenho uma dica legal pra passar. A maioria dos códigos de exemplos que encontramos na internet exibe o código await browser.newPage(), fazendo com que uma nova aba seja aberta nonavegador. Mas se pararmos para pensar quando o browser foi iniciado ele já abriu uma página, então nós só precisamos pegar ela utilizando (await browser.pages())[0]. De todo modo, precisamos acessar essa page para navegar até uma URL, que nesse caso está declarada em uma constante na linha 10.
O objeto page nos fornece o acesso à função .evaludate(), onde conseguimos utilizar javascript para acessar os elementos da página e extrair as informações. Essa função retorna uma promessa de um tipo genérico, então você pode estruturar as informações de retorno como quiser. No nosso caso, estamos retornando um array do tipo Job.
Após retornar o nosso conteúdo podemos então fechar a nossa instância do Chrome, ou caso você queira fazer ainda mais procedimentos, pode utilizar o await page.close() para fechar uma página que não irá utilizar mais.
Agora que entendemos o que está no serverless.yml e app.ts, podemos executar a nossa função. E agora tenho outra dica: quando estamos trabalhando com o chrome-aws-lambda localmente ele não tem acesso à uma instância do chrome para trabalhar, então precisamos instalar o puppeteer como dependência de desenvolvimento utilizando o comando yarn add puppeteer -D (ou npm install puppeteer -D). Internamente o chrome-aws-lambda se resolve e consegue encontrar a instância de acordo com o environment.
Então, para que não fiquem dúvidas em relação aos pacotes instalados, temos o seguinte packge.json:
OBS: lembrando que todos plugin declarado no serverless.yml deve ser instalado na aplicação também, e nesse caso estamos utilizando como dependências de desenvolvimento.
Para executar a aplicação basta utilizar o comando serverless offline e para fazer o deploy basta executar serverless deploy e ele irá subir o código para a nuvem do provedor e na região declarada.
Ao executar o comando serverless offline o que esperamos de retorno é algo parecido com essa imagem:
Podemos perceber uma URL GET exatamente com o endpoint que configuramos no serverless.yml, basta fazer uma solicitação utilizando postman, insomnia ou até mesmo no próprio browser e poderemos ver o retorno em formato JSON.
Bom... acho que é isso! :)
Na próxima publicação estou querendo trazer algo mais elaborado mostrando um pouco sobre a configuração de um schedule que irá disparar a execução da função, detalhando um pouco mais sobre os recursos da AWS.
Top comments (0)