Skip to content

Menighin/cei-crawler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cei-crawler 💸

Travis badge Coveralls badge License: MIT

Crawler para ler dados do Canal Eletrônico do Investidor

Importante (Versão sem captcha)

Para versão antiga do CEI que não possui captcha obrigatório (por enquanto), utilize o cei-crawler v2

Descrição

Essa versão do crawler varre a Nova Area Logada do CEI. Essa área logada possui um captcha para que seja feito o login e por isso existem algumas estratégias de implementação para fazer o bypass do mesmo. Além disso, o CEI agora possui uma API. Tudo que o crawler faz basicamente é encapsular as chamadas dessas API's. Portanto, o formato dos dados vem direto do CEI, não há transformação feita por esse crawler. Sendo assim, caso haja algo estranho ou errado nos dados retornados, provavelmente é o próprio CEI que está retornando.

O cei-crawler utiliza as seguintes dependências:

  • puppeteer-core para navegar com o browser e resolver o captcha.
  • axios para fazer as requisições http.

Sponsor

Caso o cei-crawler tenha te ajudado e você queira fazer alguma doação pra me ajudar a mantê-lo, utilize o QR code abaixo :) Mande também seu nome e usuário do GitHub (caso tenha) que eu coloco aqui no README. Obrigado!

PIX: joao.menighin@gmail.com

Advertisement

Criei o cei-crawler para um projeto de acompanhamento de investimentos. Caso esteja procurando algo nesse sentido, confira o Stoincs!

Instalação

Basta instalar utilizando o NPM:

npm install --save cei-crawler

Utilização

Crie uma instância do CeiCrawler passando os parametros necessários e invoque o método desejado:

let ceiCrawler = new CeiCrawler('username', 'password', {/* options */});
ceiCrawler.login(); // Login é opcional, pois antes de cada método o cei-crawler irá verificar se já esta logado.
                    // A vantagem em realizar o login em um passo diferente é para o tratamento de erros

Login & Captcha

A nova área logada do CEI possui validação por captcha. Não há forma simples de resolver e por isso algumas estratégias de resolução são implementadas. Essas estratégias são setadas na instanciação do crawler, com o objeto de options. As disponíveis são:

raw-token

Nessa estratégia de login, não é necessário informar usuário e senha porém deve-se informar o token e o cache-guid do usuário logado. Essa estratégia é útil caso você possua algum serviço terceiro que faça o login no CEI e pegue o token pra você.

Exemplo:

const ceiCrawler = new CeiCrawler(_, _, { 
    loginOptions: {
        strategy: 'raw-token'
    },
    auth: {
        "cache-guid": "cache-guid do usuário logado",
        token: "JWT do usuário logado"
    }
});

const values = await ceiCrawler.getConsolidatedValues();

user-resolve

Nessa estratégia de login, o usuário será promptado para fazer o login ele mesmo em uma janela de browser que será aberta. O crawler tenta preencher usuário e senha para você de forma que o input manual é somente para resolução do Captcha. Uma vez feito o login, o crawler trata de pegar as credencias e seguir adiante chamando os métodos. Nas opções do login deve-se também ser informado um caminho do browser para que o puppeteer o controle.

Exemplo:

const ceiCrawler = new CeiCrawler('user', 'password', { 
    loginOptions: {
        strategy: 'user-resolve',
        browserPath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
    }
});

const values = await ceiCrawler.getConsolidatedValues();

Métodos disponíveis

getConsolidatedValues()

Retorna os investimentos consolidados num valor total e divididos em subcategorias

let consolidated = await ceiCrawler.getConsolidatedValues();

Resultado:

{
  "total": 10000,
  "subTotais": [
    {
      "categoriaProduto": "Renda Variável",
      "totalPosicao": 5000,
      "percentual": 0.5
    },
    {
      "categoriaProduto": "Tesouro Direto",
      "totalPosicao": 5000,
      "percentual": 0.5
    }
  ]
}

getPosition(date, page)

Retorna as posições da tela "Posição" em todas as categorias de investimentos.

Parâmetro Tipo Default Descrição
date Dte null Data da posição. Caso seja passado null ou nenhum valor, será usada a ultima data de processamento do CEI.
page Number 1 Paginação dos dados. Por default retorna a primeira página.
let position = await ceiCrawler.getPosition();

Resultado:

{
  "paginaAtual": 1,
  "totalPaginas": 1,
  "itens": [
    {
      "categoriaProduto": "RendaVariavel",
      "tipoProduto": "Acao",
      "descricaoTipoProduto": "Ações",
      "posicoes": [
        {
          "id": "gfw2455-8a79-4127-990b-587sa37",
          "temBloqueio": false,
          "instituicao": "INTER DISTRIBUIDORA DE TITULOS E VALORES MOBILIARIOS LTDA",
          "quantidade": 100,
          "valorAtualizado": 2377.00,
          "precoFechamento": 23.77,
          "produto": "BIDI4 - BANCO INTER S.A.",
          "tipo": "PN",
          "marcacoes": [],
          "codigoNegociacao": "BIDI4",
          "documentoInstituicao": "358743882",
          "existeLogotipo": false,
          "disponivel": 100,
          "documento": "48377283492",
          "razaoSocial": "BANCO INTER S.A.",
          "codigoIsin": "BRBRHEU2",
          "distribuicao": "114",
          "escriturador": "BANCO BRADESCO S/A",
          "valorBruto": 0
        }
      ],
      "totalPosicao": 2377.00,
      "totalItemsPagina": 1
    },
    {
      "categoriaProduto": "TesouroDireto",
      "tipoProduto": "TesouroDireto",
      "descricaoTipoProduto": "Tesouro Direto",
      "posicoes": [
        {
          "id": "hfd4564-e70a-4596-93fd-987654dvbhw",
          "temBloqueio": false,
          "instituicao": "XP INVESTIMENTOS CCTVM S/A",
          "quantidade": 1.01,
          "valorAtualizado": 2200,
          "vencimento": "2024-08-15T00:00:00",
          "valorAplicado": 2000,
          "produto": "Tesouro IPCA+ 2024",
          "marcacoes": [],
          "documentoInstituicao": "8573938583",
          "existeLogotipo": false,
          "indexador": "IPCA",
          "disponivel": 1.01,
          "documento": "7658493485",
          "codigoIsin": "VRSIYASU@",
          "valorBruto": 2038,
          "nomeTituloPublico": "Tesouro IPCA+ 2024",
          "valorLiquido": 29882,
          "percRentabilidadeContratada": 4.71
        }
      ],
      "totalPosicao": 22000,
      "totalItemsPagina": 5
    }
  ],
  "detalheStatusCode": 0,
  "excecoes": []
}

getPositionDetail(id, category, type)

Retorna o detalhe de uma posição da lista anterior.

Parâmetro Tipo Default Descrição
id String undefined UUID da posição. Foi observado que o UUID de uma mesma posição pode mudar ao longo do tempo e essa requisição falhar após pegar a lista com o getPosition()
category String undefined Categoria da posição informada no método getPosition().
type String undefined Tipo da posição informada no método getPosition().
let positionDetail = await ceiCrawler.getPositionDetail('gfw2455-8a79-4127-990b-587sa37', 'RendaVariavel', 'Acao');

Resultado:

{
  "codigoIsin": "BRBIDIACNPR0",
  "distribuicao": "114",
  "empresa": "BANCO INTER S.A.",
  "escriturador": "BANCO BRADESCO S/A",
  "codigoNegociacao": "BIDI4",
  "disponivel": 100,
  "indisponivel": 0,
  "quantidade": 100,
  "marcacoes": [],
  "possuiMarcacoes": false,
  "existeLogotipo": false,
  "documentoInstituicao": "358743882"
}

getAccountStatement(startDate, endDate, page)

Retorna as movimentações da aba "Movimentação" no CEI.

Parâmetro Tipo Default Descrição
startDate Date null Data de inicio para trazer as movimentações. Caso null, será utilizada a ultima data de processamento do CEI menos 1 mês.
endDate Date null Data fim para trazer as movimentações. Caso null, será utilizada a ultima data de processamento do CEI.
page Number 1 Paginação dos dados. Por default retorna a primeira página.
let accountStatement = await ceiCrawler.getAccountStatement();

Resultado:

{
  "paginaAtual": 1,
  "totalPaginas": 2,
  "itens": [
    {
      "data": "2021-08-02T00:00:00",
      "movimentacoes": [
        {
          "tipoOperacao": "Credito",
          "tipoMovimentacao": "Juros Sobre Capital Próprio",
          "nomeProduto": "BIDI4 - BANCO INTER S.A.",
          "instituicao": "INTER DISTRIBUIDORA DE TITULOS E VALORES MOBILIARIOS LTDA",
          "quantidade": 100,
          "valorOperacao": 1.49,
          "precoUnitario": 0.01
        }
      ],
      "totalItemsPagina": 1
    },
    {
      "data": "2021-07-30T00:00:00",
      "movimentacoes": [
        {
          "tipoOperacao": "Debito",
          "tipoMovimentacao": "Transferência",
          "nomeProduto": "ALZR11 - ALIANZA TRUST RENDA IMOBILIARIA FDO INV IMOB",
          "instituicao": "RICO INVESTIMENTOS - GRUPO XP",
          "quantidade": 5
        },
        {
          "tipoOperacao": "Credito",
          "tipoMovimentacao": "Transferência",
          "nomeProduto": "ALZR11 - ALIANZA TRUST RENDA IMOBILIARIA FDO INV IMOB",
          "instituicao": "XP INVESTIMENTOS CCTVM S/A",
          "quantidade": 5
        }
      ],
      "totalItemsPagina": 2
    }
  ],
  "detalheStatusCode": 0,
  "excecoes": []
}

getIpos(date, page)

Retorna os IPOs da tela "Ofertas Públicas" no CEI.

Parâmetro Tipo Default Descrição
date Date null Data de consulta. Caso seja passado null ou nenhum valor, será usada a ultima data de processamento do CEI.
page Number 1 Paginação dos dados. Por default retorna a primeira página.
let ipos = await ceiCrawler.getIPOs();

Resultado:

{
  "paginaAtual": 1,
  "totalPaginas": 1,
  "itens": [
    {
      "data": "2021-07-27T00:00:00",
      "ofertasPublicas": [
        {
          "id": "c80c8b8f-62d2-4b48-b242-b0f310cfa95a",
          "dataLiquidacao": "2021-07-27T00:00:00",
          "nomeEmpresa": "INVESTO ETF MSCI US TECHNOLOGY FDO INV IND INV EXT",
          "tipoOferta": "OUTRO",
          "oferta": "ETF INVESTO",
          "nomeInstituicao": "INTER DISTRIBUIDORA DE TITULOS E VALORES MOBILIARIOS LTDA",
          "quantidade": 10,
          "preco": 10,
          "valor": 100
        }
      ],
      "totalItemsPagina": 1
    }
  ],
  "detalheStatusCode": 0,
  "excecoes": []
}

getIPODetail(id)

Retorna o detalhe de uma posição da lista anterior.

Parâmetro Tipo Default Descrição
id String undefined UUID do IPO. Foi observado que o UUID de um mesmo IPO pode mudar ao longo do tempo e essa requisição falhar após pegar a lista com o getIPOs()
let ipoDetail = await ceiCrawler.getIPODetail('c80c8b8f-62d2-4b48-b242-b0f310cfa95a');

Resultado:

{
  "nomeProduto": "OUTRO INVESTO ETF MSCI US TECHNOLOGY FDO INV IND INV EXT",
  "nomeInstituicao": "INTER DISTRIBUIDORA DE TITULOS E VALORES MOBILIARIOS LTDA",
  "ativo": {
    "nomeEmpresa": "INVESTO ETF MSCI US TECHNOLOGY FDO INV IND INV EXT",
    "ticker": "USTK11L",
    "oferta": "ETF INVESTO",
    "codigoIsin": "BRUSTKCTF007"
  },
  "valores": {
    "preco": 10,
    "precoMaximo": 0,
    "valor": 100
  },
  "reserva": {
    "modalidade": "Compra/Integralização de cotas do ETF INVESTO",
    "quantidade": 10,
    "valor": 0
  },
  "quantidadeAlocada": 10,
  "dataLiquidacao": "2021-07-27T00:00:00"
}

getStockTransactions(startDate, endDate, page)

Retorna os dados da aba "Negociação" no CEI.

Parâmetro Tipo Default Descrição
startDate Date null Data de inicio para trazer as negociações. Caso null, será utilizada a ultima data de processamento do CEI menos 1 mês.
endDate Date null Data fim para trazer as negociações. Caso null, será utilizada a ultima data de processamento do CEI.
page Number 1 Paginação dos dados. Por default retorna a primeira página.
let stockTransactions = await ceiCrawler.getStockTransactions();

Resultado:

{
  "paginaAtual": 1,
  "totalPaginas": 1,
  "itens": [
    {
      "data": "2021-07-20T00:00:00",
      "totalCompra": 1000,
      "totalVenda": 0,
      "negociacaoAtivos": [
        {
          "tipoMovimentacao": "Compra",
          "mercado": "Mercado à Vista",
          "nomeInstituicao": "RICO INVESTIMENTOS - GRUPO XP",
          "codigoNegociacao": "PNVL3",
          "quantidade": 100,
          "preco": 10.00,
          "valor": 1000.00
        }
      ],
      "totalItemsPagina": 1
    }
  ],
  "detalheStatusCode": 0,
  "excecoes": []
}

getProvisionedEvents(date, page)

Retorna os eventos da tela "Eventos Provisionados" no CEI.

Parâmetro Tipo Default Descrição
date Date null Data de consulta. Caso seja passado null ou nenhum valor, será usada a ultima data de processamento do CEI.
page Number 1 Paginação dos dados. Por default retorna a primeira página.
let events = await ceiCrawler.getProvisionedEvents();

Resultado:

{
  "totalValorLiquido": 8.62,
  "paginaAtual": 1,
  "totalPaginas": 1,
  "itens": [
    {
      "id": "9cc87804-f9ae-143a-acfb-c953f38c72dd",
      "produto": "WEGE3        - WEG S/A",
      "tipo": "ON",
      "tipoEvento": "JUROS SOBRE CAPITAL PRÓPRIO",
      "previsaoPagamento": "2021-08-11T00:00:00",
      "instituicao": "INTER DISTRIBUIDORA DE TITULOS E VALORES MOBILIARIOS LTDA",
      "quantidade": 300,
      "precoUnitario": 0.03,
      "valorLiquido": 8.62,
      "totalItemsPagina": 1
    }
  ],
  "detalheStatusCode": 0,
  "excecoes": []
}

getProvisionedEventDetail(id)

Retorna o detalhe de um evento provisionado da lista anterior.

Parâmetro Tipo Default Descrição
id String undefined UUID do evento. Foi observado que o UUID de um mesmo evento pode mudar ao longo do tempo e essa requisição falhar após pegar a lista com o getProvisionedEvents()
let eventDetail = await ceiCrawler.getProvisionedEventDetail('9cc87804-f9ae-143a-acfb-c953f38c72dd');

Resultado:

{
  "codigoNegociacao": "WEGE3",
  "codigoIsin": "BRWEGEACNOR0",
  "distribuicao": "202",
  "escriturador": "BANCO BRADESCO S/A",
  "empresa": "WEG S/A",
  "dataAprovacao": "2021-03-23T00:00:00",
  "dataAtualizacao": "2021-03-30T00:00:00",
  "dataEx": "2021-03-29T00:00:00",
  "impostoRenda": 15,
  "valorImpostoRenda": 1.52,
  "valorBruto": 10.14,
  "disponivel": 100,
  "indisponivel": 0,
  "produto": "WEGE3        - WEG S/A",
  "tipo": "ON",
  "tipoEvento": "JUROS SOBRE CAPITAL PRÓPRIO",
  "previsaoPagamento": "2021-08-11T00:00:00",
  "quantidade": 100,
  "precoUnitario": 0.03,
  "valorLiquido": 8.62
}

Opções

Na criação de um CeiCrawler é possivel especificar alguns valores para o parâmetro options que modificam a forma que o crawler funciona. As opções são:

Propriedade Tipo Default Descrição
debug Boolean false Se true, printa mensages de debug no log.
loginOptions.strategy String user-resolve Estratégia utilizada no login. Veja Login & Captcha para mais informações.
loginOptions.browserPath String undefined Caminho para o executavél do browser que será controlado para resolucao do Captcha. Veja Login & Captcha para mais informações.
auth.token String undefined Token JWT do usuário logado no CEI. Utilizado quando a estratégia de login é raw-token
auth.cache-guid String undefined UUID da sessão do usuário logado no CEI. Utilizado quando a estratégia de login é raw-token

Exemplo:

const ceiCrawlerOptions = {
    debug: true,
    loginOptions: {
      strategy: 'user-resolve',
      browserPath: 'path/to/browser.exe'
    }
};

let ceiCrawler = new CeiCrawler('username', 'password', ceiCrawlerOptions);

Error Handling

O CEI Crawler possui um exceção própria, CeiCrawlerError, que é lançada em alguns cenários. Essa exceção possui um atributo type para te direcionar no tratamento:

type Descrição
UNAUTHORIZED Lançada quando uma request retorna 401. Isso pode significar que o token utiliza é inválido ou expirou.
BAD_REQUEST Lançada quando uma requisição falha por má formação. Pode ser um parâmetro errado, uma data menor que o limite minimo, etc.
TOO_MANY_REQUESTS O CEI faz throttling de requisições. Se ao usar o crawler você fizer muitas requisições rapidamente esse erro pode ser retornado
INVALID_LOGIN_STRATEGY Lançada quando informada uma estratégia de login inválida.

Exemplo de como fazer um bom tratamento de erros:

const CeiCrawler = require('cei-crawler');
const { CeiErrorTypes } = require('cei-crawler')

const ceiCrawler = new CeiCrawler('usuario', 'senha', { navigationTimeout: 20000 });

try {
  const positions = ceiCrawler.getPositions();
} catch (err) {
  if (err.name === 'CeiCrawlerError') {
    if (err.type === CeiErrorTypes.UNAUTHORIZED)
      // Handle unauthrozied
    else if (err.type === CeiErrorTypes.TOO_MANY_REQUESTS)
      // Handle too many requests
    // else ...
  } else {
    // Handle generic errors
  }
}

Licença

MIT