{"id":18679984,"url":"https://github.com/effeix/krawler_mpi","last_synced_at":"2025-11-07T14:30:32.177Z","repository":{"id":92510064,"uuid":"155198301","full_name":"effeix/krawler_mpi","owner":"effeix","description":null,"archived":false,"fork":false,"pushed_at":"2018-11-10T15:05:02.000Z","size":72,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-27T22:22:09.023Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/effeix.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-29T11:10:41.000Z","updated_at":"2018-11-10T15:05:03.000Z","dependencies_parsed_at":"2023-08-21T04:15:09.215Z","dependency_job_id":null,"html_url":"https://github.com/effeix/krawler_mpi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effeix%2Fkrawler_mpi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effeix%2Fkrawler_mpi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effeix%2Fkrawler_mpi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effeix%2Fkrawler_mpi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/effeix","download_url":"https://codeload.github.com/effeix/krawler_mpi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239533067,"owners_count":19654617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T09:47:27.721Z","updated_at":"2025-11-07T14:30:32.144Z","avatar_url":"https://github.com/effeix.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Krawler MPI\n\nWeb crawler distribuído para sites de e-commerce\n\n## Objetivo\n\nUm *Web Crawler* tem como objetivo recuperar informações de uma ou mais páginas web de maneira inteligente, percorrendo caminhos criados pelos links de cada página. Neste projeto, o objetivo é criar um [Web Crawler](https://en.wikipedia.org/wiki/Web_crawler) capaz de recuperar informações de produtos disponíveis em sites de e-commerce e disponibilizá-las para o usuário, semelhante ao funcionamento do site [Buscapé](https://www.buscape.com.br/). Este *crawler* deve ser implementado de maneira distribuída, afim de diminuir o tempo de execução do programa, criando assim um sistema amigável para o usuário.\n\nA biblioteca [MPI](https://www.open-mpi.org/) será utilizada para realizar esta implementação distribuída, mais especificamente a versão disponibilizada pelo framework [Boost](https://www.boost.org/). A distribuição do programa se dá por meio da utilização de diversos processos e troca de mensagens entre estes processos, ações facilitadas pelo MPI. O intuito deste tipo de implementação é permitir a execução do programa em sistemas distribuídos, como por exemplo um *cluster* no [AWS](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_clusters.html).\n\n## Dependências\n- Boost\n- MPI\n- GCC/G++\n- CMake\n- Make\n- Python 3.7+\n- matplotlib (Python)\n\n## MPI\n\nO MPI (Message Passing Interface) é uma biblioteca criada para permitir o desenvolvimento de programas que utilizam diversos processos de um ou mais computadores para executar tarefas paralelamente. Este protocolo funciona com trocas de mensagens (dados) entre processos a partir de um processo mestre. Neste projeto, queremos fazer com que diversas URLs sejam acessadas ao mesmo tempo, portanto o objetivo do MPI será dividir URLs para cada processo, que por sua vez irão realizar a coleta de produtos e enviá-los a um processo principal (normalmente o 0 (zero)) para que sejam mostrados. os processos podem ser criados na mesma máquina ou em máquinas diferentes. Caso esteja rodando num *cluster*, um arquivo contendo hosts pode ser especificado. A utilização com *clusters* não será detalhada neste relatório, mas pode ser facilmente encontrada na Internet.\n\n## Funcionamento\n\nAs tarefas realizadas por cada processo são bastante simples:\n1. Realizar o download de páginas web.\n2. Coletar informações de produtos utilizando [Expressões Regulares](https://en.wikipedia.org/wiki/Regular_expression).\n3. Mostrar as informações de cada produto em formato [JSON](https://www.json.org/).\n\nPrimeiramente, o processo 0 (zero) recebe um lista com diversas URLs. Esta lista será quebrada quase igualmente entre N processos, definidos pelo usuário que executa o programa. Cada lista menor menor será enviada para um outro processo, onde as URLs serão percorridas e utilizadas para baixar diferentes produtos.\n\n##### Exemplo\nSuponha que a lista inicial de URLs possua 40 links distintos.\nA lista inicial tem o seguinte formato:\n\n```python\n[url1, url2, url3, url4, ..., url40]\n```\n\nSuponha também que colocaremos 4 processos para serem executados. Uma nova lista será criada contendo 4 elementos. Cada elemento é um novo vetor com 10 URLs:\n\n```python\n[\n    [url1,  ..., url10],\n    [url11, ..., url20],\n    [url21, ..., url31],\n    [url31, ..., url40]\n]\n```\n\nApós esta separação, utilizamos a função `boost::mpi::scatter()` para enviar cada elemento a um processo diferente. O primeiro vetor (URLs 1-10) será enviado para o processo de mesmo índice de sua posição na lista, ou seja, 0 (zero). Já o segundo vetor (URLs 11-20) será enviado para o processo 1, e assim por diante.\n\nCada processo utilizará seu conjunto de URLs para coletar os produtos, podendo navegar para outros links afim de facilitar a coleta. Cada produto será composto pelas seguintes informações: nome, descrição, foto, preço, preço parcelado, número de parcelas, categoria e URL específica do produto, formatados em um JSON conforme abaixo:\n\n```json\n{\n    \"nome\": \"Produto\",\n    \"descricao\": \"Descrição do Produto\",\n    \"foto\": \"http://...\",\n    \"preco\": 1000,\n    \"preco_parcelado\": 1000,\n    \"preco_num_parcelas\": 10,\n    \"categoria\": \"Categoria\",\n    \"url\": \"http://...\"\n}\n```\n\nAs informações são obtidas por meio do uso de expressões regulares que as detectam em meio ao conteúdo HTML de cada página. Após a finalização de todas as URLs de todos os processos, a função `boost::mpi::gather()` é utilizada para agrupar novamente os produtos gerados, em um processo escolhido pelo usuário (novamente, normalmente o 0 (zero)). Após agrupados, os produtos serão mostrados ao usuário via saída padrão (terminal). Abaixo est um esquema da troca de mensagens entre processos:\n\n![MPI Schema](https://i.imgur.com/9BagN78.png)\n\n## Métricas\n\nAfim de explorar a performance deste programa, algumas métricas de tempo são coletadas ao longo do *crawling*. São elas:\n\n- Tempo ocioso (TOTAL_IDLE_TIME): tempo de espera pelo download de páginas\n- Tempo médio ocioso (AVG_IDLE_TIME): tempo médio de espera pelo download de páginas\n- Tempo por produto (PROD_TIME): tempo gasto para baixar uma página específica de um produto e criar sua visualização em JSON. Cada produto baixado possui uma linha com o tempo gasto.\n- Tempo médio por produto (AVG_TIME_PER_PRODUCT): tempo total de execução dividido pelo número de produtos coletados.\n\nOutras métricas coletadas:\n- Tempo total de execução (TOTAL_EXEC_TIME).\n- Número de produtos coletados (TOTAL_PROD_COUNT).\n\n##### Nota: a métrica AVG_IDLE_TIME foi introduzida porque a métrica TOTAL_IDLE_TIME mostra o tempo total de downloads de páginas de maneira sequencial (soma de todos os tempos), o que não demonstra a realidade numa execução paralela (páginas sendo baixadas ao mesmo tempo). Como não é possível identificar com precisão o tempo de downloads *paralelos*, foi introduzido um cálculo de tempo médio de download. \n\nTodas as métricas são enviadas para a saída de erro padrão do sistema, que pode ser redirecionada para um arquivo de texto a gosto do usuário, como no exemplo a seguir (todos os valores estçao em segundos, com exceção de TOTAL_PROD_COUNT que é uma contagem):\n\n```\n1.2\n0.85\n0.50\n...\nTOTAL_PROD_COUNT: 200\nAVG_IDLE_TIME: 15\nTOTAL_IDLE_TIME: 180\nTOTAL_EXEC_TIME: 30\nAVG_TIME_PER_PRODUCT: 0.7\n```\n\n## Utilização\n\n#### Sites suportados\n- [Magazine Luiza](https://www.magazineluiza.com.br/)\n\nO programa utiliza categorias de produtos como entrada. A categoria é uma URL de um site suportado. No caso do Magazine Luiza, a URL deve ser obitda navegando para o site, clicando em **Todos os Departamentos** na parte superior esquerda, e escolhendo um dos links destacados pela área vermelha:\n\n\u003cimg src=\"https://i.imgur.com/ief76w1.png\" alt=\"\" width=\"50%\" height=\"50%\" /\u003e\n\nNa próxima página clique em um dos links destacados novamente pela área vermelha:\n\n\u003cimg src=\"https://i.imgur.com/FNuQU2P.png\" alt=\"\" width=\"50%\" height=\"50%\" /\u003e\n\nA URL da págna que abrirá é a que deve ser utilizada:\n\n\u003cimg src=\"https://i.imgur.com/NmOzgTQ.png\" alt=\"\" width=\"50%\" height=\"50%\" /\u003e\n\nO teste acima foi realizado clicando em: **Todos os Departamentos \u003e Celulares \u003e Smartphones** obtendo a URL **https://www.magazineluiza.com.br/smartphone/celulares-e-smartphones/s/te/tcsp/**\n\nApós selecionada a URL, o programa deve ser compilado da seguinte maneira:\n\n```sh\n$ cd src/build/\n$ /usr/bin/cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -G 'Unix Makefiles' \u003c/caminho/até/krawler_mpi/src\u003e\n$ make\n```\n\nUm programa em python foi criado para executar o programa (localizado em `test/`), para realizar diversos testes e criar gráficos de tempo. Para utilizá-lo, navegue até a pasta `test/` e rode o seguinte comando:\n```sh\n$ python run.py \u003cmax_procs\u003e \u003citer\u003e \u003curl\u003e\n```\n\nem que `\u003cmax_procs\u003e` é o número máximo de processos a serem utilizados - serão feitos testes utilizando de 1 até `\u003cmax_procs\u003e` processos - `\u003citer\u003e` é o número de iterações com a mesma quantidade de processos, afim de obter medidas mais precisas e `\u003curl\u003e` é a URL da categoria, como explicado acima.\n\nEste programa está preparado para rodar num cluster especifico criado para este projeto. Caso deseje rodar num cluster próprio, edite as lista `HOSTNAMES` e `HOSTDESCS` no arquivo `run.py` e coloque os dados correspondentes às máquinas de seu cluster.\n\n#### Arquivos de teste\n\nAs métricas estarão na pasta `test/files/`, com cada arquivo estando no formato `iters_mX_pY_iZ`, onde X é a quantidade de máquinas utilizadas para aquele teste, Y é a quantidade de processos utilizada para aquele teste e Z é a iteração. Por exemplo:\n\n\u003e O arquivo `iters_m2_p4_i0` mostra os resultados da primeira iteração de uma execução que utilizou 2 máquinas e 4 processos. O arquivo `iters_m2_p4_i1` mostra os resultados da segunda iteração da mesma execução.\n\nCada máquina suporta um máximo de dois processos, sendo esta uma escolha do desenvolvedor. O arquivo `hosts`, presente na pasta `test/`, mostra os *hosts* (máquinas) utilizados pela última execução e a quantidade de processos (*slots*) disponíveis em cada um. Por algum motivo ainda não descoberto, a execução com mais de 7 processos não é possvel, gerando um erro do tipo *Segmentation Fault*.\n\n#### Gráficos\n\nCaso o usuário possua a biblioteca de python `matplotlib`, 5 gráficos podem ser gerados com o programa `make_charts.py`, passando a quantidade de processos máximos a serem analisado (e.g. se o usuário passar 6, haverá 6 pontos de dado em cada gráfico). É esperado que a métrica TOTAL_PROD_COUNT se mantenha constante independentemente do número de processos, indicando que a mesma quantidade de produtos foi recuperada em todos os testes.\n\nUtilização:\n```sh\n$ python make_charts.py \u003cmax_proc\u003e\n```\n\n## Resultados\nPara demonstrar o programa foram gerados alguns testes utilizando a URL https://www.magazineluiza.com.br/artesanato/armarinhos/s/am/arsa/, categoria que não possui quantidade exagerada de produtos mas é grande o suficiente para o teste. Os testes foram realizados em um cluster AWS com instância rodando sistema Ubuntu 18.04.1, utilizando de 1 até 7 processos e de 1 até 4 máquinas, com máximo de 2 processos por máquina. Cada execução com um determinado número de processos foi realizada com 5 iterações, resultando em 42 iterações no total.\n\nO programa apresentará erros se a quantidade de páginas a serem buscadas for menor do que a quantidade de processos passados.\n\nOs gŕaficos gerados estão abaixo, sendo que o eixo horizontal representa número de processos e o eixo vertical representa tempo em segundos, exceto no gráfico TOTAL_PROD_COUNT, em que o eixo vertical representa quantidade:\n\n![TOTAL_PROD_COUNT](https://i.imgur.com/Pg3t1rj.png)\n\nComo mencionado, o gráfico é constante, mostrando que a mesma quantidade de produtos foi coletada em todos os testes.\n\n![TOTAL_EXEC_TIME](https://i.imgur.com/T39FKeL.png)\n![AVG_TIME_PER_PRODUCT](https://i.imgur.com/Dwig3nH.png)\n![AVG_IDLE_TIME](https://i.imgur.com/QLRFjvn.png)\n\nOs três gráficos acima mostram uma melhora bastante significativa conforme a quantidade de processos aumenta. No foi possvel conferir se um número maior de processos diminuiria ainda mais os tempos, por conta dos erros presentes ao utilizar mais de 7 processos.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffeix%2Fkrawler_mpi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feffeix%2Fkrawler_mpi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffeix%2Fkrawler_mpi/lists"}