npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cei-crawler

v3.0.1

Published

Crawler para pegar dados do Canal Eletronico do Investidor

Downloads

179

Readme

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!

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