Back to Home
Gistify Snippet
kyiov
Sep 4·1 file(s)
Public

Scraper cheat sheet - realpython.com (anti-bot)

Scraper cheat sheet - realpython.com (anti-bot)

#github-gist#javascript#gist:5f2371ed7c859c4f291df7818aa1cb6f#gistsync:2026-09-02T10:46:07Z
0
Raw
realpython.jsjavascript
136 lines
1import { exec } from 'child_process'
2import { promisify } from 'util'
3import fs from 'fs'
4
5const execAsync = promisify(exec)
6
7const PYTHON_BRIDGE = `
8import scrapling
9from scrapling.fetchers import StealthyFetcher
10from bs4 import BeautifulSoup
11import json
12import re
13
14def clean_code_block(text):
15 raw_lines = text.split('\\n')
16 cleaned_lines = []
17 current_line = []
18
19 for l in raw_lines:
20 s = l.strip()
21 if not s:
22 if current_line:
23 cleaned_lines.append(" ".join(current_line))
24 current_line = []
25 continue
26
27 if s.startswith('#') or s.startswith('$') or s.startswith('>>>'):
28 if current_line:
29 cleaned_lines.append(" ".join(current_line))
30 current_line = []
31 cleaned_lines.append(s)
32 else:
33 current_line.append(s)
34
35 if current_line:
36 cleaned_lines.append(" ".join(current_line))
37
38 res = "\\n".join(cleaned_lines)
39 res = re.sub(r'\\s+([,.:;\\]\\)\\}])', r'\\1', res)
40 res = re.sub(r'([\\[\\(\\{])\\s+', r'\\1', res)
41 res = re.sub(r'\\s*=\\s*', ' = ', res)
42 res = re.sub(r'\\s*\\+\\=\\s*', ' += ', res)
43 res = re.sub(r'\\s*\\-\\=\\s*', ' -= ', res)
44 return res.strip()
45
46def fetch_cheatsheet():
47 try:
48 fetcher = StealthyFetcher()
49 page = fetcher.fetch('https://realpython.com/cheatsheets/python/', network_idle=True)
50 if page.status != 200:
51 print(json.dumps({"status": False, "error": f"HTTP {page.status}"}))
52 return
53
54 soup = BeautifulSoup(page.html_content, 'html.parser')
55 title = "Python 3 Cheat Sheet"
56
57 sections = []
58
59 # Blacklist of promotional keywords
60 PROMO_WORDS = [
61 'real python', 'show/hide', 'continue your learning', 'bonus',
62 'download this', 'newsletter', 'quizzes', 'podcast', 'get a python cheat',
63 'learning paths', 'video courses', 'written tutorials', 'interactive quizzes',
64 'do you want to go deeper'
65 ]
66
67 for sec_tag in soup.find_all('section', class_='section2'):
68 h2 = sec_tag.find('h2')
69 if not h2:
70 continue
71 sec_title = h2.get_text().strip()
72
73 # Skip promotional sections
74 if any(pw in sec_title.lower() for pw in ['related tutorial', 'bonus', 'resources', 'curriculum']):
75 continue
76
77 items = []
78 for child in sec_tag.find_all(['pre', 'p']):
79 if child.name == 'pre':
80 code_clean = clean_code_block(child.get_text())
81 if code_clean:
82 items.append({'type': 'code', 'language': 'python', 'content': code_clean})
83 elif child.name == 'p':
84 t = child.get_text().strip()
85 if t and len(t) > 3 and not any(pw in t.lower() for pw in PROMO_WORDS):
86 items.append({'type': 'text', 'content': t})
87
88 if any(it['type'] == 'code' for it in items):
89 sections.append({
90 'title': sec_title,
91 'items': items
92 })
93
94 print(json.dumps({
95 "status": True,
96 "title": title,
97 "total_sections": len(sections),
98 "sections": sections
99 }))
100 except Exception as e:
101 print(json.dumps({"status": False, "error": str(e)}))
102
103if __name__ == "__main__":
104 fetch_cheatsheet()
105`
106
107let cacheCheatsheet = null
108let lastFetch = 0
109
110export async function getPythonCheatSheet(forceRefresh = false) {
111 const now = Date.now()
112 if (!forceRefresh && cacheCheatsheet && (now - lastFetch < 3600000)) {
113 return cacheCheatsheet
114 }
115
116 try {
117 const tmpScript = '/tmp/rp_cheatsheet_clean.py'
118 await fs.promises.writeFile(tmpScript, PYTHON_BRIDGE, 'utf-8')
119
120 const { stdout } = await execAsync(`${process.env.SCRAPLING_PYTHON || "/tmp/scrapping-venv/bin/python"} ${tmpScript}`, { maxBuffer: 15 * 1024 * 1024 })
121 const json = JSON.parse(stdout.trim().split('\n').pop())
122
123 if (!json.status) throw new Error(json.error || 'Gagal mengekstrak cheatsheet.')
124
125 cacheCheatsheet = json
126 lastFetch = now
127 return json
128 } catch (e) {
129 console.error('[RealPython CheatSheet Error]:', e.message)
130 throw e
131 }
132}
133
134export default {
135 getPythonCheatSheet
136}
Discussion & Comments0

No comments yet — be the first to leave feedback or start a code discussion.

Sign in with your GitHub account to write comments and join the discussion.