prince1604 commited on
Commit
503011f
·
1 Parent(s): f2e524e

Update Crawler: Add stealth args and challenge detection for Cloudflare

Browse files
Files changed (1) hide show
  1. src/crawler.py +59 -14
src/crawler.py CHANGED
@@ -148,7 +148,7 @@ class Crawler:
148
 
149
  def _fetch_playwright(self, url):
150
  """
151
- Fallback Strategy A: Use Playwright to render full page.
152
  """
153
  try:
154
  from playwright.sync_api import sync_playwright
@@ -160,54 +160,99 @@ class Crawler:
160
  # Acquire semaphore to prevent resource exhaustion
161
  with self.playwright_semaphore:
162
  with sync_playwright() as p:
163
- # Launch options
164
- # headless=False can be useful for visual debugging if needed, but we keep True for automation.
165
- browser = p.chromium.launch(headless=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  # Context with realistic viewport and locale
168
  context = browser.new_context(
169
  viewport={'width': 1920, 'height': 1080},
170
- user_agent=self.ua.random,
171
  locale="en-US",
172
  timezone_id="America/New_York",
173
- java_script_enabled=True
 
 
174
  )
 
 
 
 
 
 
 
175
 
176
  page = context.new_page()
177
 
178
  # BLOCK RESOURCES for speed: images, fonts, css
179
- page.route("**/*.{png,jpg,jpeg,gif,svg,css,woff,woff2,ttf,otf}", lambda route: route.abort())
 
 
180
 
181
  try:
182
  logger.info(f"Playwright navigating to {url}...")
183
- page.goto(url, timeout=60000, wait_until="domcontentloaded")
184
 
185
- # Wait for a bit of hydration (Optimized for speed)
186
- page.wait_for_timeout(500)
187
 
188
- # Log page title
189
  title = page.title()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  logger.info(f"Page Title: {title}")
191
 
192
- content = page.content()
 
193
 
194
  # Explicit cleanup
195
  page.close()
196
  context.close()
197
  browser.close()
198
 
199
- return content
200
 
201
  except Exception as nav:
202
  logger.error(f"Playwright navigation error: {nav}")
203
  return None
204
  finally:
205
- # Ensure browser is closed even if errors occur
206
  try:
207
  browser.close()
208
  except:
209
  pass
210
 
 
211
  except Exception as e:
212
  logger.error(f"Playwright critical error: {e}")
213
  return None
 
148
 
149
  def _fetch_playwright(self, url):
150
  """
151
+ Fallback Strategy A: Use Playwright to render full page with Stealth Mode.
152
  """
153
  try:
154
  from playwright.sync_api import sync_playwright
 
160
  # Acquire semaphore to prevent resource exhaustion
161
  with self.playwright_semaphore:
162
  with sync_playwright() as p:
163
+ # Stealth Args to bypass basic detection
164
+ # We keep headless=True for performance, but mask it
165
+ args = [
166
+ "--disable-blink-features=AutomationControlled",
167
+ "--no-sandbox",
168
+ "--disable-setuid-sandbox",
169
+ "--disable-dev-shm-usage",
170
+ "--disable-accelerated-2d-canvas",
171
+ "--no-first-run",
172
+ "--no-zygote",
173
+ "--disable-gpu",
174
+ "--hide-scrollbars",
175
+ "--mute-audio",
176
+ ]
177
+
178
+ browser = p.chromium.launch(headless=True, args=args)
179
 
180
  # Context with realistic viewport and locale
181
  context = browser.new_context(
182
  viewport={'width': 1920, 'height': 1080},
183
+ user_agent=self.ua.random, # Rotate UA
184
  locale="en-US",
185
  timezone_id="America/New_York",
186
+ java_script_enabled=True,
187
+ has_touch=True,
188
+ is_mobile=False
189
  )
190
+
191
+ # --- KEY STEALTH INJECTION ---
192
+ # Override navigator.webdriver to undefined
193
+ context.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
194
+ # mask chrome runtime
195
+ context.add_init_script("window.chrome = { runtime: {} };")
196
+ # -----------------------------
197
 
198
  page = context.new_page()
199
 
200
  # BLOCK RESOURCES for speed: images, fonts, css
201
+ # CAREFUL: Some WAFs check if CSS loads. If blocking persists, disable this.
202
+ # For now, we block images/fonts but keep CSS/JS
203
+ page.route("**/*.{png,jpg,jpeg,gif,svg,woff,woff2,ttf,otf}", lambda route: route.abort())
204
 
205
  try:
206
  logger.info(f"Playwright navigating to {url}...")
207
+ page.goto(url, timeout=90000, wait_until="domcontentloaded")
208
 
209
+ # Wait for hydration
210
+ page.wait_for_timeout(2000)
211
 
212
+ # --- Cloudflare / WAF Detection & Handling ---
213
  title = page.title()
214
+ content = page.content().lower()
215
+
216
+ retries = 0
217
+ while retries < 2 and ("just a moment..." in title.lower() or "challenge" in title.lower() or "verify you are human" in content):
218
+ logger.warning(f"Detected Cloudflare Challenge ({title}). Waiting for 15s...")
219
+ # Simulate mouse movement
220
+ try:
221
+ page.mouse.move(100, 100)
222
+ page.mouse.down()
223
+ page.mouse.up()
224
+ page.mouse.move(200, 200)
225
+ except:
226
+ pass
227
+
228
+ page.wait_for_timeout(15000)
229
+ title = page.title()
230
+ content = page.content().lower()
231
+ retries += 1
232
+ # ---------------------------------------------
233
+
234
  logger.info(f"Page Title: {title}")
235
 
236
+ # Final content grab
237
+ final_content = page.content()
238
 
239
  # Explicit cleanup
240
  page.close()
241
  context.close()
242
  browser.close()
243
 
244
+ return final_content
245
 
246
  except Exception as nav:
247
  logger.error(f"Playwright navigation error: {nav}")
248
  return None
249
  finally:
 
250
  try:
251
  browser.close()
252
  except:
253
  pass
254
 
255
+
256
  except Exception as e:
257
  logger.error(f"Playwright critical error: {e}")
258
  return None