Skip to content

Commit ce169ea

Browse files
committed
fix: integrate cloudscraper to bypass Cloudflare 403 Forbidden on Substack and other sources
1 parent 1991f26 commit ce169ea

2 files changed

Lines changed: 17 additions & 12 deletions

File tree

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ beautifulsoup4
33
lxml
44
groq
55
python-dotenv
6+
cloudscraper

run_job.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from bs4 import BeautifulSoup
55
from groq import Groq
66
from dotenv import load_dotenv
7+
import cloudscraper
78

89
# Cargar variables de entorno desde .env si existe (local)
910
load_dotenv()
@@ -32,19 +33,19 @@
3233

3334
groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
3435

35-
# User-Agent y headers avanzados para evitar bloqueos (403 Forbidden)
36+
# Inicializamos el scraper de Cloudflare una sola vez
37+
scraper = cloudscraper.create_scraper(
38+
browser={
39+
'browser': 'chrome',
40+
'platform': 'windows',
41+
'desktop': True
42+
}
43+
)
44+
3645
HEADERS = {
37-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
3846
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
3947
'Accept-Language': 'es-ES,es;q=0.9,en;q=0.8',
4048
'Referer': 'https://www.google.com/',
41-
'Connection': 'keep-alive',
42-
'Upgrade-Insecure-Requests': '1',
43-
'Sec-Fetch-Dest': 'document',
44-
'Sec-Fetch-Mode': 'navigate',
45-
'Sec-Fetch-Site': 'cross-site',
46-
'Sec-Fetch-User': '?1',
47-
'Cache-Control': 'max-age=0',
4849
}
4950

5051
# ── Helpers ───────────────────────────────────────────────────────────────────
@@ -108,6 +109,7 @@ def get_github_file():
108109
}
109110

110111
try:
112+
# Para la API de GitHub usamos requests normal (no suele tener Cloudflare agresivo para API)
111113
r = requests.get(url, headers=headers, timeout=10)
112114

113115
if r.status_code == 401:
@@ -176,6 +178,7 @@ def push_to_github(item, summary_text, categoria):
176178
"sha": sha
177179
}
178180
try:
181+
# También usamos requests normal para la API de GitHub
179182
r = requests.put(url, headers={
180183
"Authorization": f"Bearer {token}",
181184
"Accept": "application/vnd.github.v3+json"
@@ -276,9 +279,10 @@ def send_to_telegram(message):
276279

277280
def scrape_rss_feed(url, source_name, limit=5):
278281
try:
279-
# Usamos una sesión para mantener consistencia en los headers
280-
session = requests.Session()
281-
r = session.get(url, headers=HEADERS, timeout=15)
282+
# Usamos el scraper de Cloudflare para TODAS las fuentes RSS
283+
r = scraper.get(url, headers=HEADERS, timeout=15)
284+
285+
logger.info(f"FETCH {source_name}: Status {r.status_code}")
282286

283287
if r.status_code != 200:
284288
logger.error(f"RSS Error ({source_name}): Status {r.status_code}")

0 commit comments

Comments
 (0)