public static string GetFinDataCapitalFileName(this ScrappedCompany c, DocLinkInfo link) { string date = link.Data.ToString("yyyyMMdd"); string file = $"{c.CodigoCVM}.{link.DocType}.{date}.CapitalConsolidado.json"; string path = Path.Combine($"{Program.FINDATA_DIR}", c.CodigoCVM.ToString()); return(Path.Combine(path, file)); }
public static string GetFinDataFileName(this ScrappedCompany c, DocLinkInfo link, FinInfoCategoria categoria, FinInfoTipo tipo) { string date = link.Data.ToString("yyyyMMdd"); string file = $"{c.CodigoCVM}.{link.DocType}.{date}.{categoria}.{tipo}.json"; string path = Path.Combine($"{Program.FINDATA_DIR}", c.CodigoCVM.ToString()); return(Path.Combine(path, file)); }
static void UpdateCompaniesInDatabase(List <ScrappedCompany> companies) { if (companies == null) { companies = ScrappedCompany.LoadCompaniesFromFiles(BASICDATA_DIR); } EmpresaRepository.InsertOrUpdate(companies); }
private static Dictionary <DocInfoType, List <DocLinkInfo> > LoadLinks(ScrappedCompany company) { var file = company.GetLinksFileName(); if (!File.Exists(file)) { Console.WriteLine($"arquivo de links da empresa {company.RazaoSocial} não existe"); log.Error($"arquivo de links da empresa {company.RazaoSocial} não existe"); return(null); } string content = File.ReadAllText(file); return(JsonConvert.DeserializeObject <Dictionary <DocInfoType, List <DocLinkInfo> > >(content)); }
private static async Task FillCompanyData(ScrappedCompany c) { Console.WriteLine($"Obtendo informações básicas de {c.RazaoSocial}"); log.Info($"Obtendo informações básicas de {c.RazaoSocial}"); var watch = Stopwatch.StartNew(); var url = $"{BASE_URL}/pt-br/mercados/acoes/empresas/ExecutaAcaoConsultaInfoEmp.asp?CodCVM={c.CodigoCVM}&ViewDoc=0"; var client = new HttpClient(); var response = await client.GetStringWithRetryAsync(url); ParseBasicData(response, c); watch.Stop(); Console.WriteLine($"dados obtidos em {watch.Elapsed.TotalSeconds} segundos"); }
private static void FillEmpresaWithCompany(Empresa e, ScrappedCompany c) { e.Codigo = c.CodigoCVM; e.RazaoSocial = c.RazaoSocial; e.NomePregao = c.NomePregao; e.Segmento = c.Segmento; e.CNPJ = c.CNPJ; if (c.AtividadePrincipal != null) { e.AtividadePrincipal = string.Join(",", c.AtividadePrincipal); } if (c.ClassificacaoSetorial != null) { e.ClassificacaoSetorial = string.Join(",", c.ClassificacaoSetorial); } e.Site = c.Site; e.UltimaAtualizacao = c.UltimaAtualizacao; }
public static void SaveDocLinks(this ScrappedCompany c, Dictionary <DocInfoType, List <DocLinkInfo> > links) { log.Info($"Salvando arquivo de links para a empresa {c.RazaoSocial}"); var filename = c.GetLinksFileName(); log.Info($"File={filename}"); string json = JsonConvert.SerializeObject(links, Formatting.Indented); var dir = Path.GetDirectoryName(filename); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } File.WriteAllText(filename, json); }
private static List <Ticker> GetTickers(ScrappedCompany c) { log.Info($"Tickers = {string.Join(",", c.CodigosNegociacao)}"); List <Ticker> tickers = new List <Ticker>(); foreach (var ticker in c.CodigosNegociacao) { if (!string.IsNullOrWhiteSpace(ticker)) { tickers.Add(new Ticker { Nome = ticker, CodigoCVM = c.CodigoCVM }); } } return(tickers); }
static async Task ExtractFinancialDataAsync(List <ScrappedCompany> companies) { if (companies == null) { companies = ScrappedCompany.LoadCompaniesFromFiles(BASICDATA_DIR); } IEnumerable <ScrappedCompany> filtered = companies; // if (Options.Instance.Company > 0) // { // filtered = companies.Where(c => c.CodigoCVM == Options.Instance.Company); // } foreach (var company in filtered) { await FinancialDataScrapper.ExtractFinancialInfo(company); } }
private static List <ScrappedCompany> ParseCompanies(string html) { log.Info("Extraindo lista de empresas"); var parser = new HtmlParser(); var doc = parser.Parse(html); //doc.LoadHtml(html); List <ScrappedCompany> companies = new List <ScrappedCompany>(); var nodes = doc.QuerySelectorAll("tr.GridRow_SiteBmfBovespa, tr.GridAltRow_SiteBmfBovespa"); foreach (var node in nodes) { var tds = node.ChildNodes.Where(n => n.NodeName.ToLowerInvariant() == "td").ToArray(); var href = ((IElement)tds[0].FirstChild).Attributes["href"].Value.Trim(); var razao = tds[0].TextContent.Trim(); var nomepregao = tds[1].TextContent.Trim(); var segmento = tds[2].TextContent.Trim(); log.Info($"Extraindo Companhia: {razao}"); log.Info($"nomepregao = {nomepregao}"); log.Info($"href = {href}"); log.Info($"segmento = {segmento}"); if (segmento != SEGMENTO_MERCADO_BALCAO) { var company = new ScrappedCompany(); company.RazaoSocial = razao.Trim(); company.NomePregao = nomepregao.Trim(); company.Segmento = SegmentoEnumExtensions.FromString(segmento.Trim()); company.CodigoCVM = GetCodigoCvm(href); companies.Add(company); log.Info($"codigo cvm {company.CodigoCVM}"); } else { log.Info($"segmento {SEGMENTO_MERCADO_BALCAO}. Ignorando"); } } return(companies); }
static async Task ExtractDocLinksAsync(List <ScrappedCompany> companies) { if (companies == null) { companies = ScrappedCompany.LoadCompaniesFromFiles(BASICDATA_DIR); } int counter = 1; foreach (var c in companies) { bool shouldExtract = true; // Só deve extrair os links e/ou sobrepor o arquivo da empresa se a data de atualização(company.UltimaAtualizacao) for // maior que a data do arquivo (FileTime) // ou se o arquivo não existir var info = new FileInfo(c.GetLinksFileName()); if (info.Exists && info.LastWriteTime > c.UltimaAtualizacao) { shouldExtract = false; } if (!shouldExtract) { Console.WriteLine($"Empresa {c.RazaoSocial} não necessita extrair links"); log.Info($"Empresa {c.RazaoSocial} não necessita extrair links"); continue; } Console.WriteLine($"Extraindo link da empresa {c.RazaoSocial} -- {counter}/{companies.Count}"); counter += 1; log.Info($"Extraindo link da empresa {c.RazaoSocial}"); var doclinks = await BvmfDocSummaryScrapper.GetDocsInfoReferences(c); //save links for company c.SaveDocLinks(doclinks); } }
public static string GetLinksFileName(this ScrappedCompany c) { string path = $"{Program.LINKS_DIR}{c.CodigoCVM}.json"; return(path); }
private static async Task <List <DocLinkInfo> > GetDocumentsLinks(DocInfoType docType, ScrappedCompany c) { log.Info($"Obtendo documentos do tipo {docType} - {c.RazaoSocial}"); // HistoricoFormularioReferencia.aspx?codigoCVM=6017&tipo=itr&ano=0 var histUrl = $"HistoricoFormularioReferencia.aspx?codigoCVM={c.CodigoCVM}&tipo={docType.ToString().ToLower()}&ano=0"; var docs = new List <DocLinkInfo>(); // name = key // kink = value Console.WriteLine($"Obtendo lista de documentos {docType}"); var url = $"{BvmfScrapper.BASE_URL}{BvmfScrapper.LIST_URL}/{histUrl}"; var client = new HttpClient(); var response = await client.GetStringAsync(url); var parser = new HtmlParser(); var doc = parser.Parse(response); // o segundo h4 que contém o que desejamos. O primeiro é o título da página var h4 = doc.QuerySelectorAll("h4").Skip(1).Take(1).Single(); var list = GetSectionList(h4); var anchors = list.QuerySelectorAll("a"); foreach (var a in anchors) { // date, text, link //var a = (IHtmlAnchorElement)div.QuerySelector("a"); docs.Add(new DocLinkInfo(docType, (IHtmlAnchorElement)a)); } return(docs); }
public static async Task ExtractFinancialInfo(ScrappedCompany company) { // load links var links = LoadLinks(company); if (links == null) { await Task.FromResult <object>(null); } // extract ITR Console.WriteLine($"Extraindo informações de ITR da empresa {company.RazaoSocial}"); log.Info($"Extraindo informações de ITR da empresa {company.RazaoSocial}"); var itrs = links[DocInfoType.ITR]; itrs = RemoveDuplicates(itrs); foreach (var itr in itrs) { var scrapper = new ItrDfpDataScrapper(company); if (!scrapper.DoINeedToExtractAny(itr)) { continue; } var docs = await scrapper.GetAvailableDocs(itr); if (docs.ComposicaoCapital) { await scrapper.ScrapComposicaoCapital(itr); } if (docs.AtivoIndividual) { await scrapper.ScrapDoc(itr, FinInfoTipo.Individual, FinInfoCategoria.Ativo); } if (docs.PassivoIndividual) { await scrapper.ScrapDoc(itr, FinInfoTipo.Individual, FinInfoCategoria.Passivo); } if (docs.DREIndividual) { await scrapper.ScrapDoc(itr, FinInfoTipo.Individual, FinInfoCategoria.DRE); } if (docs.AtivoConsolidado) { await scrapper.ScrapDoc(itr, FinInfoTipo.Consolidado, FinInfoCategoria.Ativo); } if (docs.PassivoConsolidado) { await scrapper.ScrapDoc(itr, FinInfoTipo.Consolidado, FinInfoCategoria.Passivo); } if (docs.DREConsolidado) { await scrapper.ScrapDoc(itr, FinInfoTipo.Consolidado, FinInfoCategoria.DRE); } } // extract DFP Console.WriteLine($"Extraindo informações de DFP da empresa {company.RazaoSocial}"); log.Info($"Extraindo informações de DFP da empresa {company.RazaoSocial}"); var dfps = links[DocInfoType.DFP]; dfps = RemoveDuplicates(dfps); foreach (var dfp in dfps) { var scrapper = new ItrDfpDataScrapper(company); await scrapper.ScrapComposicaoCapital(dfp); await scrapper.ScrapDoc(dfp, FinInfoTipo.Individual, FinInfoCategoria.Ativo); await scrapper.ScrapDoc(dfp, FinInfoTipo.Individual, FinInfoCategoria.Passivo); await scrapper.ScrapDoc(dfp, FinInfoTipo.Individual, FinInfoCategoria.DRE); await scrapper.ScrapDoc(dfp, FinInfoTipo.Consolidado, FinInfoCategoria.Ativo); await scrapper.ScrapDoc(dfp, FinInfoTipo.Consolidado, FinInfoCategoria.Passivo); await scrapper.ScrapDoc(dfp, FinInfoTipo.Consolidado, FinInfoCategoria.DRE); } }
public static Dictionary <DocInfoType, List <DocLinkInfo> > LoadDocLinksForCompany(this ScrappedCompany company) { var filename = company.GetLinksFileName(); log.Info($"Carregando links da empresa {company.RazaoSocial} - {filename}"); string json = File.ReadAllText(filename); return(JsonConvert.DeserializeObject <Dictionary <DocInfoType, List <DocLinkInfo> > >(json)); }
public static async Task <Dictionary <DocInfoType, List <DocLinkInfo> > > GetDocsInfoReferences(ScrappedCompany c) { Console.WriteLine("Obtendo históricos de documentos"); log.Info($"Obtendo históricos de documentos - {c.RazaoSocial}"); // link: finacial reports // http://bvmf.bmfbovespa.com.br/cias-listadas/empresas-listadas/ResumoDemonstrativosFinanceiros.aspx?codigoCvm=21903&idioma=pt-br // __EVENTTARGET = ctl00$contentPlaceHolderConteudo$cmbAno // __VIEWSTATE // ctl00$contentPlaceHolderConteudo$cmbAno = 2009 var client = new HttpClient(); var url = $"{BvmfScrapper.BASE_URL}{BvmfScrapper.LIST_URL}ResumoDemonstrativosFinanceiros.aspx?codigoCvm={c.CodigoCVM}&idioma=pt-br"; var response = await client.GetStringWithRetryAsync(url); var docLinks = new Dictionary <DocInfoType, List <DocLinkInfo> >(); // informações trimestrais var itrs = await GetDocumentsLinks(DocInfoType.ITR, c); docLinks.Add(DocInfoType.ITR, itrs); // demonstrações financeiras padronizadas var dfps = await GetDocumentsLinks(DocInfoType.DFP, c); docLinks.Add(DocInfoType.DFP, dfps); // // Formulário de Referência // var fres = await GetDocumentsLinks(DocInfoType.FRE, c); // docLinks.Add(DocInfoType.FRE, fres); // // Formulário Cadastral // var fcas = await GetDocumentsLinks(DocInfoType.FCA, c); // docLinks.Add(DocInfoType.FCA, fcas); // // Informe Trimestral de Securitzadora // var secs = await GetDocumentsLinks(DocInfoType.SEC, c); // docLinks.Add(DocInfoType.SEC, secs); // // Informações Anuais // var ians = await GetDocumentsLinks(DocInfoType.IAN, c); // docLinks.Add(DocInfoType.IAN, ians); return(docLinks); }
private static void ParseBasicData(string html, ScrappedCompany c) { log.Info($"Extraindo informações básicas de {c.RazaoSocial}"); var parser = new HtmlParser(); var doc = parser.Parse(html); //doc.LoadHtml(html); // first thing: verify if response is "Dados indisponiveis" var alert = doc.QuerySelector("div.alert-box"); if (alert != null) { if (alert.TextContent.ToLowerInvariant().Contains("dados indisponiveis")) { log.Error($"Dados de {c.RazaoSocial} não disponíveis."); throw new UnavailableDataException(); } } var rows = doc.QuerySelectorAll("div.row"); IElement p = null; // first row is last update datetime p = rows[0].QuerySelector("p.legenda"); // text = Atualizado em 28/04/2017, às 05h13 var date = GetDate(p.TextContent.Trim()); c.UltimaAtualizacao = date; log.Info($"Última atualização {c.UltimaAtualizacao.ToString("yyyy-MM-dd HH:mm:ss")}"); // second row is basic company data var tableficha = rows[1].QuerySelector("table.ficha"); var trs = tableficha.ChildNodes[1].ChildNodes.Where(n => n.NodeName.ToLowerInvariant() == "tr").OfType <IElement>().ToArray(); // ignore first TR, we already have this info var td = trs[1].QuerySelectorAll("td").Last(); if (td.TextContent.Contains("Nenhum ativo no Mercado a Vista")) { log.Info($"A empresa {c.RazaoSocial} não possui ativos no mercado"); } else { var anchors = td.QuerySelectorAll("a.LinkCodNeg").Select(n => n.TextContent); c.CodigosNegociacao = new SortedSet <string>(anchors); log.Info($"Códigos de negociação {string.Join(",", c.CodigosNegociacao.ToArray())}"); } var cnpj = trs[2].QuerySelectorAll("td").Last().TextContent.Trim(); c.CNPJ = cnpj; var mainActivity = trs[3].QuerySelectorAll("td").Last().TextContent.Trim(); if (!string.IsNullOrWhiteSpace(mainActivity)) { c.AtividadePrincipal = (from item in mainActivity.Split('/') select item.Trim()).ToArray(); } var setorialClassfications = trs[4].QuerySelectorAll("td").Last().TextContent.Trim(); if (!string.IsNullOrWhiteSpace(setorialClassfications)) { c.ClassificacaoSetorial = (from item in setorialClassfications.Split('/') select item.Trim()).ToArray(); } if (trs.Length > 5) { c.Site = trs[5].QuerySelectorAll("td").Last().TextContent.Trim(); } }
public static async Task <List <ScrappedCompany> > GetCompanies() { Console.WriteLine("Obtendo companhias listadas"); log.Info("Obtendo companhias listadas"); var watch = Stopwatch.StartNew(); var payload = new Dictionary <string, string> { { "__EVENTTARGET", "ctl00:contentPlaceHolderConteudo:BuscaNomeEmpresa1:btnTodas" } }; string url = $"{BASE_URL}{LIST_URL}BuscaEmpresaListada.aspx?idioma=pt-br"; var client = new HttpClient(); var response = await client.PostDataAsync(url, payload); var companies = ParseCompanies(response); watch.Stop(); Console.WriteLine($"{companies.Count} companhias encontradas em {watch.Elapsed.TotalSeconds} segundos"); foreach (var c in companies) { try { await FillCompanyData(c); }catch (UnavailableDataException ex) { // ignore this company for now continue; } string file = c.GetFileName(); if (File.Exists(file)) { log.Info($"Arquivo da empresa {c.RazaoSocial} já existe. Verificando data"); Console.WriteLine("Empresa já extraída.. verificando...."); log.Info($"Carregando empresa do arquivo {file}"); var deserialized = ScrappedCompany.Load(file); // deserializer é null quando o arquivo está vazio por algum erro em execuções ateriores if (deserialized != null && c.UltimaAtualizacao <= deserialized.UltimaAtualizacao) { log.Info($"Empresa já está atualizada. Data última atualização: {c.UltimaAtualizacao}"); Console.WriteLine("Empresa está atualizada. Pulando"); c.NeedsUpdate = false; continue; } } // save file // se a empresa não tiver código de negociação, ignorar if (c.CodigosNegociacao?.Count() == 0 || c.CodigosNegociacao == null) { Console.WriteLine($"Ignorando empresa {c.RazaoSocial} por não possuir código de negociação"); log.Info($"Ignorando empresa {c.RazaoSocial} por não possuir código de negociação"); } else { // Só deve sobrepor o arquivo da empresa se a data de atualização (company.UltimaAtualizacao) for // maior que a data do arquivo (FileTime) // ou se o arquivo não existir bool shouldSave = true; var info = new FileInfo(c.GetFileName()); if (info.Exists && info.LastWriteTime > c.UltimaAtualizacao) { shouldSave = false; } if (shouldSave) { log.Info("Salvando arquivo da empresa"); c.Save(); } else { Console.WriteLine($"Empresa {c.RazaoSocial} não necessita ser atualizada - arquivo não foi salvo"); log.Info($"Empresa {c.RazaoSocial} não necessita ser atualizada - arquivo não foi salvo"); } } } // retorna somente as empresas que possuam códigos de negociação return(companies.Where(c => c.CodigosNegociacao != null && c.CodigosNegociacao.Count() > 0).ToList()); }
public static string GetFileName(this ScrappedCompany c) { string path = $"{Program.BASICDATA_DIR}{c.CodigoCVM}.json"; return(path); }
private static void UpdateExistentsTickers(DataContext db, Empresa empresaInDb, ScrappedCompany company) { log.Info($"Tickers = {string.Join(",", company.CodigosNegociacao)}"); // primeiro define todos os ticker como UNCHANGED, assim, os que não forem excluídos os adicionados // não serão alterados empresaInDb.Tickers.ForEach((ticker) => { var entry = db.Entry(ticker); if (entry != null) { entry.State = EntityState.Unchanged; } }); // tickers que existem n banco mas não em company > delete empresaInDb.Tickers.RemoveAll(t => !company.CodigosNegociacao.Contains(t.Nome)); db.RemoveRange(empresaInDb.Tickers.Where(t => !company.CodigosNegociacao.Contains(t.Nome))); foreach (var cod in company.CodigosNegociacao) { // tickers que existem em company mas não no banco > insert if (!empresaInDb.Tickers.Any(t => t.Nome == cod) && !string.IsNullOrWhiteSpace(cod)) { empresaInDb.Tickers.Add( new Ticker() { CodigoCVM = empresaInDb.Codigo, Nome = cod }); } } }
public ItrDfpDataScrapper(ScrappedCompany company) { this.Company = company; }