pyFBDS 0.1.0__py3-none-any.whl

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.
pyFDBS/__init__.py ADDED
@@ -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)
pyFDBS/requests/web.py ADDED
@@ -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
@@ -0,0 +1,295 @@
1
+ """
2
+ Módulo para usar driver do Chrome
3
+
4
+ Michel Metran
5
+ Data: 24.10.2024
6
+ Atualizado em: 24.10.2024
7
+ """
8
+
9
+ import tempfile
10
+ from pathlib import Path
11
+
12
+ from selenium import webdriver
13
+ from selenium.webdriver.chrome.options import Options as ChromeOptions
14
+
15
+ from . import config
16
+
17
+
18
+ class Chrome(webdriver.Chrome):
19
+ """
20
+ Cria driver customizado do Selenium
21
+
22
+ :param webdriver: _description_
23
+ :type webdriver: _type_
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ # driver_path: Path,
29
+ # logs_path: Path,
30
+ # down_path: Path,
31
+ *args,
32
+ **kwargs,
33
+ ):
34
+ """
35
+ - verify_ssl
36
+ - headless
37
+ - download_path
38
+ """
39
+ # Parameters
40
+ headless = kwargs.get('headless', False)
41
+ self.download_path = kwargs.get('download_path', False)
42
+ modo_colab = kwargs.get('modo_colab', False)
43
+
44
+ # Temp Path
45
+ temp_path = tempfile.gettempdir()
46
+ project_temp_path = Path(temp_path) / config.TEMP_PATH_NAME
47
+
48
+ # Scrapy Path
49
+ scrapy_path = project_temp_path / 'scrapy'
50
+ scrapy_path.mkdir(exist_ok=True, parents=True)
51
+
52
+ # Download Path
53
+ if self.download_path is False:
54
+ # Cria Pasta
55
+ self.download_path = scrapy_path / 'download'
56
+ self.download_path.mkdir(exist_ok=True, parents=True)
57
+
58
+ # my_service = ChromeService()
59
+ # print(str(self.download_path))
60
+ # print(self.download_path)
61
+ # print(self.download_path.is_dir())
62
+ # print(str(self.download_path) + os.path.sep)
63
+
64
+ # Options
65
+ options = ChromeOptions()
66
+ options.add_argument('--start-maximized')
67
+ options.add_argument('--disable-gpu')
68
+
69
+ # Se tem Modo Anônimo, o download não funciona adequadamente
70
+ # options.add_argument('--incognito')
71
+
72
+ # Certificados
73
+ options.add_argument('--ignore-certificate-errors-spki-list')
74
+ options.add_argument('--ignore-certificate-errors')
75
+ options.add_argument('--ignore-ssl-errors')
76
+
77
+ options.add_argument('--disable-infobars')
78
+ options.add_argument('--disable-extensions')
79
+ # options.add_argument('--disable-logging')
80
+ # Remove a mensagem "Chrome is being controlled by automated test software"
81
+ # que aparece quando o Chrome é iniciado pelo Selenium.
82
+ options.add_experimental_option(
83
+ 'excludeSwitches', ['enable-automation']
84
+ )
85
+ # Desativa a extensão de automação do Chrome que é carregada por
86
+ # padrão quando o Chrome é iniciado pelo Selenium.
87
+ # Isso ajuda a evitar que sites detectem que o navegador está
88
+ # sendo controlado por um script de automação.
89
+ options.add_experimental_option('useAutomationExtension', False)
90
+
91
+ options.add_experimental_option(
92
+ 'prefs',
93
+ {
94
+ 'credentials_enable_service': False,
95
+ 'profile.password_manager_enabled': False,
96
+ 'download.default_directory': str(self.download_path),
97
+ # + os.path.sep,
98
+ 'download.prompt_for_download': False,
99
+ 'download.directory_upgrade': True,
100
+ 'safebrowsing.enabled': True,
101
+ },
102
+ )
103
+
104
+ if headless is True:
105
+ options.add_argument('--headless')
106
+
107
+ if modo_colab is True:
108
+ options.add_argument('--headless')
109
+ options.add_argument('--no-sandbox')
110
+ options.add_argument('--disable-dev-shm-usage')
111
+
112
+ super().__init__(
113
+ # service=my_service,
114
+ options=options
115
+ )
116
+ self.maximize_window()
117
+
118
+
119
+ if __name__ == '__main__':
120
+ from pathlib import Path
121
+
122
+ import pyesaj.scraper as esaj
123
+ from selenium.webdriver.common.by import By
124
+
125
+ # # Credenciais
126
+ # load_dotenv()
127
+ # USERNAME = os.getenv('USERNAME_TJSP')
128
+ # PASSWORD = os.getenv('PASSWORD_TJSP')
129
+ # Instancia Driver
130
+ driver = esaj.webdriver.Chrome(headless=False)
131
+
132
+ driver.get(url='https://filesamples.com/formats/csv')
133
+
134
+ print('vai pra botão')
135
+ btn = driver.find_element(
136
+ By.XPATH, '//a[@href="/samples/document/csv/sample4.csv"]'
137
+ )
138
+ print('clica!')
139
+ btn.click()
140
+
141
+ # # Login
142
+ # log = esaj.page.Login(driver=driver)
143
+ # log.login(username=USERNAME, password=PASSWORD)
144
+
145
+ # # Intimações
146
+ # processo = '2336412-80.2024.8.26.0000'
147
+
148
+ # # Define
149
+ # intim_search = esaj.params.intim.input.ConsultaIntimacoes(
150
+ # em_nome_de='Ministério Público do Estado de São Paulo',
151
+ # instancia='Segundo Grau',
152
+ # # secao='Direito Criminal',
153
+ # # orgao_julgador='17ª Câmara de Direito Privado B',
154
+ # # especializacao='Criminal',
155
+ # # especializacao_nao_definida=True,
156
+ # # cargo='Secretaria ProcCriminal',
157
+ # # cargo_nao_definido=True,
158
+ # # assunto_principal='10527 - Livros / Jornais / Periódicos',
159
+ # area='Ambas',
160
+ # ciencia_ato='Todos',
161
+ # natureza_comunicacao='Ambas',
162
+ # situacao='Ambas',
163
+ # processo=processo,
164
+ # )
165
+
166
+ # dados = intim_search
167
+ # print(intim_search)
168
+
169
+ # # Vai para página
170
+ # esaj.page.intim.Consulta(driver=driver)
171
+
172
+ # # Atributo: Em Nome De
173
+ # if dados.em_nome_de is not None:
174
+ # intim_em_nome = esaj.page.intim.EmNomeDe(driver=driver)
175
+ # intim_em_nome.set_option(option=dados.em_nome_de)
176
+
177
+ # # Atributo: Tipo de Participação
178
+ # if dados.tipo_participacao is not None:
179
+ # intim_tipo = esaj.page.intim.TipoParticipacao(driver=driver)
180
+ # intim_tipo.set_option(option=dados.tipo_participacao)
181
+
182
+ # # Atributo: Instância
183
+ # if dados.instancia is not None:
184
+ # intim_inst = esaj.page.intim.Instancia(driver=driver)
185
+ # intim_inst.set_option(instancia=dados.instancia)
186
+
187
+ # # Atributo: Foro
188
+ # if dados.foro is not None:
189
+ # intim_foro = esaj.page.intim.Foro(driver=driver)
190
+ # intim_foro.set_option(option=dados.foro)
191
+
192
+ # # Atributo: Vara
193
+ # if dados.vara is not None:
194
+ # intim_vara = esaj.page.intim.Vara(driver=driver)
195
+ # intim_vara.set_option(option=dados.vara)
196
+
197
+ # # Atributo: Seção
198
+ # if dados.secao is not None:
199
+ # intim_secao = esaj.page.intim.Secao(driver=driver)
200
+ # intim_secao.set_option(option=dados.secao)
201
+
202
+ # # Atributo: Órgão Julgador
203
+ # if dados.orgao_julgador is not None:
204
+ # intim_org = esaj.page.intim.OrgaoJulgador(driver=driver)
205
+ # intim_org.set_option(option=dados.orgao_julgador)
206
+
207
+ # # Atributo: Especialização
208
+ # if dados.especializacao is not None:
209
+ # intim_esp = esaj.page.intim.Especializacao(driver=driver)
210
+ # intim_esp.set_option(option=dados.especializacao)
211
+
212
+ # # Atributo: Especialização Checkbox
213
+ # if dados.especializacao_nao_definida is True:
214
+ # intim_esp = esaj.page.intim.Especializacao(driver=driver)
215
+ # intim_esp.apenas_processos_sem_especializacao()
216
+
217
+ # # Atributo: Cargo
218
+ # if dados.cargo is not None:
219
+ # intim_cargo = esaj.page.intim.Cargo(driver=driver)
220
+ # intim_cargo.set_option(option=dados.cargo)
221
+
222
+ # # Atributo: Cargo Checkbox
223
+ # if dados.especializacao_nao_definida is True:
224
+ # intim_cargo = esaj.page.intim.Cargo(driver=driver)
225
+ # intim_cargo.apenas_processos_sem_cargo()
226
+
227
+ # # Atributo: Classe
228
+ # if dados.classe is not None:
229
+ # intim_classe = esaj.page.intim.Classe(driver=driver)
230
+ # intim_classe.set_option(option=dados.classe)
231
+
232
+ # # Atributo: Assunto Principal
233
+ # if dados.assunto_principal is not None:
234
+ # intim_assunto = esaj.page.intim.AssuntoPrincipal(driver=driver)
235
+ # intim_assunto.set_option(option=dados.assunto_principal)
236
+
237
+ # # Atributo: Área
238
+ # if dados.area is not None:
239
+ # intim_area = esaj.page.intim.Area(driver=driver)
240
+ # intim_area.set_option(option=dados.area)
241
+
242
+ # # Período: De
243
+ # if dados.periodo_de is not None:
244
+ # intim_per = esaj.page.intim.consulta.Periodo(driver=driver)
245
+ # intim_per.de(data=dados.periodo_de)
246
+
247
+ # # Período: Até
248
+ # if dados.periodo_ate is not None:
249
+ # intim_per = esaj.page.intim.consulta.Periodo(driver=driver)
250
+ # intim_per.ate(data=dados.periodo_ate)
251
+
252
+ # # Período: Intervalo
253
+ # if dados.periodo_de is not None and dados.periodo_ate is not None:
254
+ # intim_per = esaj.page.intim.consulta.Periodo(driver=driver)
255
+ # intim_per.define_intervalo(de=dados.periodo_de, ate=dados.periodo_ate)
256
+
257
+ # # Atributo: Processo
258
+ # if dados.processo is not None:
259
+ # intim_proc = esaj.page.intim.Processo(driver=driver)
260
+ # intim_proc.write(texto=dados.processo)
261
+
262
+ # # Atributo: Ciência do Ato
263
+ # if dados.ciencia_ato is not None:
264
+ # intim_cien = esaj.page.intim.consulta.CienciaAto(driver=driver)
265
+ # intim_cien.set_option(option=dados.ciencia_ato)
266
+
267
+ # # Atributo: Natureza de Comunicação
268
+ # if dados.natureza_comunicacao is not None:
269
+ # intim_nat = esaj.page.intim.NaturezaComunicacao(driver=driver)
270
+ # intim_nat.set_option(natureza=dados.natureza_comunicacao)
271
+
272
+ # # Atributo: Situação
273
+ # if dados.situacao is not None:
274
+ # intim_sit = esaj.page.intim.consulta.Situacao(driver=driver)
275
+ # intim_sit.set_option(situacao=dados.situacao)
276
+
277
+ # # Aguarda
278
+ # time.sleep(2)
279
+
280
+ # # Consulta
281
+ # consulta = esaj.page.intim.ConsultarIntimacoes(driver=driver)
282
+ # consulta.consultar()
283
+
284
+ # # Tabela
285
+ # tab = esaj.page.intim.Tabela(driver=driver)
286
+ # tab.get_table(
287
+ # # instancia=instancia,
288
+ # seleciona_processos=[processo],
289
+ # )
290
+
291
+ # acao = esaj.page.intim.Acoes(driver=driver)
292
+ # csv_filepath = acao.export_csv()
293
+ # print(csv_filepath)
294
+
295
+ # driver.quit()
@@ -0,0 +1,15 @@
1
+ """
2
+ Módulo com variáveis sobre a definição do driver
3
+ E extensões do driver.
4
+ """
5
+
6
+ # Nome da pasta que ficará dentro da pasta temp.
7
+ # Nessa pasta serão armazenados logs e drivers do Selenium
8
+ TEMP_PATH_NAME = 'fbds'
9
+
10
+ # Url do GeckoDriver
11
+ URL_GECKODRIVER_WINDOWS = 'https://github.com/mozilla/geckodriver/releases/download/v0.32.0/geckodriver-v0.32.0-win64.zip'
12
+ URL_GECKODRIVER_LINUX = 'https://github.com/mozilla/geckodriver/releases/download/v0.35.0/geckodriver-v0.35.0-linux64.tar.gz'
13
+
14
+ # URLs da Extensão xPath
15
+ URL_ADDONS_XPATH = 'https://addons.mozilla.org/firefox/downloads/file/3588871/xpath_finder-1.0.2-fx.xpi'
@@ -0,0 +1,174 @@
1
+ """
2
+ Módulo para usar driver do FireFox
3
+
4
+ Michel Metran
5
+ Data: mar.2023
6
+ Atualizado em: mar.2023
7
+ """
8
+
9
+ import tempfile
10
+ from pathlib import Path
11
+
12
+ import requests
13
+ from selenium import webdriver
14
+ from selenium.webdriver.firefox.options import Options as FirefoxOptions
15
+ from selenium.webdriver.firefox.service import Service as FirefoxService
16
+
17
+ from . import config, gecko
18
+
19
+
20
+ class Firefox(webdriver.Firefox):
21
+ """
22
+ Cria driver customizado do Selenium
23
+
24
+ :param webdriver: _description_
25
+ :type webdriver: _type_
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ # driver_path: Path,
31
+ # logs_path: Path,
32
+ # down_path: Path,
33
+ *args,
34
+ **kwargs,
35
+ ):
36
+ """
37
+ - verify_ssl
38
+ - headless
39
+ - download_path
40
+
41
+ :param my_driver_path: _description_
42
+ :type my_driver_path: pathlib
43
+ :param my_logs_path: _description_
44
+ :type my_logs_path: pathlib
45
+ """
46
+
47
+ # Parameters
48
+ verify_ssl = kwargs.get('verify_ssl', True)
49
+ headless = kwargs.get('headless', False)
50
+ self.download_path = kwargs.get('download_path', False)
51
+
52
+ # Temp Path
53
+ temp_path = tempfile.gettempdir()
54
+ project_temp_path = Path(temp_path) / config.TEMP_PATH_NAME
55
+
56
+ # Scrapy Path
57
+ scrapy_path = project_temp_path / 'scrapy'
58
+ scrapy_path.mkdir(exist_ok=True, parents=True)
59
+
60
+ # Driver Path
61
+ self.driver_path = scrapy_path / 'driver'
62
+ self.driver_path.mkdir(exist_ok=True, parents=True)
63
+
64
+ # Logs Path
65
+ self.logs_path = scrapy_path / 'logs'
66
+ self.logs_path.mkdir(exist_ok=True, parents=True)
67
+
68
+ # Services
69
+ geckodriver = gecko.Gecko()
70
+ gecko_path = geckodriver.get_path_geckodriver(verify_ssl=verify_ssl)
71
+
72
+ # Logs
73
+ logs_filepath = self.logs_path / 'geckodriver.log'
74
+
75
+ # Services
76
+ my_service = FirefoxService(
77
+ executable_path=gecko_path, log_path=logs_filepath
78
+ )
79
+
80
+ # Options
81
+ my_options = FirefoxOptions()
82
+ if headless:
83
+ my_options.add_argument('--headless')
84
+
85
+ # Download Path
86
+ if self.download_path is False:
87
+ # Cria Pasta
88
+ self.download_path = scrapy_path / 'download'
89
+ self.download_path.mkdir(exist_ok=True, parents=True)
90
+
91
+ # Define pasta de Download
92
+ my_options.set_preference(
93
+ 'browser.download.dir', str(self.download_path)
94
+ )
95
+
96
+ my_options.set_preference('intl.accept_languages', 'pt-BR, pt')
97
+ my_options.set_preference('browser.download.folderList', 2)
98
+ my_options.set_preference(
99
+ 'browser.download.manager.showWhenStarting', False
100
+ )
101
+ my_options.set_preference('pdfjs.disabled', True)
102
+ my_options.set_preference('plugin.scan.Acrobat', '99.0')
103
+ my_options.set_preference('plugin.scan.plid.all', False)
104
+ my_options.set_preference(
105
+ 'browser.helperApps.showOpenOptionForPdfJS', False
106
+ )
107
+ my_options.set_preference('browser.download.forbid_open_with', True)
108
+ my_options.set_preference(
109
+ 'browser.helperApps.neverAsk.saveToDisk',
110
+ 'application/octet-stream, application/pdf',
111
+ )
112
+ # Quando é necessário fazer repost about: config
113
+ # https://superuser.com/questions/1410598/disable-firefox-must-send-information-that-will-repeat-any-action-dialog-box
114
+ my_options.set_preference(
115
+ 'dom.confirm_repost.testing.always_accept', True
116
+ )
117
+
118
+ # Driver
119
+ # my_driver = super(Driver, self)
120
+ # my_driver.__init__(service=my_service, options=my_options)
121
+ super().__init__(service=my_service, options=my_options)
122
+ self.maximize_window()
123
+
124
+ def add_extension_xpath(self) -> None:
125
+ """
126
+ Adiciona xPath extension
127
+ """
128
+
129
+ # Temp Path
130
+ temp_path = tempfile.gettempdir()
131
+ project_temp_path = Path(temp_path) / config.TEMP_PATH_NAME
132
+
133
+ # Scrapy Path
134
+ scrapy_path = project_temp_path / 'scrapy'
135
+ scrapy_path.mkdir(exist_ok=True, parents=True)
136
+
137
+ # Adds Path
138
+ adds_path = scrapy_path / 'adds'
139
+ adds_path.mkdir(exist_ok=True, parents=True)
140
+
141
+ # Add-ons Xpath
142
+ xpath_path = adds_path / 'xpath.xpi'
143
+ xpath_path = xpath_path.absolute().resolve()
144
+
145
+ # Download
146
+ if not xpath_path.is_file():
147
+ r = requests.get(
148
+ url=config.URL_ADDONS_XPATH, timeout=60, verify=False
149
+ )
150
+
151
+ with open(xpath_path, 'wb') as f:
152
+ f.write(r.content)
153
+
154
+ # Add
155
+ self.install_addon(str(xpath_path), temporary=True)
156
+
157
+
158
+ if __name__ == '__main__':
159
+ import time
160
+
161
+ # from pyesaj import paths
162
+ # Instancia Driver
163
+ driver = Firefox(headless=False, verify_ssl=False, )
164
+
165
+ # Add xPath Extension
166
+ driver.add_extension_xpath()
167
+
168
+ driver.get(url='https://www.uol.com.br/')
169
+ time.sleep(3)
170
+ driver.quit()
171
+
172
+ gecko = gecko.Gecko()
173
+ gecko_p = gecko.get_path_geckodriver()
174
+ print(gecko_p)
@@ -0,0 +1,151 @@
1
+ """
2
+ _summary_
3
+
4
+ :raises Exception: _description_
5
+ :return: _description_
6
+ :rtype: _type_
7
+ """
8
+
9
+ import platform
10
+ import tarfile
11
+ import tempfile
12
+ from pathlib import Path
13
+ from zipfile import ZipFile
14
+
15
+ import requests
16
+
17
+ from . import config
18
+
19
+
20
+ class Gecko:
21
+ """
22
+ _summary_
23
+ """
24
+
25
+ def __init__(self) -> None:
26
+ """
27
+ _summary_
28
+
29
+ :return: _description_
30
+ :rtype: _type_
31
+ """
32
+
33
+ # Temp Path
34
+ temp_path = tempfile.gettempdir()
35
+ project_temp_path = Path(temp_path) / config.TEMP_PATH_NAME
36
+
37
+ # Scrapy Path
38
+ scrapy_path = project_temp_path / 'scrapy'
39
+ scrapy_path.mkdir(exist_ok=True, parents=True)
40
+
41
+ # Driver Path
42
+ self.driver_path = scrapy_path / 'driver'
43
+ self.driver_path.mkdir(exist_ok=True, parents=True)
44
+
45
+ # Drivers por Sistema Operacional
46
+ if platform.system() == 'Windows':
47
+ gecko_win_filepath = self.driver_path / 'geckodriver.exe'
48
+ if gecko_win_filepath.is_file():
49
+ self.has_geckodriver = True
50
+
51
+ elif not gecko_win_filepath.is_file():
52
+ self.has_geckodriver = False
53
+
54
+ elif platform.system() == 'Linux':
55
+ gecko_linux_filepath = self.driver_path / 'geckodriver'
56
+ if gecko_linux_filepath.is_file():
57
+ self.has_geckodriver = True
58
+
59
+ elif not gecko_linux_filepath.is_file():
60
+ self.has_geckodriver = False
61
+
62
+ def _get_geckodriver(self, verify_ssl: bool):
63
+ """
64
+ Faz o download do geckodriver!
65
+
66
+ :return:
67
+ """
68
+
69
+ # print(gecko_zip_filepath)
70
+ if not self.has_geckodriver:
71
+ if platform.system() == 'Windows':
72
+ # Download do geckodriver
73
+ r = requests.get(
74
+ config.URL_GECKODRIVER_WINDOWS,
75
+ timeout=60,
76
+ verify=verify_ssl,
77
+ )
78
+
79
+ # Save
80
+ name_gecko = Path(config.URL_GECKODRIVER_WINDOWS).name
81
+ gecko_zip_filepath = self.driver_path / name_gecko
82
+ with open(gecko_zip_filepath, 'wb') as f:
83
+ f.write(r.content)
84
+
85
+ # Extract
86
+ with ZipFile(gecko_zip_filepath, 'r') as zip_ref:
87
+ zip_ref.extractall(self.driver_path)
88
+
89
+ elif platform.system() == 'Linux':
90
+ # Download do geckodriver
91
+ r = requests.get(
92
+ config.URL_GECKODRIVER_LINUX, timeout=60, verify=verify_ssl
93
+ )
94
+
95
+ # Save
96
+ name_gecko = Path(config.URL_GECKODRIVER_LINUX).name
97
+ gecko_zip_filepath = self.driver_path / name_gecko
98
+ with open(gecko_zip_filepath, 'wb') as f:
99
+ f.write(r.content)
100
+
101
+ # Extract
102
+ with tarfile.open(gecko_zip_filepath, 'r') as tar_ref:
103
+ tar_ref.extractall(self.driver_path)
104
+
105
+ elif self.has_geckodriver:
106
+ print(f'Geckodriver already in {self.driver_path}')
107
+
108
+ def get_path_geckodriver(self, verify_ssl: bool = False) -> Path | str:
109
+ """
110
+ dddd
111
+ """
112
+
113
+ # Faz download se for necessário
114
+ self._get_geckodriver(verify_ssl=verify_ssl)
115
+
116
+ # Path
117
+ if platform.system() == 'Windows':
118
+ _gecko_path = self.driver_path / 'geckodriver.exe'
119
+ _gecko_path = _gecko_path.resolve().as_posix().replace('/', '\\')
120
+ return _gecko_path
121
+
122
+ elif platform.system() == 'Linux':
123
+ _gecko_path = self.driver_path / 'geckodriver'
124
+ return _gecko_path
125
+
126
+ else:
127
+ raise Exception(f'Ajustar para plataforma {platform.system()}')
128
+
129
+
130
+ if __name__ == '__main__':
131
+ import time
132
+
133
+ from pyesaj import webdriver
134
+
135
+ # Instancia Driver
136
+ driver = webdriver.Firefox(
137
+ # driver_path=paths.driver_path,
138
+ # logs_path=paths.app_logs_path,
139
+ # down_path=paths.driver_path,
140
+ headless=False,
141
+ verify_ssl=False,
142
+ )
143
+ # Add xPath Extension
144
+ driver.add_extension_xpath()
145
+ driver.get(url='https://www.uol.com.br/')
146
+ time.sleep(3)
147
+ driver.quit()
148
+
149
+ gecko = Gecko()
150
+ gecko_path = gecko.get_path_geckodriver()
151
+ print(gecko_path)
@@ -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,20 @@
1
+ pyFDBS/__init__.py,sha256=zUuCifHMhjcfy-5UfEK8FqJBqyRVpObhZzZFfUn7080,127
2
+ pyFDBS/requests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ pyFDBS/requests/cache.py,sha256=9fxhr62qOzqH-3yfMWgxWFfwJRRTLD09YzWhHDAmLhQ,1009
4
+ pyFDBS/requests/download.py,sha256=mps8mzvG8Dc0Rmlr0YLVv9bdtTLGbKTH3upRKDknWZ4,4324
5
+ pyFDBS/requests/logger.py,sha256=jtR2tzII8WXTJSliKsMOA005YVqQX--fkNI5hmRvYMs,5807
6
+ pyFDBS/requests/teste.py,sha256=H9U5Crhg_bHJfLrU2gHSNGhDi6xgdGD21t-GVPcAznA,298
7
+ pyFDBS/requests/web.py,sha256=PqRRd2ty6zMAXfyV_g6m2V2GaLBNmhYYPY41Zug-RuY,2451
8
+ pyFDBS/scraper/__init__.py,sha256=hY_8LuHGs7u4O4sEdLJ7l4jaBmgh2bwrnUxx85g_7W4,55
9
+ pyFDBS/scraper/page/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ pyFDBS/scraper/page/sss.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ pyFDBS/scraper/webdriver/__init__.py,sha256=n0Hqj0eP5RBQicZ2sBt7A8JSNNPBWR7vAq9sWoMbsfY,56
12
+ pyFDBS/scraper/webdriver/chrome.py,sha256=ZchR5qgWNZDb0tcVJifdnxGhQtXp3CyOE_YZ_hULegk,9775
13
+ pyFDBS/scraper/webdriver/config.py,sha256=ebHxrHMNjQRQtXRqqevRlU0yxg37i5zj3d7_nj3gCTw,623
14
+ pyFDBS/scraper/webdriver/firefox.py,sha256=3KJM6Ud2iGRNav5Xv0UdDn-6mhKJKqL6jD3Lqzl2v_4,5088
15
+ pyFDBS/scraper/webdriver/gecko.py,sha256=75aOODWkfV8WWaUGhdyYrSOTzMmRDbtf34so82AU7tU,4263
16
+ pyfbds-0.1.0.dist-info/licenses/LICENSE,sha256=_iz4azhfrEKs8_YrVR8otmznlfBagqvUygzZuV7FFjM,1090
17
+ pyfbds-0.1.0.dist-info/METADATA,sha256=mH5GYdbCMxvZ5C2SqRibi2-2fKVqojSc6oWe9snlZ10,1561
18
+ pyfbds-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
19
+ pyfbds-0.1.0.dist-info/top_level.txt,sha256=OlAFr-ScJuI1s1bYukiWsBvf2_SUErG5GUFYNXxsrlM,7
20
+ pyfbds-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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.
@@ -0,0 +1 @@
1
+ pyFDBS