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 +4 -0
- pyFDBS/requests/__init__.py +0 -0
- pyFDBS/requests/cache.py +42 -0
- pyFDBS/requests/download.py +148 -0
- pyFDBS/requests/logger.py +168 -0
- pyFDBS/requests/teste.py +13 -0
- pyFDBS/requests/web.py +78 -0
- pyFDBS/scraper/__init__.py +1 -0
- pyFDBS/scraper/page/__init__.py +0 -0
- pyFDBS/scraper/page/sss.py +0 -0
- pyFDBS/scraper/webdriver/__init__.py +2 -0
- pyFDBS/scraper/webdriver/chrome.py +295 -0
- pyFDBS/scraper/webdriver/config.py +15 -0
- pyFDBS/scraper/webdriver/firefox.py +174 -0
- pyFDBS/scraper/webdriver/gecko.py +151 -0
- pyfbds-0.1.0.dist-info/METADATA +55 -0
- pyfbds-0.1.0.dist-info/RECORD +20 -0
- pyfbds-0.1.0.dist-info/WHEEL +5 -0
- pyfbds-0.1.0.dist-info/licenses/LICENSE +21 -0
- pyfbds-0.1.0.dist-info/top_level.txt +1 -0
pyFDBS/__init__.py
ADDED
|
File without changes
|
pyFDBS/requests/cache.py
ADDED
|
@@ -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
|
pyFDBS/requests/teste.py
ADDED
|
@@ -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,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
|
+
[](https://github.com/open-geodata/pyFBDS)
|
|
22
|
+
[](https://pypi.org/project/pyfbds/)<br>
|
|
23
|
+
[](https://pyFBDS.readthedocs.io/)
|
|
24
|
+
[](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,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
|