pyFBDS 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyfbds-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Open Geodata
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pyfbds-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyFBDS
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: requests>=2.32.5
9
+ Requires-Dist: requests-cache>=1.2.1
10
+ Requires-Dist: tqdm>=4.67.1
11
+ Requires-Dist: lxml>=6.0.0
12
+ Requires-Dist: ipywidgets>=8.1.8
13
+ Requires-Dist: aiohttp>=3.13.2
14
+ Requires-Dist: nest-asyncio>=1.6.0
15
+ Requires-Dist: pandas>=2.3.3
16
+ Requires-Dist: selenium>=4.38.0
17
+ Dynamic: license-file
18
+
19
+ # pyFBDS
20
+
21
+ [![Repo](https://img.shields.io/badge/GitHub-repo-blue?logo=github&logoColor=f5f5f5)](https://github.com/open-geodata/pyFBDS)
22
+ [![PyPI - Version](https://img.shields.io/pypi/v/pyfbds?logo=pypi&label=PyPI&color=blue)](https://pypi.org/project/pyfbds/)<br>
23
+ [![Read the Docs](https://img.shields.io/readthedocs/pyFBDS?logo=ReadTheDocs&label=Read%20The%20Docs)](https://pyFBDS.readthedocs.io/)
24
+ [![Publish Python to PyPI](https://github.com/michelmetran/pyFBDS/actions/workflows/publish-to-pypipoetry.yml/badge.svg)](https://github.com/michelmetran/pyFBDS/actions/workflows/publish-to-pypipoetry.yml)
25
+
26
+ _Scripts_ para obter dados espaciais do [**repositório público de mapas e _shapefiles_ para _download_**](https://geo.fbds.org.br/). Veja mais na documentação:
27
+
28
+ > [https://pyFBDS.readthedocs.io/](https://pyFBDS.readthedocs.io/)
29
+
30
+ <br>
31
+
32
+ ---
33
+
34
+ ## Como Instalar?
35
+
36
+ ```shell
37
+ pip3 install pyFBDS
38
+ ```
39
+
40
+ <br>
41
+
42
+ ---
43
+
44
+ ## Como Usar?
45
+
46
+ ```shell
47
+ # Importa pacote
48
+ import pyFBDS
49
+
50
+ # Instancia Objeto
51
+ fbds = pyFBDS.Repo(output_path='.')
52
+
53
+ # Chama o método
54
+ fbds.get_municipio(id_ibge=353243)
55
+ ```
pyfbds-0.1.0/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # pyFBDS
2
+
3
+ [![Repo](https://img.shields.io/badge/GitHub-repo-blue?logo=github&logoColor=f5f5f5)](https://github.com/open-geodata/pyFBDS)
4
+ [![PyPI - Version](https://img.shields.io/pypi/v/pyfbds?logo=pypi&label=PyPI&color=blue)](https://pypi.org/project/pyfbds/)<br>
5
+ [![Read the Docs](https://img.shields.io/readthedocs/pyFBDS?logo=ReadTheDocs&label=Read%20The%20Docs)](https://pyFBDS.readthedocs.io/)
6
+ [![Publish Python to PyPI](https://github.com/michelmetran/pyFBDS/actions/workflows/publish-to-pypipoetry.yml/badge.svg)](https://github.com/michelmetran/pyFBDS/actions/workflows/publish-to-pypipoetry.yml)
7
+
8
+ _Scripts_ para obter dados espaciais do [**repositório público de mapas e _shapefiles_ para _download_**](https://geo.fbds.org.br/). Veja mais na documentação:
9
+
10
+ > [https://pyFBDS.readthedocs.io/](https://pyFBDS.readthedocs.io/)
11
+
12
+ <br>
13
+
14
+ ---
15
+
16
+ ## Como Instalar?
17
+
18
+ ```shell
19
+ pip3 install pyFBDS
20
+ ```
21
+
22
+ <br>
23
+
24
+ ---
25
+
26
+ ## Como Usar?
27
+
28
+ ```shell
29
+ # Importa pacote
30
+ import pyFBDS
31
+
32
+ # Instancia Objeto
33
+ fbds = pyFBDS.Repo(output_path='.')
34
+
35
+ # Chama o método
36
+ fbds.get_municipio(id_ibge=353243)
37
+ ```
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyFBDS
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: requests>=2.32.5
9
+ Requires-Dist: requests-cache>=1.2.1
10
+ Requires-Dist: tqdm>=4.67.1
11
+ Requires-Dist: lxml>=6.0.0
12
+ Requires-Dist: ipywidgets>=8.1.8
13
+ Requires-Dist: aiohttp>=3.13.2
14
+ Requires-Dist: nest-asyncio>=1.6.0
15
+ Requires-Dist: pandas>=2.3.3
16
+ Requires-Dist: selenium>=4.38.0
17
+ Dynamic: license-file
18
+
19
+ # pyFBDS
20
+
21
+ [![Repo](https://img.shields.io/badge/GitHub-repo-blue?logo=github&logoColor=f5f5f5)](https://github.com/open-geodata/pyFBDS)
22
+ [![PyPI - Version](https://img.shields.io/pypi/v/pyfbds?logo=pypi&label=PyPI&color=blue)](https://pypi.org/project/pyfbds/)<br>
23
+ [![Read the Docs](https://img.shields.io/readthedocs/pyFBDS?logo=ReadTheDocs&label=Read%20The%20Docs)](https://pyFBDS.readthedocs.io/)
24
+ [![Publish Python to PyPI](https://github.com/michelmetran/pyFBDS/actions/workflows/publish-to-pypipoetry.yml/badge.svg)](https://github.com/michelmetran/pyFBDS/actions/workflows/publish-to-pypipoetry.yml)
25
+
26
+ _Scripts_ para obter dados espaciais do [**repositório público de mapas e _shapefiles_ para _download_**](https://geo.fbds.org.br/). Veja mais na documentação:
27
+
28
+ > [https://pyFBDS.readthedocs.io/](https://pyFBDS.readthedocs.io/)
29
+
30
+ <br>
31
+
32
+ ---
33
+
34
+ ## Como Instalar?
35
+
36
+ ```shell
37
+ pip3 install pyFBDS
38
+ ```
39
+
40
+ <br>
41
+
42
+ ---
43
+
44
+ ## Como Usar?
45
+
46
+ ```shell
47
+ # Importa pacote
48
+ import pyFBDS
49
+
50
+ # Instancia Objeto
51
+ fbds = pyFBDS.Repo(output_path='.')
52
+
53
+ # Chama o método
54
+ fbds.get_municipio(id_ibge=353243)
55
+ ```
@@ -0,0 +1,23 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ pyFBDS.egg-info/PKG-INFO
5
+ pyFBDS.egg-info/SOURCES.txt
6
+ pyFBDS.egg-info/dependency_links.txt
7
+ pyFBDS.egg-info/requires.txt
8
+ pyFBDS.egg-info/top_level.txt
9
+ pyFDBS/__init__.py
10
+ pyFDBS/requests/__init__.py
11
+ pyFDBS/requests/cache.py
12
+ pyFDBS/requests/download.py
13
+ pyFDBS/requests/logger.py
14
+ pyFDBS/requests/teste.py
15
+ pyFDBS/requests/web.py
16
+ pyFDBS/scraper/__init__.py
17
+ pyFDBS/scraper/page/__init__.py
18
+ pyFDBS/scraper/page/sss.py
19
+ pyFDBS/scraper/webdriver/__init__.py
20
+ pyFDBS/scraper/webdriver/chrome.py
21
+ pyFDBS/scraper/webdriver/config.py
22
+ pyFDBS/scraper/webdriver/firefox.py
23
+ pyFDBS/scraper/webdriver/gecko.py
@@ -0,0 +1,9 @@
1
+ requests>=2.32.5
2
+ requests-cache>=1.2.1
3
+ tqdm>=4.67.1
4
+ lxml>=6.0.0
5
+ ipywidgets>=8.1.8
6
+ aiohttp>=3.13.2
7
+ nest-asyncio>=1.6.0
8
+ pandas>=2.3.3
9
+ selenium>=4.38.0
@@ -0,0 +1 @@
1
+ pyFDBS
@@ -0,0 +1,4 @@
1
+ from .requests.download import download_files_parallel
2
+ from .requests.logger import FBDSLogger
3
+ from .requests.web import FBDS
4
+
File without changes
@@ -0,0 +1,42 @@
1
+ """
2
+ Para cache das requisições
3
+ """
4
+
5
+ from datetime import timedelta
6
+
7
+ import requests
8
+ import requests_cache
9
+
10
+ # Configuração do cache
11
+ requests_cache.install_cache(
12
+ cache_name='fbds_cache', # Nome do arquivo de cache
13
+ backend='sqlite', # Backend para armazenamento (SQLite)
14
+ expire_after=timedelta(days=3), # Cache expira após 7 dias
15
+ allowable_methods=('GET', 'POST'), # Métodos HTTP permitidos
16
+ )
17
+
18
+
19
+ def make_request(url):
20
+ """
21
+ Faz uma requisição HTTP com suporte a cache
22
+
23
+ Parameters:
24
+ -----------
25
+ url : str
26
+ URL para fazer a requisição
27
+
28
+ Returns:
29
+ --------
30
+ response : requests.Response
31
+ Resposta da requisição
32
+ is_cached : bool
33
+ Indica se a resposta veio do cache
34
+ """
35
+ response = requests.get(url)
36
+ is_cached = getattr(response, 'from_cache', False)
37
+
38
+ # Informação sobre o cache
39
+ # cache_status = 'CACHE' if is_cached else 'NOVA REQUISIÇÃO'
40
+ # print(f"{cache_status}: {url}")
41
+
42
+ return response, is_cached
@@ -0,0 +1,148 @@
1
+ """
2
+ Módulo para download dos dados usando asyncio
3
+ """
4
+
5
+ import asyncio
6
+ from pathlib import Path
7
+
8
+ import aiohttp
9
+ from tqdm.asyncio import tqdm_asyncio
10
+ from tqdm.notebook import tqdm
11
+
12
+ from .cache import make_request
13
+ from .logger import FBDSLogger
14
+
15
+
16
+ async def download_file_async(session, url_info, output_dir):
17
+ """
18
+ Download assíncrono de um único arquivo
19
+
20
+ Parameters:
21
+ -----------
22
+ session : aiohttp.ClientSession
23
+ Sessão HTTP assíncrona
24
+ url_info : dict
25
+ Dicionário com informações do arquivo (url, name, etc)
26
+ output_dir : str or Path
27
+ Diretório onde salvar o arquivo
28
+ """
29
+ try:
30
+ url = url_info['url']
31
+ # Remove o base URL e usa o caminho relativo
32
+ relative_path = url.replace('https://geo.fbds.org.br/', '')
33
+ output_path = Path(output_dir) / relative_path
34
+
35
+ # Cria o diretório se não existir
36
+ output_path.parent.mkdir(parents=True, exist_ok=True)
37
+
38
+ # Faz o download
39
+ async with session.get(url) as response:
40
+ if response.status == 200:
41
+ content = await response.read()
42
+
43
+ # Salva o arquivo
44
+ with open(output_path, 'wb') as f:
45
+ f.write(content)
46
+
47
+ result = {
48
+ 'nome': url_info['name'],
49
+ 'status': 'sucesso',
50
+ 'size': len(content),
51
+ }
52
+ else:
53
+ result = {
54
+ 'nome': url_info['name'],
55
+ 'status': 'erro',
56
+ 'erro': f'Status code: {response.status}',
57
+ }
58
+ except Exception as e:
59
+ result = {'nome': url_info['name'], 'status': 'erro', 'erro': str(e)}
60
+
61
+ return result
62
+
63
+
64
+ async def download_files_async(url_list, output_dir, max_concurrent=5):
65
+ """
66
+ Download assíncrono de múltiplos arquivos
67
+
68
+ Parameters:
69
+ -----------
70
+ url_list : list
71
+ Lista de dicionários com informações dos arquivos
72
+ output_dir : str or Path
73
+ Diretório onde salvar os arquivos
74
+ max_concurrent : int
75
+ Número máximo de downloads simultâneos
76
+ """
77
+ # Configura conexão com limite de conexões simultâneas
78
+ conn = aiohttp.TCPConnector(limit=max_concurrent)
79
+
80
+ async with aiohttp.ClientSession(connector=conn) as session:
81
+ # Cria a lista de tarefas
82
+ tasks = []
83
+ for url_info in url_list:
84
+ task = download_file_async(session, url_info, output_dir)
85
+ tasks.append(task)
86
+
87
+ # Executa as tasks com barra de progresso
88
+ results = await tqdm_asyncio.gather(
89
+ *tasks,
90
+ desc="Downloading files",
91
+ total=len(tasks),
92
+ ascii=True, # Melhor compatibilidade
93
+ mininterval=0.5, # Atualiza a cada 0.5 segundos
94
+ )
95
+
96
+ return results
97
+
98
+
99
+ def download_files_parallel(
100
+ url_list, output_dir, max_concurrent=5, logger=None
101
+ ):
102
+ """
103
+ Wrapper para executar o download assíncrono
104
+
105
+ Parameters:
106
+ -----------
107
+ url_list : list
108
+ Lista de dicionários com informações dos arquivos
109
+ output_dir : str or Path
110
+ Diretório onde salvar os arquivos
111
+ max_concurrent : int
112
+ Número máximo de downloads simultâneos
113
+ logger : FBDSLogger, optional
114
+ Logger existente para usar. Se None, cria um novo.
115
+ """
116
+ try:
117
+ # Usa o logger fornecido ou cria um novo
118
+ if logger is None:
119
+ logger = FBDSLogger()
120
+ logger.start_download_session()
121
+
122
+ # Pega o loop de eventos atual ou cria um novo se não existir
123
+ try:
124
+ loop = asyncio.get_event_loop()
125
+ except RuntimeError:
126
+ loop = asyncio.new_event_loop()
127
+ asyncio.set_event_loop(loop)
128
+
129
+ # Se estamos em um notebook IPython, use o nest_asyncio
130
+ try:
131
+ import nest_asyncio
132
+
133
+ nest_asyncio.apply()
134
+ except ImportError:
135
+ pass
136
+
137
+ # Executa o download assíncrono
138
+ results = loop.run_until_complete(
139
+ download_files_async(url_list, output_dir, max_concurrent)
140
+ )
141
+
142
+ # Analisa e registra os resultados
143
+ logger.analyze_results(results)
144
+ return results
145
+
146
+ except Exception as e:
147
+ logger.logger.error(f"Erro durante o download: {str(e)}")
148
+ return []
@@ -0,0 +1,168 @@
1
+ """
2
+ Sistema de logs para a aplicação FBDS
3
+ """
4
+
5
+ import json
6
+ import logging
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ # Obtém o diretório raiz do projeto
11
+ PROJECT_ROOT = Path(__file__).parent.parent
12
+ DEFAULT_LOG_DIR = PROJECT_ROOT / 'logs'
13
+
14
+
15
+ class FBDSLogger:
16
+ _instance = None
17
+ _initialized = False
18
+
19
+ def __new__(cls, log_dir=None, new_session=False):
20
+ # Verifica se já existe uma instância ou se foi pedida uma nova sessão
21
+ if cls._instance is None or new_session:
22
+ # Cria uma nova instância se não existir ou se new_session=True
23
+ cls._instance = super(FBDSLogger, cls).__new__(cls)
24
+ # Marca como não inicializado para forçar a execução do __init__
25
+ cls._initialized = False
26
+ # Retorna a instância (seja ela nova ou existente)
27
+ return cls._instance
28
+
29
+ def __init__(self, log_dir=None, new_session=False):
30
+ if not self._initialized or new_session:
31
+ # Usa o diretório fornecido ou o padrão
32
+ self.log_dir = Path(log_dir) if log_dir else DEFAULT_LOG_DIR
33
+ self.log_dir.mkdir(parents=True, exist_ok=True)
34
+
35
+ # Configura o logger principal
36
+ self.logger = logging.getLogger('FBDS')
37
+ self.logger.setLevel(logging.INFO)
38
+
39
+ # Remove handlers anteriores se existirem
40
+ for handler in self.logger.handlers[:]:
41
+ self.logger.removeHandler(handler)
42
+
43
+ # Cria handlers
44
+ self._setup_handlers()
45
+
46
+ # Dicionário para armazenar estatísticas
47
+ self.stats = {
48
+ 'total': 0,
49
+ 'success': 0,
50
+ 'errors': 0,
51
+ 'cached': 0,
52
+ 'start_time': None,
53
+ 'end_time': None,
54
+ 'errors_list': [],
55
+ }
56
+
57
+ self._initialized = True
58
+
59
+ def _setup_handlers(self):
60
+ # Handler para arquivo
61
+ # Usa apenas a data, não o timestamp completo
62
+ date_str = datetime.now().strftime('%Y%m%d')
63
+ self.log_file = self.log_dir / f'fbds_{date_str}.log'
64
+ # self.stats_file = self.log_dir / f'stats_{date_str}.json'
65
+
66
+ file_handler = logging.FileHandler(
67
+ self.log_file, encoding='utf-8', mode='a'
68
+ )
69
+ file_handler.setLevel(logging.INFO)
70
+
71
+ # Handler para console
72
+ console_handler = logging.StreamHandler()
73
+ console_handler.setLevel(logging.INFO)
74
+
75
+ # Formato do log
76
+ formatter = logging.Formatter(
77
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
78
+ datefmt='%Y-%m-%d %H:%M:%S',
79
+ )
80
+
81
+ file_handler.setFormatter(formatter)
82
+ console_handler.setFormatter(formatter)
83
+
84
+ # Adiciona handlers ao logger
85
+ self.logger.addHandler(file_handler)
86
+ self.logger.addHandler(console_handler)
87
+
88
+ def start_download_session(self):
89
+ """Inicia uma nova sessão de download"""
90
+ self.stats = {
91
+ 'total': 0,
92
+ 'success': 0,
93
+ 'errors': 0,
94
+ 'cached': 0,
95
+ 'start_time': datetime.now(),
96
+ 'end_time': None,
97
+ 'errors_list': [],
98
+ }
99
+ self.logger.info('Iniciando nova sessão de download')
100
+
101
+ def end_download_session(self):
102
+ """Finaliza a sessão de download e gera relatório"""
103
+ self.stats['end_time'] = datetime.now()
104
+ duration = self.stats['end_time'] - self.stats['start_time']
105
+
106
+ # Log do resumo
107
+ self.logger.info(f"=== Resumo da Sessão de Download ===")
108
+ self.logger.info(
109
+ f"Downloads com sucesso: {self.stats['success']} de {self.stats['total']}"
110
+ )
111
+ if self.stats['errors'] > 0:
112
+ self.logger.error(
113
+ f"Erros: {self.stats['errors']} de {self.stats['total']}"
114
+ )
115
+ # self.logger.info(
116
+ # f"Arquivos do cache: {self.stats['cached']} de {self.stats['total']}"
117
+ # )
118
+ self.logger.info(f"Duração total: {duration}")
119
+
120
+ # Se houver erros, registra eles
121
+ if self.stats['errors_list']:
122
+ self.logger.error("Erros encontrados:")
123
+ for error in self.stats['errors_list']:
124
+ self.logger.error(f"- {error['nome']}: {error['erro']}")
125
+
126
+ # # Salva as estatísticas em JSON
127
+ # stats_file = (
128
+ # self.log_dir
129
+ # / f'stats_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
130
+ # )
131
+ # with open(stats_file, 'w', encoding='utf-8') as f:
132
+ # # Converte datetime para string
133
+ # stats_dict = self.stats.copy()
134
+ # stats_dict['start_time'] = self.stats['start_time'].isoformat()
135
+ # stats_dict['end_time'] = self.stats['end_time'].isoformat()
136
+ # json.dump(stats_dict, f, ensure_ascii=False, indent=4)
137
+
138
+ def log_result(self, result):
139
+ """Registra o resultado de um download"""
140
+ self.stats['total'] += 1
141
+
142
+ if result.get('cached', False):
143
+ self.stats['cached'] += 1
144
+ self.logger.info(f"Arquivo em cache: {result['nome']}")
145
+
146
+ if result['status'] == 'sucesso':
147
+ self.stats['success'] += 1
148
+ self.logger.info(
149
+ f"Download concluído: {result['nome']} ({result['size']} bytes)"
150
+ )
151
+ else:
152
+ self.stats['errors'] += 1
153
+ self.stats['errors_list'].append(result)
154
+ self.logger.error(
155
+ f"Erro no download de {result['nome']}: {result['erro']}"
156
+ )
157
+
158
+ def analyze_results(self, results):
159
+ """
160
+ Analisa os resultados dos downloads
161
+ """
162
+ self.start_download_session()
163
+
164
+ for result in results:
165
+ self.log_result(result)
166
+
167
+ self.end_download_session()
168
+ return self.stats
@@ -0,0 +1,13 @@
1
+ # """
2
+ # Sistema de logs para a aplicação FBDS
3
+ # """
4
+
5
+ # import json
6
+ # import logging
7
+ # from datetime import datetime
8
+ # from pathlib import Path
9
+
10
+ # # Obtém o diretório raiz do projeto
11
+ # PROJECT_ROOT = Path(__file__).parent.parent
12
+ # DEFAULT_LOG_DIR = PROJECT_ROOT / 'logs'
13
+ # print(DEFAULT_LOG_DIR)
@@ -0,0 +1,78 @@
1
+ """
2
+ Summary
3
+ """
4
+
5
+ from pathlib import Path
6
+ from urllib.parse import urljoin
7
+
8
+ from lxml import html
9
+
10
+ from .cache import make_request
11
+
12
+
13
+ class FBDS:
14
+ def __init__(self) -> None:
15
+ self.url_base = "https://geo.fbds.org.br/"
16
+
17
+ def get_links(self, url, ignore_first):
18
+ # Usa a função make_request com cache
19
+ response, is_cached = make_request(url)
20
+ response.raise_for_status()
21
+ tree = html.fromstring(response.content)
22
+
23
+ list_folders = []
24
+ folders = tree.xpath("//tr")
25
+
26
+ # Ignora o primeiro tr, que é o cabeçalho
27
+ folders = folders[ignore_first:]
28
+
29
+ for folder in folders:
30
+ # Get Data
31
+ link = folder.xpath('.//td[@class="fb-n"]/a/@href')[0]
32
+ tipo = folder.xpath('.//td[@class="fb-i"]/img/@src')[0]
33
+ name = folder.xpath('.//td[@class="fb-n"]/a')[0].text.strip()
34
+ data = folder.xpath('.//td[@class="fb-d"]')[0].text.strip()
35
+ size = folder.xpath('.//td[@class="fb-s"]')[0].text.strip()
36
+
37
+ # Append to list
38
+ list_folders.append(
39
+ {
40
+ "url": urljoin(self.url_base, link),
41
+ "type": Path(tipo).stem,
42
+ "name": name,
43
+ "date": data,
44
+ "size": size,
45
+ }
46
+ )
47
+ return list_folders
48
+
49
+ def get_states(self) -> list[dict]:
50
+ """
51
+
52
+
53
+ :return: _description_
54
+ :rtype: list[dict]
55
+ """
56
+ return self.get_links(url=self.url_base, ignore_first=1)
57
+
58
+ def get_municipalities(self, uf):
59
+ state = self.get_state(uf=uf)
60
+ url = state["url"]
61
+ return self.get_links(url=url, ignore_first=2)
62
+
63
+ def get_layers(self, municipality, uf):
64
+ municipalitie = self.get_municipalitie(municipality=municipality, uf=uf)
65
+ url = municipalitie["url"]
66
+ return self.get_links(url=url, ignore_first=2)
67
+
68
+ def get_state(self, uf=None):
69
+ states = self.get_states()
70
+ return [x for x in states if x["name"] == uf][0]
71
+
72
+ def get_municipalitie(self, municipality=None, uf=None):
73
+ municipalities = self.get_municipalities(uf=uf)
74
+ return [x for x in municipalities if x["name"] == municipality][0]
75
+
76
+ def get_layer(self, municipality=None, uf=None, layer=None):
77
+ layers = self.get_layers(municipality=municipality, uf=uf)
78
+ return [x for x in layers if x["name"] == layer][0]
@@ -0,0 +1 @@
1
+ #from . import page, webdriver, search, outros, params
File without changes
File without changes
@@ -0,0 +1,2 @@
1
+ from .firefox import Firefox
2
+ from .chrome import Chrome