Dans ce module, nous allons explorer le fonctionnement de « tornet_scraper ». Vous avez déjà appris à extraire des données des forums « tornet » et « clearnet », mais pour rationaliser ce processus, nous avons besoin d'un outil avancé de scraping web doté de fonctionnalités permettant le scraping de données à grande échelle.
Ce sujet peut sembler difficile pour certains, mais à l'ère de l'IA, où une grande partie de la programmation peut être automatisée, et grâce à ce cours qui exploite l'IA pour la cyberdéfense, la création d'une application web pour le scraping de données est désormais plus accessible.
Chaque application web repose sur une pile technologique, un ensemble d'outils logiciels utilisés pour développer les différents composants de l'application. Par exemple, les sites web tels que Facebook disposent d'une interface utilisateur (front-end) et d'une logique côté serveur (back-end) qui rendent les pages accessibles. Dans certains cas, le front-end et le back-end peuvent utiliser le même langage de programmation. Par exemple, les applications web basées sur JavaScript peuvent utiliser Vue pour le front-end et Nuxt pour le back-end, ce qui simplifie le développement puisque les deux sont écrits en JavaScript.
Pour nos besoins, nous avons besoin d'une pile technologique évolutive et compatible avec les API, que vous pouvez personnaliser indépendamment. Ce module n'approfondira pas les concepts de programmation, car vous pouvez copier-coller le code dans votre outil d'IA préféré pour obtenir une explication adaptée à votre style d'apprentissage.
Si vous préférez passer ce module et commencer par un projet fonctionnel, vous pouvez le faire, mais vous ne comprendrez pas comment le projet fonctionne et il ne sera qu'un outil parmi d'autres. Le projet final est disponible ici :
https://github.com/CyberMounties/tornet_scraper
Les thèmes abordés dans cette section sont les suivants :
- Comment fonctionnent les modèles
- Comment fonctionne
main.py - Comment fonctionnent les bases de données
- Comment fonctionnent les routes
- Comment fonctionnent les scrapers
- Comment fonctionnent les services
Comment fonctionnent les modèles
Les applications web modernes utilisent des modèles, tels que des modèles par défaut ou de base, pour définir les éléments qui apparaissent sur toutes les pages, comme un menu de navigation ou une barre de navigation. Vous pourriez copier-coller la barre de navigation dans chaque page, mais toute modification nécessiterait de mettre à jour chaque page individuellement.
Pour rationaliser ce processus, nous utilisons un moteur de modèles. Dans les applications web Python, Jinja2 nous permet de créer un modèle de base avec des blocs de contenu pour une conception cohérente et efficace.
Ouvrez app/templates/base.html :
<!-- app/templates/base.html -->
<!DOCTYPE html>
<html lang="en" data-theme="nord">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %} - Tornet Scraper</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body class="min-h-screen bg-base-200">
<!-- Navbar -->
<div class="navbar bg-base-100 shadow-xl">
<div class="navbar-start">
<a class="btn btn-ghost text-xl">Tornet Scraper</a>
</div>
<div class="navbar-center hidden lg:flex">
<ul class="menu menu-horizontal px-1">
<li><a href="/" class="btn btn-ghost">Dashboard</a></li>
<li><a href="/proxy-gen" class="btn btn-ghost">Proxy Gen</a></li>
<li><a href="/manage-api" class="btn btn-ghost">API Management</a></li>
<li><a href="/bot-profile" class="btn btn-ghost">Bot Profile</a></li>
<li><a href="/marketplace-scan" class="btn btn-ghost">Marketplace Scan</a></li>
<li><a href="/posts-scans" class="btn btn-ghost">Posts Scans</a></li>
<li><a href="/watchlist" class="btn btn-ghost">Watchlist</a></li>
</ul>
</div>
<div class="navbar-end">
<div class="dropdown dropdown-end lg:hidden">
<label tabindex="0" class="btn btn-ghost">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</label>
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-52">
<li><a href="/">Dashboard</a></li>
<li><a href="/proxy-gen">Proxy Gen</a></li>
<li><a href="/manage-api">API Management</a></li>
<li><a href="/bot-profile">Bot Profile</a></li>
<li><a href="/marketplace-scan">Marketplace Scan</a></li>
<li><a href="/posts-scans" class="btn btn-ghost">Posts Scans</a></li>
<li><a href="/watchlist" class="btn btn-ghost">Watchlist</a></li>
</ul>
</div>
</div>
</div>
<!-- Main content -->
<main class="container mx-auto p-4 mt-6 bg-base-300 rounded-box">
<!-- Flash Messages Container -->
<div class="mb-4" id="flash-messages">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.category | default('info') }} shadow-lg mb-4 flex justify-between items-center flash-message">
<div>
<span>{{ message.text }}</span>
</div>
<button class="btn btn-sm btn-circle btn-ghost" onclick="this.parentElement.remove()">✕</button>
</div>
{% endfor %}
{% endif %}
</div>
{% block content %}
{% endblock %}
</main>
<script>
// Automatically remove flash messages after 5 seconds
document.addEventListener('DOMContentLoaded', function() {
function removeFlashMessages() {
const flashMessages = document.querySelectorAll('.flash-message');
flashMessages.forEach(function(message) {
// Skip if already fading
if (message.style.opacity === '0') return;
setTimeout(function() {
message.style.transition = 'opacity 0.5s ease';
message.style.opacity = '0';
setTimeout(function() {
message.remove();
}, 500);
}, 5000);
});
}
removeFlashMessages();
const flashContainer = document.querySelector('#flash-messages');
if (flashContainer) {
const observer = new MutationObserver(function() {
removeFlashMessages();
});
observer.observe(flashContainer, { childList: true });
}
});
</script>
</body>
</html>
Le modèle base.html est un modèle de base Jinja2 qui permet d'obtenir une mise en page cohérente pour toutes les pages. Il comprend une barre de navigation, des messages flash et des emplacements réservés pour le titre et le contenu que les modèles enfants peuvent remplacer. Vous trouverez ci-dessous une explication technique de son fonctionnement et de son mécanisme d'extension.
-
Structure HTML :
- Définit un document HTML5 standard avec un attribut
data-theme=« nord »pour le style DaisyUI. - Inclut des ressources externes : DaisyUI CSS, Tailwind CSS et jQuery pour le style et l'interactivité.
- Définit un document HTML5 standard avec un attribut
-
Bloc de titre :
- La balise
<title>contient un bloc Jinja2{% block title %}{% endblock %} - Tornet Scraper. - Les modèles enfants remplacent le bloc
titlepour définir un titre spécifique à la page, qui est ajouté avec « - Tornet Scraper ». - Exemple : un modèle enfant avec
{% block title %}Dashboard{% endblock %}donne<title>Dashboard - Tornet Scraper</title>.
- La balise
-
Barre de navigation :
- Une barre de navigation responsive avec un logo (« Tornet Scraper ») et des liens de navigation (Tableau de bord, Générateur de proxy, etc.).
- Sur les grands écrans (
lg:flex), les liens s'affichent horizontalement ; sur les petits écrans, un menu déroulant (déclenché par une icône hamburger) affiche les liens verticalement.
-
Messages flash :
- Un
<div id="flash-messages">affiche les messages envoyés depuis le backend (par exemple, les notifications de réussite ou d'erreur). - Utilise Jinja2 :
{% if messages %}parcourtmessages(une liste d'objets avectextetcategory). - Chaque message est stylisé comme une
alerteDaisyUI avec une classe dynamique (alert-{{ message.category }}, par défautinfo). - Un bouton Fermer (
✕) supprime le message lorsque vous cliquez dessus. - JavaScript fait automatiquement disparaître et supprime les messages flash après 5 secondes à l'aide d'une transition CSS (
opacity). - Un
MutationObservergarantit que les nouveaux messages flash (ajoutés dynamiquement) sont également supprimés automatiquement.
- Un
-
Bloc de contenu :
- La section
<main>contient un espace réservé{% block content %}{% endblock %}. - Les modèles enfants remplacent ce bloc pour injecter du contenu spécifique à la page dans le conteneur principal, stylisé avec les classes Tailwind/DaisyUI (
container,mx-auto, etc.).
- La section
-
Extension dans les modèles enfants :
- Les modèles enfants utilisent
{% extends “base.html” %}pour hériter de la structure de base. - Ils remplacent
{% block title %}pour le titre de la page et{% block content %}pour le contenu principal. - Cela permet d'afficher la mise en page complète de
base.htmlavec « Dashboard » dans le titre et le contenu spécifié dans la section<main>.
- Les modèles enfants utilisent
Cette configuration garantit une interface utilisateur cohérente, des titres dynamiques, une navigation réutilisable et une gestion conviviale des messages flash sur toutes les pages.
dashboard.html
Le fichier situé dans app/templates/dashboard.html fournit un exemple clair de la manière dont il étend base.html. Bien que ce fichier ne soit pas strictement nécessaire, j'ai pris l'habitude de l'inclure. Voici le code qu'il contient :
<!-- app/templates/dashboard.html -->
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<h1 class="text-3xl font-bold text-base-content mb-4">Dashboard</h1>
{% endblock %}
Remarquez comment il étend base.html ?
Le contenu de {% block content %} est inséré dans le conteneur de contenu principal défini dans base.html :
<!-- Main content -->
<main class="container mx-auto p-4 mt-6 bg-base-300 rounded-box">
{% block content %}
{% endblock %}
</main>
Le titre est également défini :
{% block title %}Dashboard{% endblock %}
Dans base.html, la structure du titre est la suivante :
<title>{% block title %}{% endblock %} - Tornet Scraper</title>
Lorsque vous affichez le code source de la page du tableau de bord, le titre apparaît comme suit :
<title>Dashboard - Tornet Scraper</title>
Au lieu de réécrire le titre pour chaque page, vous pouvez le définir dans base.html, puis définir uniquement le nom de la page pour toutes les autres pages.
Comment fonctionne main.py
Vous trouverez main.py dans app/main.py.
Le fichier main.py est le cœur d'une application web FastAPI. Il définit les routes, les modèles et les interactions avec la base de données pour une interface de scraping web. Vous trouverez ci-dessous une explication technique des variables globales, des routeurs inclus et des fonctions de gestion des routes.
Variables globales
-
app :
- Objectif : instance principale de l'application FastAPI.
- Détails : initialise le framework FastAPI pour gérer les requêtes et les réponses HTTP.
-
BASE_DIR :
- Objectif : stocke le chemin absolu du répertoire racine du projet.
- Détails : dérivé à l'aide de
os.pathpour localiser le répertoireapp/templatespour les modèles Jinja2.
-
TEMPLATES_DIR:- Objectif : spécifie le répertoire des modèles Jinja2.
- Détails : défini sur
app/templatesdans la racine du projet pour le rendu des modèles HTML.
-
templates:- Objectif : instance du moteur de modèles Jinja2.
- Détails : configuré pour charger les modèles à partir de
TEMPLATES_DIRpour le rendu HTML dynamique.
Routeurs inclus
-
proxy_gen_router:- Objectif : enregistre les routes pour la fonctionnalité de génération de proxy.
- Détails : importé depuis
app.routes.proxy_gen, gère les points de terminaison liés au proxy.
-
manage_api_router:- Objectif : enregistre les routes pour la gestion des API.
- Détails : importé depuis
app.routes.manage_api, gère les clés API et les paramètres.
-
bot_profile_router:- Objectif : enregistre les routes pour la gestion des profils de bot.
- Détails : importé depuis
app.routes.bot_profile, gère la configuration des bots.
-
marketplace_api_router:- Objectif : enregistre les routes pour l'analyse du marché.
- Détails : importé depuis
app.routes.marketplace, gère la récupération des données du marché.
-
posts_api_router:- Objectif : enregistre les routes pour l'analyse des publications.
- Détails : importé depuis
app.routes.posts, gère les opérations liées aux données des publications.
-
watchlist_api_router:- Objectif : enregistre les routes pour la gestion de la liste de surveillance.
- Détails : importé depuis
app.routes.watchlist, gère les éléments de la liste de surveillance et les analyses.
Fonctions du gestionnaire de routes
-
dashboard:- Objectif : affiche la page du tableau de bord.
- Paramètres clés :
request: objetRequestFastAPI pour le contexte du modèle.db:SessionSQLAlchemy pour l'accès à la base de données (viaDepends(get_db)).
- Retourne :
TemplateResponserendantdashboard.htmlavec les messages flashés de la session.
-
proxy_gen:- Objectif : Affiche la page de génération de proxys avec une liste de proxys.
- Paramètres clés :
request: objet FastAPIRequest.db: SQLAlchemySessionpour interroger les proxys.
- Retourne :
TemplateResponserendantproxy_gen.htmlavec les données proxy (nom du conteneur, IP, nœud de sortie Tor, horodatage, état d'exécution) et les messages flashés.
-
manage_api:- Objectif : affiche la page de gestion des API avec les détails des API.
- Paramètres clés :
request: objet FastAPIRequest.db: SQLAlchemySessionpour interroger les API.
- Retourne :
TemplateResponserendantmanage_api.htmlavec les données API (ID, nom, fournisseur, clé, modèle, nombre maximal de jetons, invite, horodatage, état actif) et les messages flashés.
-
bot_profile:- Objectif : affiche la page de gestion du profil du bot.
- Paramètres clés :
request: objet FastAPIRequest.db: SQLAlchemySessionpour interroger les profils des bots et la dernière URL onion.
- Retourne :
TemplateResponserendantbot_profile.htmlavec les données du profil du bot (ID, nom d'utilisateur, mot de passe masqué, objectif, proxy Tor, horodatage), la dernière URL onion et les messages flashés.
-
marketplace:- Objectif : Rend la page de scan du marché avec pagination et données de scan des publications.
- Paramètres clés :
request: Objet FastAPIRequest.db: SQLAlchemySessionpour interroger les scans.
- Retourne :
TemplateResponseaffichantmarketplace.htmlavec pagination des scans, post-scans et messages flashés.
-
posts_scans:- Objectif : affiche la page d'aperçu des post-scans.
- Paramètres clés :
request: objet FastAPIRequest.db: SQLAlchemySessionpour l'accès à la base de données.
- Retourne :
TemplateResponserendantposts_scans.htmlavec des messages flashés.
-
posts_scan_result:- Objectif : affiche les résultats d'un scan de publication spécifique.
- Paramètres clés :
scan_id: ID entier du scan.request: objet FastAPIRequest.db: SQLAlchemySessionpour l'accès à la base de données.name: nom facultatif du scan (chaîne de caractères, vide par défaut).
- Retourne :
TemplateResponserendantposts_scan_result.htmlavec l'ID du scan, son nom et les messages flashés.
-
watchlist:- Objectif : affiche la page d'aperçu de la liste de surveillance.
- Paramètres clés :
request: objet FastAPIRequest.db: session SQLAlchemy pour l'accès à la base de données.
- Retourne :
TemplateResponserendantwatchlist.htmlavec les messages flashés.
-
watchlist_profile:- Objectif : Affiche le profil d'un élément spécifique de la liste de surveillance et les scans associés.
- Paramètres clés :
target_id: ID entier de l'élément de la liste de surveillance.request: objet FastAPIRequest.db: SQLAlchemySessionpour interroger l'élément de la liste de surveillance et les analyses.
- Renvoie :
TemplateResponserendantwatchlist_profile.htmlavec l'élément de la liste de surveillance, les données d'analyse et les messages flashés ; lève une exceptionHTTPException(404 pour un élément manquant, 500 pour des erreurs serveur).
Fonctionnement des bases de données
Dans notre application FastAPI, db.py gère les configurations du moteur de base de données, tandis que models.py définit le schéma et les tables de la base de données. L'utilisation d'un fichier db.py séparé permet de passer facilement d'un type de base de données à l'autre. Bien que tornet_scraper utilise SQLite3 pour le prototypage et les tests, ce n'est pas idéal pour le scraping de données à grande échelle. Pour les environnements de production, nous recommandons de passer à une base de données plus robuste comme PostgreSQL.
Vous pouvez également implémenter une logique dans db.py pour basculer dynamiquement entre SQLite3 et un serveur de base de données en fonction de conditions ou de l'environnement (par exemple, développement, test ou production).
db.py
Le fichier db.py configure la connexion à la base de données et la gestion des sessions pour une application FastAPI utilisant SQLAlchemy. Vous trouverez ci-dessous une explication concise de ses composants et de ses fonctionnalités.
Vous trouverez db.py dans app/database/db.py.
-
Objectif :
- Configure une connexion à la base de données SQLite, crée des tables et fournit une dépendance pour les sessions de base de données dans les routes FastAPI.
-
Variables globales :
BASE_DIR:- Objectif : définit le répertoire racine du projet.
- Détails : utilise
Path(__file__).resolve().parent.parent.parentpour calculer le chemin absolu vers la racine du projet, garantissant ainsi que le fichier de base de données est correctement situé.
DATABASE_URL:- Objectif : Spécifie la chaîne de connexion à la base de données.
- Détails : Configuré pour SQLite comme
sqlite:///{BASE_DIR}/tornet_scraper.db, pointant vers un fichiertornet_scraper.dbdans la racine du projet.
engine:- Objectif : moteur SQLAlchemy pour les interactions avec la base de données.
- Détails : créé avec
create_engine(DATABASE_URL, connect_args={« check_same_thread »: False})pour gérer les connexions SQLite. L'argumentcheck_same_threadpermet d'utiliser SQLite dans un environnement FastAPI multithread.
SessionLocal:- Objectif : Usine pour créer des sessions de base de données.
- Détails : Configuré avec
sessionmaker(autocommit=False, autoflush=False, bind=engine)pour créer des sessions liées au moteur, avec contrôle manuel de la validation et du vidage.
-
Fonctions :
get_db:- Objectif : fournit une session de base de données pour les routes FastAPI.
- Paramètres clés : aucun (fonction de dépendance).
- Retourne : renvoie une session SQLAlchemy (
db) et s'assure qu'elle se ferme après utilisation. - Détails : utilisé comme dépendance FastAPI (via
Depends(get_db)) pour injecter une session dans les gestionnaires de routes. Le bloctry/finallygarantit la fermeture de la session, empêchant ainsi les fuites de ressources.
init_db:- Objectif : Initialise la base de données en créant des tables.
- Paramètres clés : Aucun.
- Retourne : Aucun.
- Détails : importe
Basedepuismodels.pyet appelleBase.metadata.create_all(bind=engine)pour créer toutes les tables définies (par exemple,proxies,apis) dans la base de données SQLite si elles n'existent pas.
-
Utilisation :
- Objectif : active les opérations de base de données dans l'application.
- Détails :
init_dbest appelé dansmain.pypour configurer le schéma de la base de données au démarrage.get_dbest utilisé dans les gestionnaires de routes (par exemple,/proxy-gen,/manage-api) pour interroger ou modifier les données des tables définies dansmodels.py. La session garantit l'intégrité des transactions et le nettoyage correct des ressources.
models.py
Le fichier models.py définit le schéma de base de données pour une application FastAPI à l'aide de SQLAlchemy, en spécifiant les tables, les colonnes et les relations. Vous trouverez ci-dessous une explication concise de ses composants et de ses fonctionnalités.
Vous trouverez models.py dans app/database/models.py.
-
Présentation de SQLAlchemy :
- Objectif : SQLAlchemy est une bibliothèque ORM (mappage objet-relationnel) pour Python, qui permet d'interagir avec une base de données relationnelle à l'aide d'objets Python.
- Détails : mappe les classes Python aux tables de la base de données, permettant ainsi les opérations CRUD via une syntaxe orientée objet. Utilise
declarative_basepour créer une classe de base (Base) pour les définitions de modèles.
-
Colonnes :
- Objectif : représentent les champs d'une table de base de données.
- Détails : définies à l'aide de la classe
Columnde SQLAlchemy, qui spécifie des attributs tels que le type de données (par exemple,Integer,String,DateTime), les contraintes (par exemple,primary_key,unique,nullable) et les valeurs par défaut (par exemple,datetime.utcnow). Les colonnes stockent les données de chaque enregistrement d'une table.
-
Tables :
- Objectif : représenter les tables de base de données qui stockent des données structurées.
- Détails : chaque classe (par exemple,
Proxy,APIs) hérite deBaseet définit une table via__tablename__. Les tables comprennent des colonnes et des contraintes facultatives (par exemple,UniqueConstraintpour les combinaisons uniques de champs). Exemples :proxies,apis,bot_profiles, etc.
-
Relations :
- Objectif : définir des associations entre les tables pour les requêtes relationnelles.
- Détails : établies à l'aide de colonnes
ForeignKey(par exemple,MarketplacePost.scan_idfait référence àmarketplace_post_scans.id). Les relations permettent de joindre des tables, comme lierMarketplacePostàMarketplacePostScanviascan_id. SQLAlchemy les traite comme des références d'objets dans les requêtes.
-
Énumérations :
- Objectif : définir des vocabulaires contrôlés pour des champs spécifiques.
- Détails : utilise
enum.Enumde Python (par exemple,BotPurpose,ScanStatus) pour limiter les valeurs des colonnes à des options prédéfinies (par exemple,SCRAPE_MARKETPLACE,RUNNING). Stockées sous forme de chaînes dans la base de données viaEnum.
-
Composants clés :
Base: Classe de base SQLAlchemy pour tous les modèles, utilisée pour générer des schémas de table.- Définitions de table : Des classes telles que
Proxy,APIs,BotProfile, etc. définissent des tables pour stocker les détails des proxys, les configurations API, les profils de bot, les URL onion, les analyses de marché, les publications et les listes de surveillance. - Contraintes : les contraintes uniques (par exemple,
UniqueConstraintdansMarketplacePost) garantissent l'intégrité des données en empêchant les enregistrements en double basés sur des combinaisons de colonnes spécifiques. - Horodatage : de nombreuses tables comprennent une colonne
timestamp(par défautdatetime.utcnow) pour suivre les heures de création/mise à jour des enregistrements.
-
Utilisation :
- Objectif : les modèles sont utilisés par l'application FastAPI pour interagir avec la base de données.
- Détails : les routes
main.pyinterrogent ces modèles (via des sessions SQLAlchemy) pour récupérer ou stocker des données, qui sont ensuite transmises aux modèles Jinja2 pour être rendues ou traitées dans la logique API. Par exemple, les donnéesProxysont interrogées dans la route/proxy-genpour afficher les détails du proxy.
Comment fonctionnent les routes
Dans le développement d'API, il est essentiel d'organiser efficacement les points de terminaison API, car il n'est pas pratique de router toutes les requêtes via un seul point de terminaison.
Par exemple, le format /api/posts/get-post indique que /api/posts/* est dédié aux opérations liées aux publications, telles que l'affichage, la suppression ou la modification de publications. Tous les appels API associés sont préfixés par /api/posts/.
Étant donné que notre application comprend des fonctionnalités telles que la génération de proxys, la gestion des bots, le scraping de marketplaces, le scraping de publications, la surveillance des menaces, etc., il est essentiel d'organiser correctement les points de terminaison.
Vous trouverez toutes les routes dans app/routes/*.py, qui contient six fichiers Python. Voici un exemple de bot_profile.py :
# app/routes/bot_profile.py
import logging
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from pydantic import BaseModel
from app.database.models import BotProfile, OnionUrl, BotPurpose, APIs
from app.database.db import get_db
from typing import Optional
from app.services.tornet_forum_login import login_to_tor_website
from app.services.gen_random_ua import gen_desktop_ua
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
bot_profile_router = APIRouter(prefix="/api/bot-profile", tags=["API", "Bot Profile Management"])
# Pydantic models for validation
class BotProfileCreate(BaseModel):
username: str
password: str
purpose: str
tor_proxy: Optional[str] = None
session: Optional[str] = None
class BotProfileUpdate(BaseModel):
username: Optional[str] = None
password: Optional[str] = None
purpose: Optional[str] = None
tor_proxy: Optional[str] = None
user_agent: Optional[str] = None
session: Optional[str] = None
class OnionUrlCreate(BaseModel):
url: str
# Get all bot profiles
@bot_profile_router.get("/list")
async def get_bot_profiles(db: Session = Depends(get_db)):
try:
profiles = db.query(BotProfile).all()
return [
{
"id": p.id,
"username": p.username,
"password": "********",
"actual_password": p.password,
"purpose": p.purpose.value,
"tor_proxy": p.tor_proxy,
"has_session": bool(p.session and len(p.session) > 0),
"session": p.session,
"user_agent": p.user_agent,
"timestamp": p.timestamp.isoformat()
} for p in profiles
]
except Exception as e:
logger.error(f"Error fetching bot profiles: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
Nous utilisons la journalisation pour suivre les événements au sein de l'application. La journalisation est essentielle pour comprendre ce qui se passe dans votre application web ; sans elle, vous avancez à l'aveuglette. Bien que les journaux puissent être enregistrés dans un fichier, j'évite généralement de le faire lors du développement local, mais je le recommande pour les environnements de production.
Cependant, l'aspect le plus critique est la création de routeurs :
bot_profile_router = APIRouter(prefix="/api/bot-profile", tags=["API", "Bot Profile Management"])
Ce routeur établit un préfixe de point de terminaison, /api/bot-profile, qui sera utilisé par tous les points de terminaison associés.
Par exemple, la fonction suivante récupère tous les profils de bot à partir de la table BotProfile :
@bot_profile_router.get("/list")
async def get_bot_profiles(db: Session = Depends(get_db)):
Lorsque vous utilisez @bot_profile_router.get(« /list »), le point de terminaison est préfixé par /api/bot-profile, ce qui donne /api/bot-profile/list.
Cette structure permet d'organiser efficacement des centaines ou des milliers d'itinéraires API. Le bot_profile_router est importé dans main.py et enregistré.
Comment fonctionnent les scrapers
Tous les scrapers sont stockés dans app/scrapers/*.py.
Ces fichiers sont généralement importés en tant que modules dans d'autres parties du code source. Par exemple, dans marketplace_scraper.py, la fonction create_pagination_batches prend en entrée l'URL d'une page web, telle que :
http://site.url/pagination?page=1
Elle crée un lot de paginations comme ceci :
http://site.url/pagination?page=1
http://site.url/pagination?page=2
http://site.url/pagination?page=3
--- snip ---
http://site.url/pagination?page=10
Cette approche est efficace pour la répartition des tâches, par exemple pour attribuer chaque lot de 10 pages de pagination à un seul bot pour le scraping.
Comment fonctionnent les services
Tous les services se trouvent dans app/services/*.py.
Les services sont également utilisés comme modules, mais ils ont une fonction distincte, puisqu'ils gèrent des tâches telles que :
- Gestion de Docker pour les proxys
- Génération de proxys
- Connexion
- Contournement des CAPTCHA
Comme vous le verrez plus tard, nous utilisons des conteneurs Docker pour créer des proxys Tor localement. Lorsqu'un proxy n'est plus nécessaire, il doit être supprimé. Pour vérifier si les proxys sont en cours d'exécution, vous pouvez utiliser une fonction telle que container_running de container_status.py pour vérifier l'état des conteneurs Docker.