
# Fichier: python_cheats/cheatsheets/requests_complete.txt
# Python Requests - HTTP Library Complète



[OK] INSTALLATION ET IMPORT


[OK] INSTALLATION
    pip install requests
    pip install requests[security]  # Avec sécurité renforcée
    pip install requests[socks]     # Support SOCKS proxy

[OK] IMPORT
    import requests
    from requests import Session
    from requests.auth import HTTPBasicAuth, HTTPDigestAuth
    from requests.exceptions import RequestException, Timeout, HTTPError
    import json

[OK] VERSION
    print(requests.__version__)


[OK] MÉTHODES HTTP - BASIQUE


[OK] GET REQUEST
    # Simple
    response = requests.get('https://api.example.com/data')
    
    # Avec paramètres (query string)
    params = {'key': 'value', 'page': 1, 'limit': 10}
    response = requests.get('https://api.example.com/search', params=params)
    # URL: https://api.example.com/search?key=value&page=1&limit=10
    
    # Paramètres avec listes
    params = {'ids': [1, 2, 3]}
    response = requests.get('https://api.example.com/items', params=params)
    # URL: https://api.example.com/items?ids=1&ids=2&ids=3

[OK] POST REQUEST
    # Form data (application/x-www-form-urlencoded)
    data = {'username': 'john', 'password': 'secret'}
    response = requests.post('https://api.example.com/login', data=data)
    
    # JSON (application/json)
    response = requests.post('https://api.example.com/api', json=data)
    
    # Équivalent manuel
    import json
    headers = {'Content-Type': 'application/json'}
    response = requests.post('https://api.example.com/api',
                            data=json.dumps(data),
                            headers=headers)

[OK] PUT REQUEST
    # Mise à jour complète
    data = {'name': 'John Doe', 'email': 'john@example.com'}
    response = requests.put('https://api.example.com/user/1', json=data)

[OK] PATCH REQUEST
    # Mise à jour partielle
    data = {'email': 'newemail@example.com'}
    response = requests.patch('https://api.example.com/user/1', json=data)

[OK] DELETE REQUEST
    response = requests.delete('https://api.example.com/user/1')
    
    # Avec paramètres
    response = requests.delete('https://api.example.com/user/1',
                              params={'force': True})

[OK] HEAD REQUEST
    # Obtenir uniquement les headers (pas de body)
    response = requests.head('https://api.example.com/resource')
    print(response.headers)
    print(response.status_code)

[OK] OPTIONS REQUEST
    # Découvrir les méthodes supportées
    response = requests.options('https://api.example.com/resource')
    print(response.headers.get('Allow'))  # GET, POST, PUT, DELETE


[OK] RESPONSE OBJECT - PROPRIÉTÉS


[OK] STATUS CODE
    response = requests.get('https://api.example.com')
    
    print(response.status_code)       # 200, 404, 500, etc.
    print(response.ok)                # True si status < 400
    print(response.reason)            # 'OK', 'Not Found', etc.
    
    # Vérifier des codes spécifiques
    if response.status_code == 200:
        print("Success")
    elif response.status_code == 404:
        print("Not found")
    elif response.status_code >= 500:
        print("Server error")

[OK] CONTENU DE LA RÉPONSE
    # Texte (str)
    text = response.text
    
    # Bytes (brut)
    content = response.content
    
    # JSON (dict/list)
    data = response.json()
    
    # Avec gestion d'erreur JSON
    try:
        data = response.json()
    except json.JSONDecodeError:
        print("Response is not valid JSON")

[OK] HEADERS
    # Tous les headers
    print(response.headers)
    
    # Header spécifique (insensible à la casse)
    content_type = response.headers['Content-Type']
    content_type = response.headers.get('Content-Type')
    
    # Headers courants
    print(response.headers['Content-Length'])
    print(response.headers['Server'])
    print(response.headers['Date'])

[OK] ENCODAGE
    print(response.encoding)          # 'utf-8', 'ISO-8859-1', etc.
    
    # Changer l'encodage
    response.encoding = 'utf-8'
    print(response.text)  # Utilise le nouvel encodage

[OK] URL ET HISTORIQUE
    print(response.url)               # URL finale (après redirections)
    print(response.history)           # Liste des redirections
    
    # Vérifier redirections
    if response.history:
        print(f"Request was redirected")
        for resp in response.history:
            print(f"{resp.status_code}: {resp.url}")
        print(f"Final destination: {response.url}")

[OK] TEMPS DE RÉPONSE
    print(response.elapsed)           # timedelta object
    print(response.elapsed.total_seconds())  # En secondes

[OK] COOKIES
    print(response.cookies)           # RequestsCookieJar
    
    # Cookie spécifique
    session_id = response.cookies.get('session_id')
    
    # Tous les cookies en dict
    cookies_dict = requests.utils.dict_from_cookiejar(response.cookies)

[OK] CONTENU BRUT (STREAMING)
    response = requests.get('https://api.example.com', stream=True)
    print(response.raw)
    print(response.raw.read(10))  # Lire 10 bytes


[OK] REQUEST OPTIONS - AVANCÉ


[OK] HEADERS PERSONNALISÉS
    headers = {
        'User-Agent': 'MyApp/1.0.0',
        'Authorization': 'Bearer token123',
        'Accept': 'application/json',
        'Accept-Language': 'en-US,en;q=0.9',
        'Custom-Header': 'custom-value'
    }
    response = requests.get('https://api.example.com', headers=headers)
    
    # Headers courants
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Connection': 'keep-alive'
    }

[OK] QUERY PARAMETERS
    # Méthode 1: dictionnaire
    params = {
        'search': 'python',
        'category': 'programming',
        'sort': 'date',
        'page': 1,
        'limit': 20
    }
    response = requests.get('https://api.example.com/search', params=params)
    
    # Méthode 2: liste de tuples (pour doublons)
    params = [
        ('tag', 'python'),
        ('tag', 'requests'),
        ('tag', 'api')
    ]
    response = requests.get('https://api.example.com/search', params=params)
    
    # Méthode 3: string
    response = requests.get('https://api.example.com/search?q=python&page=1')
    
    # Paramètres avec valeurs None (ignorés)
    params = {'key1': 'value1', 'key2': None}
    # key2 ne sera pas inclus dans l'URL

[OK] TIMEOUT
    # Timeout simple (secondes)
    try:
        response = requests.get('https://api.example.com', timeout=5)
    except requests.Timeout:
        print("Request timed out after 5 seconds")
    
    # Timeout séparé pour connexion et lecture
    try:
        response = requests.get('https://api.example.com',
                              timeout=(3, 10))  # (connect, read)
    except requests.ConnectTimeout:
        print("Connection timeout")
    except requests.ReadTimeout:
        print("Read timeout")
    
    # Pas de timeout (attention!)
    response = requests.get('https://api.example.com', timeout=None)

[OK] ALLOW REDIRECTS
    # Suivre les redirections (défaut: True)
    response = requests.get('https://api.example.com', allow_redirects=True)
    
    # Ne pas suivre
    response = requests.get('https://api.example.com', allow_redirects=False)
    print(response.status_code)  # 301, 302, etc.
    print(response.headers['Location'])  # URL de redirection

[OK] VERIFY SSL
    # Vérifier certificat SSL (défaut: True, recommandé)
    response = requests.get('https://api.example.com', verify=True)
    
    # Désactiver vérification ([ATTENTION] non sécurisé!)
    import urllib3
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    response = requests.get('https://api.example.com', verify=False)
    
    # Spécifier bundle de certificats
    response = requests.get('https://api.example.com',
                          verify='/path/to/certfile')

[OK] CERT (CLIENT CERTIFICATES)
    # Certificat client
    response = requests.get('https://api.example.com',
                          cert=('/path/to/client.cert', '/path/to/client.key'))
    
    # Avec certificat intermédiaire
    response = requests.get('https://api.example.com',
                          cert=('/path/to/client.cert',
                               '/path/to/client.key',
                               '/path/to/ca.cert'))

[OK] STREAM
    # Streaming pour gros fichiers
    response = requests.get('https://api.example.com/bigfile',
                          stream=True)
    
    # Itérer par chunks
    for chunk in response.iter_content(chunk_size=8192):
        if chunk:
            # Traiter le chunk
            pass
    
    # Itérer par lignes
    for line in response.iter_lines():
        if line:
            print(line.decode('utf-8'))


[OK] AUTHENTIFICATION


[OK] BASIC AUTH
    from requests.auth import HTTPBasicAuth
    
    # Méthode 1: HTTPBasicAuth
    response = requests.get('https://api.example.com',
                          auth=HTTPBasicAuth('username', 'password'))
    
    # Méthode 2: Tuple (plus simple)
    response = requests.get('https://api.example.com',
                          auth=('username', 'password'))

[OK] DIGEST AUTH
    from requests.auth import HTTPDigestAuth
    
    response = requests.get('https://api.example.com',
                          auth=HTTPDigestAuth('username', 'password'))

[OK] BEARER TOKEN (JWT)
    headers = {
        'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
    }
    response = requests.get('https://api.example.com', headers=headers)

[OK] API KEY
    # Dans headers
    headers = {'X-API-Key': 'your-api-key-here'}
    response = requests.get('https://api.example.com', headers=headers)
    
    # Dans query params
    params = {'api_key': 'your-api-key-here'}
    response = requests.get('https://api.example.com', params=params)

[OK] OAUTH 2.0
    # Installation: pip install requests-oauthlib
    from requests_oauthlib import OAuth2Session
    
    client_id = 'your-client-id'
    client_secret = 'your-client-secret'
    token_url = 'https://api.example.com/oauth/token'
    
    oauth = OAuth2Session(client_id)
    token = oauth.fetch_token(token_url,
                             client_secret=client_secret)
    
    response = oauth.get('https://api.example.com/protected')

[OK] CUSTOM AUTH
    from requests.auth import AuthBase
    
    class TokenAuth(AuthBase):
        """Custom authentication avec token"""
        def __init__(self, token):
            self.token = token
        
        def __call__(self, r):
            r.headers['X-Custom-Token'] = self.token
            return r
    
    response = requests.get('https://api.example.com',
                          auth=TokenAuth('my-token'))


[OK] COOKIES


[OK] ENVOYER DES COOKIES
    # Dictionnaire
    cookies = {'session_id': '12345', 'user': 'john'}
    response = requests.get('https://api.example.com', cookies=cookies)
    
    # CookieJar
    from http.cookiejar import CookieJar
    jar = CookieJar()
    response = requests.get('https://api.example.com', cookies=jar)

[OK] LIRE DES COOKIES
    response = requests.get('https://api.example.com')
    
    # Cookie spécifique
    session_id = response.cookies['session_id']
    session_id = response.cookies.get('session_id', 'default')
    
    # Tous les cookies
    for cookie in response.cookies:
        print(f"{cookie.name}: {cookie.value}")
    
    # Convertir en dict
    cookies_dict = dict(response.cookies)

[OK] COOKIES PERSISTANTS
    # Sauvegarder
    import pickle
    with open('cookies.pkl', 'wb') as f:
        pickle.dump(requests.cookies, f)
    
    # Charger
    with open('cookies.pkl', 'rb') as f:
        cookies = pickle.load(f)
    response = requests.get('https://api.example.com', cookies=cookies)


[OK] SESSIONS


[OK] SESSION BASIQUE
    # Crée une session qui persiste cookies et headers
    session = requests.Session()
    
    # Premier request
    response = session.get('https://api.example.com/login')
    
    # Les cookies sont automatiquement conservés
    response = session.get('https://api.example.com/profile')

[OK] SESSION AVEC HEADERS
    session = requests.Session()
    
    # Headers par défaut pour toute la session
    session.headers.update({
        'Authorization': 'Bearer token123',
        'User-Agent': 'MyApp/1.0'
    })
    
    response1 = session.get('https://api.example.com/endpoint1')
    response2 = session.get('https://api.example.com/endpoint2')

[OK] SESSION AVEC AUTH
    session = requests.Session()
    session.auth = ('username', 'password')
    
    # Toutes les requêtes utilisent cette auth
    response = session.get('https://api.example.com/protected')

[OK] COOKIES DANS SESSION
    session = requests.Session()
    
    # Définir cookies
    session.cookies.set('session_id', '12345')
    
    # Les cookies persistent entre les requêtes
    response = session.get('https://api.example.com')

[OK] CONTEXT MANAGER POUR SESSION
    with requests.Session() as session:
        session.headers.update({'Authorization': 'Bearer token'})
        
        response = session.get('https://api.example.com/data')
        print(response.json())
    
    # Session automatiquement fermée

[OK] PRÉPARER UNE REQUÊTE
    session = requests.Session()
    
    # Préparer
    req = requests.Request('GET', 'https://api.example.com',
                          params={'key': 'value'})
    prepared = session.prepare_request(req)
    
    # Inspecter
    print(prepared.url)
    print(prepared.headers)
    
    # Envoyer
    response = session.send(prepared)

[OK] HOOKS (ÉVÉNEMENTS)
    def print_url(r, *args, **kwargs):
        print(f"Request URL: {r.url}")
    
    session = requests.Session()
    session.hooks['response'] = print_url
    
    response = session.get('https://api.example.com')


[OK] UPLOAD DE FICHIERS


[OK] UPLOAD SIMPLE
    # Upload un fichier
    files = {'file': open('document.pdf', 'rb')}
    response = requests.post('https://api.example.com/upload', files=files)
    
    # Ne pas oublier de fermer
    with open('document.pdf', 'rb') as f:
        files = {'file': f}
        response = requests.post('https://api.example.com/upload', files=files)

[OK] UPLOAD AVEC METADATA
    # Spécifier filename et content-type
    files = {
        'file': ('report.pdf', open('report.pdf', 'rb'), 'application/pdf')
    }
    response = requests.post('https://api.example.com/upload', files=files)
    
    # Avec headers personnalisés
    files = {
        'file': ('report.pdf', open('report.pdf', 'rb'), 'application/pdf',
                {'X-Custom-Header': 'value'})
    }

[OK] UPLOAD MULTIPLE FILES
    files = [
        ('files', ('file1.txt', open('file1.txt', 'rb'), 'text/plain')),
        ('files', ('file2.txt', open('file2.txt', 'rb'), 'text/plain')),
        ('files', ('file3.pdf', open('file3.pdf', 'rb'), 'application/pdf'))
    ]
    response = requests.post('https://api.example.com/upload', files=files)

[OK] UPLOAD AVEC DONNÉES ADDITIONNELLES
    files = {'file': open('document.pdf', 'rb')}
    data = {
        'title': 'Monthly Report',
        'description': 'Report for January',
        'category': 'finance'
    }
    response = requests.post('https://api.example.com/upload',
                            files=files,
                            data=data)

[OK] UPLOAD DEPUIS MÉMOIRE
    from io import BytesIO
    
    # Créer un fichier en mémoire
    file_content = b'Hello, World!'
    file_object = BytesIO(file_content)
    
    files = {'file': ('hello.txt', file_object, 'text/plain')}
    response = requests.post('https://api.example.com/upload', files=files)

[OK] UPLOAD AVEC PROGRESS
    from tqdm import tqdm
    
    def upload_with_progress(filepath, url):
        file_size = os.path.getsize(filepath)
        
        with open(filepath, 'rb') as f:
            with tqdm(total=file_size, unit='B', unit_scale=True) as pbar:
                def read_callback(monitor):
                    pbar.update(len(monitor))
                
                files = {'file': f}
                response = requests.post(url, files=files)
        
        return response


[OK] DOWNLOAD DE FICHIERS


[OK] DOWNLOAD SIMPLE
    response = requests.get('https://example.com/file.pdf')
    
    with open('downloaded.pdf', 'wb') as f:
        f.write(response.content)

[OK] DOWNLOAD AVEC STREAMING
    # Pour gros fichiers
    url = 'https://example.com/bigfile.zip'
    
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with open('bigfile.zip', 'wb') as f:
            for chunk in r.iter_content(chunk_size=8192):
                f.write(chunk)

[OK] DOWNLOAD AVEC PROGRESS BAR
    from tqdm import tqdm
    
    url = 'https://example.com/file.zip'
    response = requests.get(url, stream=True)
    
    total_size = int(response.headers.get('content-length', 0))
    
    with open('file.zip', 'wb') as f:
        with tqdm(total=total_size, unit='B', unit_scale=True) as pbar:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
                pbar.update(len(chunk))

[OK] DOWNLOAD AVEC RESUME (REPRISE)
    import os
    
    url = 'https://example.com/file.zip'
    filename = 'file.zip'
    
    # Vérifier si fichier existe déjà
    if os.path.exists(filename):
        resume_pos = os.path.getsize(filename)
        headers = {'Range': f'bytes={resume_pos}-'}
    else:
        resume_pos = 0
        headers = {}
    
    response = requests.get(url, headers=headers, stream=True)
    
    mode = 'ab' if resume_pos > 0 else 'wb'
    with open(filename, mode) as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)

[OK] DOWNLOAD AVEC VÉRIFICATION
    import hashlib
    
    def download_with_checksum(url, filename, expected_md5):
        response = requests.get(url, stream=True)
        
        md5 = hashlib.md5()
        
        with open(filename, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
                md5.update(chunk)
        
        if md5.hexdigest() != expected_md5:
            os.remove(filename)
            raise ValueError("Checksum mismatch")
        
        return filename


[OK] PROXIES


[OK] HTTP/HTTPS PROXY
    proxies = {
        'http': 'http://10.10.1.10:3128',
        'https': 'http://10.10.1.10:1080',
    }
    response = requests.get('https://api.example.com', proxies=proxies)

[OK] PROXY AVEC AUTHENTIFICATION
    proxies = {
        'http': 'http://user:pass@10.10.1.10:3128',
        'https': 'http://user:pass@10.10.1.10:1080',
    }
    response = requests.get('https://api.example.com', proxies=proxies)

[OK] SOCKS PROXY
    # Installation: pip install requests[socks]
    proxies = {
        'http': 'socks5://user:pass@host:port',
        'https': 'socks5://user:pass@host:port'
    }
    response = requests.get('https://api.example.com', proxies=proxies)

[OK] PROXY DEPUIS VARIABLE D'ENVIRONNEMENT
    # Définir HTTP_PROXY et HTTPS_PROXY
    import os
    os.environ['HTTP_PROXY'] = 'http://10.10.1.10:3128'
    os.environ['HTTPS_PROXY'] = 'http://10.10.1.10:1080'
    
    # requests utilise automatiquement ces variables
    response = requests.get('https://api.example.com')

[OK] DÉSACTIVER PROXY POUR CERTAINS DOMAINES
    proxies = {
        'http': 'http://10.10.1.10:3128',
        'https': 'http://10.10.1.10:1080',
        'no': 'localhost,127.0.0.1,.local'
    }


[OK] GESTION D'ERREURS


[OK] ERREURS BASIQUES
    try:
        response = requests.get('https://api.example.com', timeout=5)
        response.raise_for_status()  # Lève HTTPError pour 4xx/5xx
        
    except requests.HTTPError as e:
        print(f"HTTP Error: {e}")
        print(f"Status Code: {e.response.status_code}")
        
    except requests.ConnectionError:
        print("Failed to connect to the server")
        
    except requests.Timeout:
        print("Request timed out")
        
    except requests.RequestException as e:
        print(f"An error occurred: {e}")

[OK] ERREURS DÉTAILLÉES
    try:
        response = requests.get('https://api.example.com')
        response.raise_for_status()
        
    except requests.exceptions.SSLError as e:
        print(f"SSL Error: {e}")
        
    except requests.exceptions.ProxyError as e:
        print(f"Proxy Error: {e}")
        
    except requests.exceptions.TooManyRedirects as e:
        print(f"Too many redirects: {e}")
        
    except requests.exceptions.ConnectTimeout as e:
        print(f"Connection timeout: {e}")
        
    except requests.exceptions.ReadTimeout as e:
        print(f"Read timeout: {e}")
        
    except requests.exceptions.URLRequired as e:
        print(f"URL required: {e}")
        
    except requests.exceptions.MissingSchema as e:
        print(f"Invalid URL: {e}")

[OK] VÉRIFICATION MANUELLE DU STATUS
    response = requests.get('https://api.example.com')
    
    if response.status_code == 200:
        data = response.json()
    elif response.status_code == 404:
        print("Resource not found")
    elif response.status_code == 401:
        print("Unauthorized")
    elif response.status_code >= 500:
        print("Server error")
    else:
        print(f"Unexpected status: {response.status_code}")

[OK] RETRY AUTOMATIQUE
    from requests.adapters import HTTPAdapter
    from requests.packages.urllib3.util.retry import Retry
    
    session = requests.Session()
    
    retry = Retry(
        total=5,                    # Nombre total de retries
        read=5,
        connect=5,
        backoff_factor=0.3,         # Délai entre retries
        status_forcelist=(500, 502, 504),  # Status codes à retry
        allowed_methods=["HEAD", "GET", "OPTIONS", "POST"]
    )
    
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    
    response = session.get('https://api.example.com')

[OK] RETRY MANUEL
    import time
    
    def request_with_retry(url, max_retries=3, delay=1):
        for attempt in range(max_retries):
            try:
                response = requests.get(url, timeout=5)
                response.raise_for_status()
                return response
            
            except (requests.ConnectionError, requests.Timeout) as e:
                if attempt < max_retries - 1:
                    print(f"Attempt {attempt + 1} failed. Retrying in {delay}s...")
                    time.sleep(delay)
                    delay *= 2  # Exponential backoff
                else:
                    raise
    
    response = request_with_retry('https://api.example.com')


[OK] PATTERNS ET BONNES PRATIQUES


[OK] API CLIENT CLASS
    class APIClient:
        def __init__(self, base_url, api_key=None):
            self.base_url = base_url
            self.session = requests.Session()
            
            if api_key:
                self.session.headers.update({'X-API-Key': api_key})
        
        def _request(self, method, endpoint, **kwargs):
            url = f"{self.base_url}/{endpoint}"
            response = self.session.request(method, url, **kwargs)
            response.raise_for_status()
            return response.json()
        
        def get(self, endpoint, **kwargs):
            return self._request('GET', endpoint, **kwargs)
        
        def post(self, endpoint, **kwargs):
            return self._request('POST', endpoint, **kwargs)
        
        def put(self, endpoint, **kwargs):
            return self._request('PUT', endpoint, **kwargs)
        
        def delete(self, endpoint, **kwargs):
            return self._request('DELETE', endpoint, **kwargs)
    
    # Utilisation
    client = APIClient('https://api.example.com', api_key='secret')
    users = client.get('users', params={'page': 1})
    new_user = client.post('users', json={'name': 'John'})

[OK] RATE LIMITING
    import time
    from collections import deque
    
    class RateLimitedSession(requests.Session):
        def __init__(self, requests_per_second=10):
            super().__init__()
            self.requests_per_second = requests_per_second
            self.requests = deque()
        
        def request(self, *args, **kwargs):
            now = time.time()
            
            # Supprimer les requêtes de plus d'1 seconde
            while self.requests and self.requests[0] < now - 1:
                self.requests.popleft()
            
            # Attendre si limite atteinte
            if len(self.requests) >= self.requests_per_second:
                sleep_time = 1 - (now - self.requests[0])
                if sleep_time > 0:
                    time.sleep(sleep_time)
            
            self.requests.append(time.time())
            return super().request(*args, **kwargs)
    
    session = RateLimitedSession(requests_per_second=5)
    for i in range(20):
        response = session.get('https://api.example.com/data')

[OK] CACHING
    from requests_cache import CachedSession
    
    # Installation: pip install requests-cache
    session = CachedSession(
        cache_name='api_cache',
        backend='sqlite',
        expire_after=3600  # 1 heure
    )
    
    # Les requêtes identiques utilisent le cache
    response1 = session.get('https://api.example.com')