prince1604 commited on
Commit ·
d92f150
1
Parent(s): 747f2d2
Fix crawl evasion: Add WAF block detection to Tier 1 fetch
Browse files- src/__pycache__/crawler.cpython-314.pyc +0 -0
- src/crawler.py +37 -2
src/__pycache__/crawler.cpython-314.pyc
CHANGED
|
Binary files a/src/__pycache__/crawler.cpython-314.pyc and b/src/__pycache__/crawler.cpython-314.pyc differ
|
|
|
src/crawler.py
CHANGED
|
@@ -53,14 +53,21 @@ class Crawler:
|
|
| 53 |
# Tier 1: curl_cffi
|
| 54 |
content = self._fetch_http(url)
|
| 55 |
if content:
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
# Tier 2: Cloudscraper (Intermediate, handles JS challenges)
|
| 59 |
logger.info(f"Tier 1 failed. Trying Cloudscraper info for {url}...")
|
| 60 |
try:
|
| 61 |
resp = self.scraper.get(url, timeout=10)
|
| 62 |
if 200 <= resp.status_code < 300:
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
| 64 |
except Exception as e:
|
| 65 |
logger.warning(f"Cloudscraper failed: {e}")
|
| 66 |
|
|
@@ -71,6 +78,34 @@ class Crawler:
|
|
| 71 |
|
| 72 |
return None
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
def _fetch_http(self, url):
|
| 75 |
"""
|
| 76 |
Fast HTTP fetch with aggressive timeouts and minimal retries.
|
|
|
|
| 53 |
# Tier 1: curl_cffi
|
| 54 |
content = self._fetch_http(url)
|
| 55 |
if content:
|
| 56 |
+
# Validate content isn't a block page
|
| 57 |
+
if not self._is_blocked(content):
|
| 58 |
+
return content
|
| 59 |
+
else:
|
| 60 |
+
logger.warning(f"Tier 1 fetched blocked content for {url}. Escalating...")
|
| 61 |
|
| 62 |
# Tier 2: Cloudscraper (Intermediate, handles JS challenges)
|
| 63 |
logger.info(f"Tier 1 failed. Trying Cloudscraper info for {url}...")
|
| 64 |
try:
|
| 65 |
resp = self.scraper.get(url, timeout=10)
|
| 66 |
if 200 <= resp.status_code < 300:
|
| 67 |
+
if not self._is_blocked(resp.text):
|
| 68 |
+
return resp.text
|
| 69 |
+
else:
|
| 70 |
+
logger.warning(f"Cloudscraper also blocked for {url}.")
|
| 71 |
except Exception as e:
|
| 72 |
logger.warning(f"Cloudscraper failed: {e}")
|
| 73 |
|
|
|
|
| 78 |
|
| 79 |
return None
|
| 80 |
|
| 81 |
+
def _is_blocked(self, content):
|
| 82 |
+
"""
|
| 83 |
+
Detects if the content is likely a WAF block page, CAPTCHA, or 'Just a moment'.
|
| 84 |
+
"""
|
| 85 |
+
if not content or len(content) < 500:
|
| 86 |
+
return True # Too small, suspicious
|
| 87 |
+
|
| 88 |
+
lower_content = content.lower()
|
| 89 |
+
block_keywords = [
|
| 90 |
+
"just a moment...",
|
| 91 |
+
"enable javascript",
|
| 92 |
+
"verify you are human",
|
| 93 |
+
"access denied",
|
| 94 |
+
"cloudflare",
|
| 95 |
+
"captcha",
|
| 96 |
+
"security check",
|
| 97 |
+
"turn on javascript",
|
| 98 |
+
"challenge.js"
|
| 99 |
+
]
|
| 100 |
+
|
| 101 |
+
if any(k in lower_content for k in block_keywords):
|
| 102 |
+
# Double check: sometimes legitimate pages mention these words.
|
| 103 |
+
# But usually, if it's < 5KB and has these words, it's a block.
|
| 104 |
+
if len(content) < 5000:
|
| 105 |
+
return True
|
| 106 |
+
|
| 107 |
+
return False
|
| 108 |
+
|
| 109 |
def _fetch_http(self, url):
|
| 110 |
"""
|
| 111 |
Fast HTTP fetch with aggressive timeouts and minimal retries.
|