ParaaAi commited on
Commit
9fc76d5
·
verified ·
1 Parent(s): 3e6c76e

Update stj.py

Browse files
Files changed (1) hide show
  1. stj.py +65 -156
stj.py CHANGED
@@ -1,7 +1,6 @@
1
  import asyncio
2
  import glob
3
  import os
4
- import subprocess
5
  import time
6
  from pathlib import Path
7
 
@@ -14,23 +13,21 @@ URL_TESAURO = "https://scon.stj.jus.br/SCON/thesaurus/"
14
  SCREENSHOT_FILE = "stj_tesauro_debug.png"
15
  ERRO_FILE = "stj_erro.png"
16
 
 
17
  BROWSER_ARGS = [
18
  "--disable-background-timer-throttling",
19
  "--disable-backgrounding-occluded-windows",
20
  "--disable-renderer-backgrounding",
21
  "--disable-component-update",
22
  "--no-sandbox",
23
- "--disable-setuid-sandbox",
24
  "--disable-dev-shm-usage",
25
  "--disable-gpu",
26
  "--no-zygote",
27
  "--lang=pt-BR",
28
  "--window-size=1366,900",
29
- # Reduz o "headless fingerprint" em ambientes container
30
  "--disable-blink-features=AutomationControlled",
31
  ]
32
 
33
-
34
  # ============================================================
35
  # UTILITÁRIOS
36
  # ============================================================
@@ -42,26 +39,9 @@ def safe_unlink(path: str) -> None:
42
  except OSError:
43
  pass
44
 
45
-
46
- def start_xvfb() -> None:
47
- """Sobe Xvfb no display :99 se ainda não estiver rodando."""
48
- try:
49
- subprocess.run(["pgrep", "-f", "Xvfb"], check=True, capture_output=True)
50
- print("🖥️ Xvfb já está rodando.")
51
- except subprocess.CalledProcessError:
52
- print("🖥️ Iniciando Xvfb...")
53
- subprocess.Popen(
54
- ["Xvfb", ":99", "-screen", "0", "1366x900x24"],
55
- stdout=subprocess.DEVNULL,
56
- stderr=subprocess.DEVNULL,
57
- )
58
- time.sleep(2)
59
- print("🖥️ Xvfb iniciado no display :99")
60
-
61
-
62
  def find_playwright_chrome() -> str:
63
- """Encontra o binário do Chrome na imagem oficial do Playwright."""
64
- print("🔎 Procurando Chrome em /ms-playwright...")
65
  patterns = [
66
  "/ms-playwright/chromium-*/chrome-linux/chrome",
67
  "/ms-playwright/chromium-*/chrome-linux64/chrome",
@@ -72,51 +52,24 @@ def find_playwright_chrome() -> str:
72
  path = sorted(matches)[-1]
73
  print(f"✅ Chrome encontrado em: {path}")
74
  return path
75
-
76
- home_cache = os.path.expanduser("~/.cache/ms-playwright")
77
- if os.path.isdir(home_cache):
78
- for pattern in (
79
- f"{home_cache}/chromium-*/chrome-linux/chrome",
80
- f"{home_cache}/chromium-*/chrome-linux64/chrome",
81
- ):
82
- matches = glob.glob(pattern)
83
- if matches:
84
- path = sorted(matches)[-1]
85
- print(f"✅ Chrome encontrado no cache: {path}")
86
- return path
87
-
88
- # Fallback: instaladores "normais"
89
- for alt in (
90
- "/usr/bin/google-chrome-stable",
91
- "/usr/bin/google-chrome",
92
- "/usr/bin/chromium",
93
- ):
94
  if os.path.isfile(alt):
95
  print(f"✅ Chrome encontrado em: {alt}")
96
  return alt
97
-
98
- raise FileNotFoundError("Nenhum binário do Chrome foi encontrado no container.")
99
-
100
 
101
  def is_page_not_found(body_text: str) -> bool:
102
- markers = ["Página não encontrada", "oops!", "Coordenadoria de Segurança e Defesa Cibernética"]
103
  return any(m.lower() in body_text.lower() for m in markers)
104
 
105
-
106
  def is_cloudflare_challenge(body_text: str, title: str) -> bool:
107
- markers = [
108
- "just a moment",
109
- "verify you are human",
110
- "checking your browser",
111
- "cloudflare",
112
- "verificação de segurança",
113
- ]
114
  haystack = f"{title}\n{body_text}".lower()
115
  return any(m in haystack for m in markers)
116
 
117
-
118
  async def get_page_state(tab) -> tuple[str, str]:
119
- """Retorna (body_text, title) da aba atual."""
120
  try:
121
  body = await tab.evaluate("document.body ? document.body.innerText : ''")
122
  except Exception:
@@ -127,122 +80,80 @@ async def get_page_state(tab) -> tuple[str, str]:
127
  title = ""
128
  return body or "", title or ""
129
 
130
-
131
- # ============================================================
132
- # BYPASS CLOUDFLARE — ETAPA 1: verify_cf nativo (OpenCV)
133
- # ============================================================
134
- async def try_native_verify_cf(tab, timeout: int = 45) -> bool:
135
- """
136
- Usa o verify_cf embutido do nodriver (template matching com OpenCV).
137
- Retorna True se a página deixou de mostrar challenge.
138
- """
139
- print("🛡️ [1/2] verify_cf nativo (OpenCV)...")
140
- start = time.time()
141
- while time.time() - start < timeout:
142
- body, title = await get_page_state(tab)
143
- if not is_cloudflare_challenge(body, title):
144
- print("✅ Sem challenge do Cloudflare (verify_cf nativo OK).")
145
- return True
146
-
147
- try:
148
- result = await tab.verify_cf()
149
- if result:
150
- print(f"🖱️ verify_cf reportou clique em: {result}")
151
- except Exception as e:
152
- # Erro clássico "cannot unpack non-iterable NoneType" quando não há checkbox
153
- print(f"⚠️ verify_cf: {e.__class__.__name__} (ignorado)")
154
-
155
- await tab.sleep(2)
156
-
157
- body, title = await get_page_state(tab)
158
- if not is_cloudflare_challenge(body, title):
159
- return True
160
- print("❌ verify_cf nativo não resolveu dentro do tempo.")
161
- return False
162
-
163
-
164
- # ============================================================
165
- # BYPASS CLOUDFLARE — ETAPA 2: CFVerify (fallback)
166
- # ============================================================
167
- async def try_cf_verify(tab, max_retries: int = 15) -> bool:
168
- """Fallback usando a biblioteca nodriver-cf-verify (se instalada)."""
169
- print("🛡️ [2/2] CFVerify (fallback)...")
170
- try:
171
- from nodriver_cf_verify import CFVerify
172
- except ImportError:
173
- print("⚠️ nodriver-cf-verify não instalado — pulando fallback.")
174
- return False
175
-
176
- try:
177
- verifier = CFVerify(_browser_tab=tab, _debug=True)
178
- success = await verifier.verify(
179
- _max_retries=max_retries,
180
- _interval_between_retries=1,
181
- _reload_page_after_n_retries=5,
182
- )
183
- return bool(success)
184
- except Exception as e:
185
- print(f"⚠️ CFVerify lançou exceção: {e}")
186
- return False
187
-
188
-
189
  # ============================================================
190
- # FLUXO PRINCIPAL
191
  # ============================================================
192
  async def resolver_cloudflare(tab) -> bool:
193
- """Tenta as duas estratégias em ordem até resolver."""
 
194
  body, title = await get_page_state(tab)
195
  if not is_cloudflare_challenge(body, title):
196
  print("🟢 Página já está limpa (sem Cloudflare).")
197
  return True
198
 
199
- if await try_native_verify_cf(tab):
200
- return True
201
-
202
- # Recarrega antes de tentar CFVerify, para começar do zero
203
- print("🔄 Recarregando antes do fallback...")
204
- try:
205
- await tab.reload()
206
- await tab.sleep(2)
207
- except Exception:
208
- pass
209
-
210
- if await try_cf_verify(tab):
211
- return True
212
 
 
213
  return False
214
 
215
-
 
 
216
  async def main() -> None:
217
- print("🚀 Iniciando nodriver + anti-Cloudflare...")
218
-
219
- # 1) Display virtual
220
- start_xvfb()
221
- os.environ["DISPLAY"] = ":99"
222
-
223
- # 2) Limpeza de artefatos
224
  safe_unlink(SCREENSHOT_FILE)
225
  safe_unlink(ERRO_FILE)
226
 
227
- browser = None
228
  try:
229
- # 3) Chrome dinâmico
230
  chrome_path = find_playwright_chrome()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
- # 4) Abre navegador (headful via Xvfb)
233
- browser = await uc.start(
234
- headless=False,
235
- no_sandbox=True,
236
- browser_executable_path=chrome_path,
237
- browser_args=BROWSER_ARGS,
238
- )
239
  tab = await browser.get(URL_TESAURO)
240
  await tab.sleep(3)
241
 
242
- # 5) Anti-Cloudflare
243
- ok = await resolver_cloudflare(tab)
244
- if not ok:
245
- print("❌ Não foi possível resolver o Cloudflare.")
246
  body, title = await get_page_state(tab)
247
  print(f" Título atual: {title!r}")
248
  try:
@@ -252,13 +163,12 @@ async def main() -> None:
252
  pass
253
  return
254
 
255
- # 6) Validação da página real
256
  await tab.sleep(1)
257
  body_text, title = await get_page_state(tab)
258
 
259
  if is_page_not_found(body_text):
260
  print("❌ A página retornada é um 404 do STJ.")
261
- print(f" URL: {URL_TESAURO}")
262
  return
263
 
264
  if "tesauro" not in body_text.lower() and "vocabulário" not in body_text.lower():
@@ -266,7 +176,7 @@ async def main() -> None:
266
  else:
267
  print("✅ Página do Tesauro carregada com sucesso!")
268
 
269
- # 7) Screenshot final
270
  try:
271
  await tab.save_screenshot(SCREENSHOT_FILE)
272
  print(f"📸 Screenshot salvo em: {os.path.abspath(SCREENSHOT_FILE)}")
@@ -276,12 +186,11 @@ async def main() -> None:
276
  print("═" * 50)
277
 
278
  except Exception as e:
279
- print(f"💥 Erro inesperado: {e}")
280
  finally:
281
  if browser:
282
  browser.stop()
283
  print("🛑 Navegador encerrado.")
284
 
285
-
286
  if __name__ == "__main__":
287
  uc.loop().run_until_complete(main())
 
1
  import asyncio
2
  import glob
3
  import os
 
4
  import time
5
  from pathlib import Path
6
 
 
13
  SCREENSHOT_FILE = "stj_tesauro_debug.png"
14
  ERRO_FILE = "stj_erro.png"
15
 
16
+ # Argumentos para ambiente de container. O modo headless já é ativado via parâmetro.
17
  BROWSER_ARGS = [
18
  "--disable-background-timer-throttling",
19
  "--disable-backgrounding-occluded-windows",
20
  "--disable-renderer-backgrounding",
21
  "--disable-component-update",
22
  "--no-sandbox",
 
23
  "--disable-dev-shm-usage",
24
  "--disable-gpu",
25
  "--no-zygote",
26
  "--lang=pt-BR",
27
  "--window-size=1366,900",
 
28
  "--disable-blink-features=AutomationControlled",
29
  ]
30
 
 
31
  # ============================================================
32
  # UTILITÁRIOS
33
  # ============================================================
 
39
  except OSError:
40
  pass
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def find_playwright_chrome() -> str:
43
+ """Localiza o binário do Chrome na imagem do Playwright ou no cache."""
44
+ print("🔎 Procurando Chrome...")
45
  patterns = [
46
  "/ms-playwright/chromium-*/chrome-linux/chrome",
47
  "/ms-playwright/chromium-*/chrome-linux64/chrome",
 
52
  path = sorted(matches)[-1]
53
  print(f"✅ Chrome encontrado em: {path}")
54
  return path
55
+ # Fallback para instalações padrão
56
+ for alt in ("/usr/bin/google-chrome-stable", "/usr/bin/google-chrome", "/usr/bin/chromium"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  if os.path.isfile(alt):
58
  print(f"✅ Chrome encontrado em: {alt}")
59
  return alt
60
+ raise FileNotFoundError("Nenhum binário do Chrome foi encontrado.")
 
 
61
 
62
  def is_page_not_found(body_text: str) -> bool:
63
+ markers = ["Página não encontrada", "oops!"]
64
  return any(m.lower() in body_text.lower() for m in markers)
65
 
 
66
  def is_cloudflare_challenge(body_text: str, title: str) -> bool:
67
+ markers = ["just a moment", "verify you are human", "checking your browser", "cloudflare"]
 
 
 
 
 
 
68
  haystack = f"{title}\n{body_text}".lower()
69
  return any(m in haystack for m in markers)
70
 
 
71
  async def get_page_state(tab) -> tuple[str, str]:
72
+ """Retorna (body_text, title) de forma segura."""
73
  try:
74
  body = await tab.evaluate("document.body ? document.body.innerText : ''")
75
  except Exception:
 
80
  title = ""
81
  return body or "", title or ""
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  # ============================================================
84
+ # BYPASS CLOUDFLARE (simplificado)
85
  # ============================================================
86
  async def resolver_cloudflare(tab) -> bool:
87
+ """Tenta resolver o desafio do Cloudflare com verify_cf nativo."""
88
+ print("🛡️ Tentando resolver Cloudflare (verify_cf nativo)...")
89
  body, title = await get_page_state(tab)
90
  if not is_cloudflare_challenge(body, title):
91
  print("🟢 Página já está limpa (sem Cloudflare).")
92
  return True
93
 
94
+ # Tenta o verify_cf nativo por algumas vezes
95
+ for i in range(10):
96
+ try:
97
+ # O erro NoneType é esperado quando não há checkbox; é ignorado.
98
+ await tab.verify_cf()
99
+ except Exception:
100
+ pass
101
+ await tab.sleep(3)
102
+ body, title = await get_page_state(tab)
103
+ if not is_cloudflare_challenge(body, title):
104
+ print("✅ Challenge do Cloudflare resolvido!")
105
+ return True
106
+ print(f"🔄 Tentativa {i+1}/10...")
107
 
108
+ print("❌ Não foi possível resolver o Cloudflare.")
109
  return False
110
 
111
+ # ============================================================
112
+ # FLUXO PRINCIPAL
113
+ # ============================================================
114
  async def main() -> None:
115
+ print("🚀 Iniciando nodriver (headless, retry)...")
 
 
 
 
 
 
116
  safe_unlink(SCREENSHOT_FILE)
117
  safe_unlink(ERRO_FILE)
118
 
119
+ # Localiza o Chrome antes de tentar iniciar
120
  try:
 
121
  chrome_path = find_playwright_chrome()
122
+ except FileNotFoundError as e:
123
+ print(f"💥 {e}")
124
+ return
125
+
126
+ browser = None
127
+ # Lógica de retry para contornar a falha de conexão inicial
128
+ for tentativa in range(1, 4):
129
+ print(f"🔄 Tentativa de inicialização {tentativa}/3...")
130
+ try:
131
+ browser = await uc.start(
132
+ headless=True, # Modo headless é mais estável em containers
133
+ sandbox=False, # Substitui no_sandbox=True
134
+ browser_executable_path=chrome_path,
135
+ browser_args=BROWSER_ARGS,
136
+ )
137
+ print("✅ Navegador iniciado com sucesso.")
138
+ break # Sai do loop se conectar
139
+ except Exception as e:
140
+ print(f"⚠️ Falha na tentativa {tentativa}: {e}")
141
+ if tentativa < 3:
142
+ print("⏳ Aguardando 3 segundos para tentar novamente...")
143
+ await asyncio.sleep(3)
144
+ else:
145
+ print("💥 Todas as tentativas de inicialização falharam.")
146
+ return
147
+
148
+ if not browser:
149
+ return
150
 
151
+ try:
 
 
 
 
 
 
152
  tab = await browser.get(URL_TESAURO)
153
  await tab.sleep(3)
154
 
155
+ # Tenta o bypass do Cloudflare
156
+ if not await resolver_cloudflare(tab):
 
 
157
  body, title = await get_page_state(tab)
158
  print(f" Título atual: {title!r}")
159
  try:
 
163
  pass
164
  return
165
 
166
+ # Valida se a página é a correta
167
  await tab.sleep(1)
168
  body_text, title = await get_page_state(tab)
169
 
170
  if is_page_not_found(body_text):
171
  print("❌ A página retornada é um 404 do STJ.")
 
172
  return
173
 
174
  if "tesauro" not in body_text.lower() and "vocabulário" not in body_text.lower():
 
176
  else:
177
  print("✅ Página do Tesauro carregada com sucesso!")
178
 
179
+ # Salva o screenshot final
180
  try:
181
  await tab.save_screenshot(SCREENSHOT_FILE)
182
  print(f"📸 Screenshot salvo em: {os.path.abspath(SCREENSHOT_FILE)}")
 
186
  print("═" * 50)
187
 
188
  except Exception as e:
189
+ print(f"💥 Erro inesperado durante a navegação: {e}")
190
  finally:
191
  if browser:
192
  browser.stop()
193
  print("🛑 Navegador encerrado.")
194
 
 
195
  if __name__ == "__main__":
196
  uc.loop().run_until_complete(main())