-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
315 lines (251 loc) · 9.81 KB
/
app.py
File metadata and controls
315 lines (251 loc) · 9.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
"""
title: 'Robô de Monitoramento Diário de Preço DestravaDev#3'
author: 'Elias Albuquerque'
version: 'Python 3.12.0'
created: '2024-07-25'
update: '2024-07-27'
"""
import logging.config
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import *
from time import sleep
import datetime
import os
import sys
import openpyxl
import schedule
import keyboard
def logging_settings():
"""
Configura o logging da aplicação utilizando o arquivo 'config.ini'.
"""
logging.config.fileConfig('config.ini', disable_existing_loggers=False)
logging.warning('Aplicação inicializada')
def driver_settings():
"""
Configura o driver do Chrome e o WebDriverWait para interação com o site.
Returns: driver, wait
"""
logging.info('Iniciando configurações da aplicação...')
try:
options = Options()
arguments = [
'--block-new-web-contents',
'--disable-notifications',
'--no-default-browser-check',
'--lang=pt-BR',
# '--headless',
'--window-position=36,68',
'--window-size=1100,750',]
for argument in arguments:
options.add_argument(argument)
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(
driver,
15,
poll_frequency=1,
ignored_exceptions=[
NoSuchElementException,
ElementNotVisibleException,
ElementNotSelectableException])
return driver, wait
except Exception as e:
logging.error(f'Erro na configuração do driver: {e}')
return None, None
def access_website(url, driver, wait):
"""
Esta função acessa um site especificado usando um driver de navegador.
Args: url (str), driver (webdriver), wait (int)
Returns: driver (webdriver)
"""
logging.info('Acessando o site (aguarde!)...')
try:
driver.get(url)
sleep(10)
driver.execute_script(f'document.body.style.zoom=".67"')
sleep(5)
return driver
except TimeoutException as e:
logging.error(f'Erro ao acessar o site {url}: {e}')
return None
except NoSuchElementException as e:
logging.error(f'Erro ao acessar o site {url}: Elemento não encontrado: {e}')
return None
except WebDriverException as e:
logging.error(f'Erro ao acessar o site {url}: {e}')
return None
def extract_product_value(driver, xpath_element):
"""
Extrai o valor do produto da página web usando JavaScript.
Args: driver (webdriver)
Returns: price_value (str)
"""
logging.info('Extraindo o valor do produto...')
try:
price_value = driver.execute_script(
"return document.querySelector(arguments[0]).getAttribute('content')",
xpath_element)
return price_value
except NoSuchElementException as e:
logging.error(f'Erro ao acessar o valor do produto: {str(e).split("\n")[0]}')
return None
except WebDriverException as e:
logging.error(f'Erro ao acessar o valor do produto: {str(e).split("\n")[0]}')
return None
finally:
driver.quit()
def process_data(data):
"""
Converte a string de dados para inteiro ou decimal.
Args: data (str)
Returns: data_value (int or float)
"""
if "." in data or "," in data:
data_value = float(data)
else:
data_value = int(data)
return data_value
def create_spreadsheet(spreadsheet_name, sheet_name, columns):
"""
Cria uma planilha Excel com colunas especificadas.
Args: spreadsheet_name (str), sheet_name (str), columns (list)
Returns: spreadsheet_name (str)
"""
if os.path.exists(spreadsheet_name):
return spreadsheet_name
try:
logging.info(f'Criando nova planilha "{spreadsheet_name}"...')
workbook = openpyxl.Workbook()
workbook.active.title = sheet_name
sheet = workbook.active
sheet.append(columns)
# Aplicando estilo da planilha
sheet.row_dimensions[1].height = 30
for col in range(1, len(columns) + 1):
sheet.column_dimensions[openpyxl.utils.get_column_letter(col)].width = 20
for row in sheet.iter_rows():
for cell in row:
cell.font = openpyxl.styles.Font(name='Calibri', size=13, bold=True)
cell.alignment = openpyxl.styles.Alignment(
horizontal='center', vertical='center')
cell.border = openpyxl.styles.Border(
top=openpyxl.styles.Side(style='thin'),
bottom=openpyxl.styles.Side(style='thin')) # Borda
workbook.save(spreadsheet_name)
return spreadsheet_name
except Exception as e:
logging.error(f'Erro ao criar a planilha: {e}')
return None
def generating_data_for_spreadsheet(product_name, price, url_link):
"""
Gera um dicionário com os dados do produto para inserir na planilha.
Args: product_name (str), price (int or float), url_link (str)
Returns: data_product (dict)
"""
data_product = {
'product': product_name,
'date': datetime.datetime.now().strftime('%d/%m/%y %H:%M:%S'),
'value': price,
'link': url_link
}
return data_product
def insert_data_into_spreadsheet(spreadsheet_name, data_to_insert):
"""
Insere os dados na planilha Excel.
Args: spreadsheet_name (str), data_to_insert (dict)
"""
logging.info(f'Inserindo dados na planilha...')
try:
workbook = openpyxl.load_workbook(spreadsheet_name)
sheet = workbook.active
row_num = sheet.max_row + 1
for col_num, value in enumerate(data_to_insert.values(), start=1):
if col_num == 4:
sheet.cell(row=row_num, column=col_num).hyperlink = value
sheet.cell(row=row_num, column=col_num).alignment = openpyxl.styles.Alignment(horizontal='center')
else:
sheet.cell(row=row_num, column=col_num).value = value
sheet.cell(row=row_num, column=col_num).alignment = openpyxl.styles.Alignment(horizontal='center')
workbook.save(spreadsheet_name)
except Exception as e:
logging.error(f'Erro ao inserir dados na planilha: {e}')
def schedule_application_execution(minutes=30):
"""
Agendar a execução da aplicação a cada intervalo de minutos definido.
Args: minutes (int): Intervalo de minutos para execução com valor padrão de 30 minutos.
"""
try:
logging.info(f'Agendando execução da aplicação para daqui à {minutes} minutos...')
def run_application():
try:
logging.info('Executando o script agendado...')
main()
except Exception as e:
logging.error(f'Erro durante a execução da aplicação: {e}')
finally:
schedule.every(minutes).minutes.do(run_application).tag('application')
# Cancelar qualquer agendamento anterior
schedule.clear('application')
# Agendar a execução inicial
schedule.every(minutes).minutes.do(run_application).tag('application')
# Obter o próximo horário da próxima execução
next_execution = schedule.next_run()
next_time_execution = next_execution.strftime('%H:%M:%S')
logging.info(f'A aplicação será executada às: {next_time_execution}')
print('\t - Mantenha pressionada a tecla:')
print('\t\t"ESC": Para interromper a execução')
print('\t\t"P" : Para abrir a planilha')
while True:
schedule.run_pending()
# Abrir a planilha
if keyboard.is_pressed('p'):
os.system('powershell -Command "start \'.\\Registro de preços.xlsx\'"')
# Interromper a execução
if keyboard.is_pressed('esc'):
schedule.clear('application')
logging.info("Script interrompido pelo usuário")
logging.info('Aplicação finalizada\n')
sys.exit()
sleep(1)
except Exception as e:
logging.error(f'Erro ao agendar execução da aplicação: {e}')
def main():
# url = 'https://bit.ly/tabS9ultra_int_value' # int value
url = 'https://bit.ly/tabS9ultra' # float value
xpath_element = 'meta[itemprop="price"]'
spreadsheet_name = 'Registro de preços.xlsx'
sheet_name = 'Produto'
columns = ['Produto', 'Data Atual', 'Valor', 'Link Produto']
product_name = 'Tab S9 Ultra'
# 0. Configurações da aplicação
logging_settings()
driver, wait = driver_settings()
# 1. Acessar o site
access_website(url, driver, wait)
if not driver:
return
# 2. Coletar o preço do produto
price_value = extract_product_value(driver, xpath_element)
if not price_value:
return
# 3. Tratar o dado para número inteiro ou decimal
price = process_data(price_value)
if not price:
return
# 4. Criar planilha com as colunas: Produto, Data Atual, Valor, Link Produto
spreadsheet_name = create_spreadsheet(spreadsheet_name, sheet_name, columns)
if not spreadsheet_name:
return
# 5. Inserir os dados na planilha e salvar
data_product = generating_data_for_spreadsheet(product_name, price, url)
insert_data_into_spreadsheet(spreadsheet_name, data_product)
# 6. Agendar o script para que execute a cada 30 min.
schedule_application_execution()
if __name__ == '__main__':
main()