Je me suis intéressé récemment à faire des requêtes de al façon la plus basique possible. Loin des librairies qui vous masquent ce qui se passe sous le capot, on va voir les technique dans le shell qui permettent de faire des requêtes réseau, je suis sûr que je n’aurais pas la technique la plus bas niveau mais on va s’en rapprocher.
CURL
Ce programme est mondialement connu, il est présent partout, dans toutes les plateformes. Il est présent dans tous les langages. Par exemple la librairie curl en PHP permet de faire tout type de requête réseau. Voyons comment en shell nous pouvons faire une requête basique
curl https://yvonh.com/ -o yvonh.html
cat yvonh.html //lire le fichier généré qui est vide
L’argument -o va rediriger l’output dans un fichier texte avec extension HTML.
Dans ce cas précis vous n’allez rien voir, car il y a une redirection vers www.yvonh.com, or par défaut Curl ne suit pas la redirection. Il va retourner un résultat vide. La requête suivante va instruire Curl de suivre les redirections.
curl -L https://yvonh.com -o yvonh.html
On en va pas voir en détail toutes les commandes de Curl, on va voir les autres méthode de requêtage réseau
wget
wget est une commande très souvent utilisée
wget -qO- https://www.example.com
openssl
openssl s_client -connect www.example.com:80
Il y a aussi d’autres commandes mais qui ne sont pas toutes présentes dans Linux
Support for password authentication was removed on August 13, 2021
Depuis cette date, il est impossible de cloner un repository privé, car pour cloner un repository privé il faut s’authentifier. Pour remplacer le mot de passe, Github utilise le PAT : Personal Access Token.
Comment créer le PAT avec Github
Pour créer le PAT, allez dans Settings sur la page d’accueil de votre Github, puis sur la barre latérale gauche, cliquer sur Developer Settings, puis sur Personal Access tokens, puis dans la liste choisissez Tokens (Classic)
Cliquez sur Generate new token dans la liste déroulante et choisissez classic.
Pour les opération usuelles, cliquez la checkbox repo, et choisissez une durée de validité, 30 jours par défaut. Une fois généré, copiez la chaine PAT en lieu sûr; car vous ne le verrez plus dans votre Github. (Evitez de le mettre dans votre repository !!!)
Comment utiliser le PAT pour cloner?
Le PAT remplace en lieu et place votre mot de passe, il est plus long et plus secret, et a une durée de vie déterminée, contrairement à votre mot de passe, il est donc plus sûr.
Donc suite à la commande git clone https://…
Username for 'https://github.com': votre_nick
Password for 'https://votre_nick@github.com
Au moment d’entrer le password, vous collez simplement votre PAT (Personal Access Token)
Ou alors si vous renseignez votre PAT ça va plus vite:
Vous devez avoir fait la création d’une paire de clé privée-public. Ensuite lors de la création de votre droplet, vous aurez uploadé la clé publique sur digitalOcean.
Nous allons faire la connexion avec Putty
D’abord localisez l’adresse IP de votre Droplet, ouvrez Putty et mettez l’adresse IP. Puis allez dans Auth, puis ajoutez votre clé privée au format .ppk Le soucis c’est qu’avec ssh-keygen, vous ne générez pas de .ppk, il vous faudra l’utilitaire Puttygen.
Création de la clé privée au format .ppk
Dans Puttygen, chargez votre clé privée et sauvez la.
Importez la clé
Sélectionnez votre clé privée (la clé sans extension)
Chargez la clé
Vous pouvez sauver votre clé sans passphrase (c’est plus simple)
Retour dans Putty
Ajout de l’utilisateur, aller dans Connection > data
Sauvegarder les données de la session pour ne pas avoir à les retaper
Dans le cas où cela ne marcherait pas essayez de télécharger la dernière version de Putty, j’étais pour ma part en 0.72 et en mettant à jour vers la 0.81 ça marche.
Si vous vouslez accéder à votre console et que l’écran tourne en boucle, c’est que sans doute vous avez touché au FireWall. Vous avez touché au FireWall à l’occasion de l’installation de Nginx.
Pour recouvrer l’accès à votre console, il faut utiliser la Recovery Console, pour y accéder, aller dans Access dans la page principale du droplet.
Après le démarrage de la Recovery Console, il vous sera demandé de rentrer le mot de passe du user root, le problème est que vous n’avez pas de mot de passe root si vous avez choisi d’accéder pas clé SSH.
Il vous faudra donc resetter votre mot de passe root, en choisissant l’option juste après la Recovery Console, Reset Root Password.
Vous voila prêt à changer la configuration du Firewall une fois loggé
Une fois que vous avez créé votre droplet, il est conseillé de créer un utilisateur sudoable, sinon faites les opérations suivantes avec l’utilisateur root.
Nous allons mettre à jour vos paquets et installer Nginx
sudo apt update
sudo apt install nginx
Si vous êtes sous root pas la peine de mettre sudo. Sudo permet d’élever les privilèges d’un utilisateur non root.
Paramétrage du firewall ufw (uncomplicated Fire Wall)
sudo ufw app list // liste les applications connue par ufw
Available applications:
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
Si on n’a pas encore configuré un SSL pour le serveur, on va utiliser le profil Nginx HTTP.
>ufw allow 'Nginx HTTP'
Afficher le status du firewall
>ufw status
Status: inactive // si vous avez ça c'est que ufw n'est pas activé
faite la commande suivante:
>ufw enable
>ufw status
Status: active
To Action From
-- ------ ----
Nginx HTTP ALLOW Anywhere
Nginx HTTP (v6) ALLOW Anywhere (v6)
Vérifier que le serveur web tourne
A la fin de l’installation, Unbuntu démarre le serveur Nginx. Nous allons vérifier avec la commande
systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Sat 2024-07-27 21:06:47 UTC; 16min ago
Docs: man:nginx(8)
Process: 2697 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUC>
Process: 2699 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 2701 (nginx)
Tasks: 2 (limit: 509)
Memory: 2.2M (peak: 2.3M)
CPU: 16ms
CGroup: /system.slice/nginx.service
├─2701 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
└─2702 "nginx: worker process"
Jul 27 21:06:47 ubuntu-s-1vcpu-512mb-10gb-ams3-01 systemd[1]: Starting nginx.service - A high performance web se>
Jul 27 21:06:47 ubuntu-s-1vcpu-512mb-10gb-ams3-01 systemd[1]: Started nginx.service - A high performance web ser>
lines 1-16/16 (END)
troisième ligne indique active running.
Important ! autorisez le SSH, sinon vous risquez de ne plus avoir accès à la console, si c’est le cas voici une solution
ufw allow ssh
Nous allons visiter dans le navigateur une page pour vérifier que Nginx fonctionne. Faite la commande pour connaitre l’IP qui doit vous afficher une page:
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
165.232.90.185
10.18.0.5
fe80::28e7:4eff:fee5:b7fe
//commande alternative
curl -4 icanhazip.com
Je passe par l’IP V4, et j’obtiens bien une page web, la page d’accueil de Nginx.
Dans le contexte de GitHub Actions, une action est une application personnalisée pour la plateforme GitHub Actions qui automatise une tâche spécifique ou un ensemble de tâches au sein d’un workflow. Les actions sont les éléments de base des workflows et peuvent être réutilisées dans plusieurs workflows et dépôts.
Points Clés sur les Actions
Unités de Code Réutilisables : Les actions sont conçues pour être réutilisables. Vous pouvez les partager entre différents dépôts et avec la communauté GitHub.
Préconstruites ou Personnalisées : Vous pouvez utiliser des actions préconstruites disponibles sur le GitHub Marketplace ou créer vos propres actions personnalisées adaptées à vos besoins spécifiques.
Encapsulent des Tâches : Les actions encapsulent des tâches telles que le checkout du code, la configuration des environnements, l’exécution de tests et le déploiement des applications.
Configurables : Les actions acceptent souvent des entrées et produisent des sorties, ce qui permet de les personnaliser et de les composer ensemble pour créer des workflows complexes.
Types d’Actions
Actions JavaScript : Actions construites en utilisant Node.js qui peuvent exécuter du code JavaScript.
Actions de Conteneur Docker : Actions qui s’exécutent dans un conteneur Docker, ce qui vous permet de les empaqueter avec toutes leurs dépendances.
Actions Composites : Actions qui combinent plusieurs étapes en une seule action, définie en utilisant une série de commandes shell et d’autres actions.
Qu’est ce que actions/checkout@v3?
C’est une action prédéfinie de Github, qui clone un repository dans l’environnement du workflow.
Glossaire Github action:
logs
uses : est utlisé pour sélectionner une action déjà définie dans un docker, dans un repository.
name: le nom du workflow
on : écouteur d’événement
artifact
runs-on: spécifie le type de machine (ou « runner ») sur laquelle un job doit s’exécuter. Un runner est un serveur qui exécute les workflows définis dans vos fichiers YAML. Un runner peut être auto-hébergé.
Types de Runners Disponibles
GitHub Actions offre plusieurs types de runners hébergés (préconfigurés par GitHub) que vous pouvez utiliser :
ubuntu-latest : Une machine virtuelle exécutant la dernière version stable d’Ubuntu.
ubuntu-20.04 : Une machine virtuelle exécutant Ubuntu 20.04.
ubuntu-22.04 : Une machine virtuelle exécutant Ubuntu 22.04.
windows-latest : Une machine virtuelle exécutant la dernière version stable de Windows.
windows-2019 : Une machine virtuelle exécutant Windows Server 2019.
macos-latest : Une machine virtuelle exécutant la dernière version stable de macOS.
macos-11 : Une machine virtuelle exécutant macOS 11 (Big Sur).
Rsync permet de synchroniser des dossiers, où qu’ils soient pourvu que deux machine aient accès via des clés SSH.
Par exemple soient les machines Msource et Mtarget. Msource doit générer une clé SSH et mettre la clé publique dans Mtarget. Ainsi Msource pourra se connecter sans mot de passe à Mtarget.
Bien que su rLinux Rsync soit disponible par défaut, sur Window c’est assez compliqué de l’installer.
Nous partirons du postulat qu’on est sous Linux et que Rsync est présent.
Synchronisation d’un fichier avec Rsync
rsync -a monfichier.txt login_mtarget@mtarget.com:/home/public_html
Synchronisation d’un dossier avec Rsync
rsync -a repertoire1 login_mtarget@mtarget.com:/home/public_html
Tous les fichier du repertoire1 seront copié dans le répertoire /home/public_html de la machine cible Mtarget.
Vous savez sans doute uploader via un formulaire une image vers un serveur, et aussi via ajax (c’est un peu plus compliqué). Mais il est possible d’uploader en Ajax progressivement par morceau (chunk en anglais).
La fonction uploadBlob() va prendre le fichier qui est passé en argument, puis le décomposer dans une boucle en plusieurs chunk de taille fixe. Notre boucle se termine quand on a atteint la fin du fichier.
C’est plutôt simple côté backend, le code est synchrone. Il retourne la quantité de bytes écrits ( 1 byte = 1 octet)
Essai d’upload par paquet en javascript
Nous allons prendre un fichier qui fait plus de 100000 octets pour déclencher la découpe. Pour l’exemple j’ai une image de 811 Ko nommée cpu4.png. Cela fait combien de paquets (chunks) à envoyer? Simple division 811000 / 100000 = 9. Le dernier paquet fait moins de 100000 octets ou bytes.
On ouvre les devtools, et effectivement j’ai 9 envois.
Si vous essyez d’ouvrir l’image cependant, vous ne pourrez pas la visualiser. En fait l’image uploadée est différente de l’image choisie, vous n’avez qu’à comparer leurs checksum sur un outil comme celui-ci.
Mais comment est ce possible? Regardez l’image ci-dessus, le fichier php renvoit la taille écrite en guise de réponse, et l’avant dernier paquet montre 29669 octets, alors que ce devait être le dernier paquet qui doit montrer ce nombre ! Bref les paquets sont envoyés dans le désordre !! Nous allons corriger cela simplement avec async/await, qui va rendre un peu de synchronisme dans la boucle for.
Async/await à la rescousse.
Nous allons modifier le code de la façon suivante : uploadBlob sera async et on « awaitera » fetch.
async function uploadBlob(file) {
//reader = new FileReader() // le file reader va lire à partir d'une position donnée
const chunkSize = 100_000;
// const filename = file.name;
let start = 0
let i = 0
for (let start = 0; start < file.size; start += chunkSize) {
console.log("morceau ", i, "uploadé")
i++
let end = start + chunkSize
const chunk = file.slice(start, end);
const fd = new FormData();
fd.set('file', chunk,);
await fetch(url, {
method: "POST",
body: chunk
})
.then(response => response.text())
}
}
Maintenant le dernier paquet est bien le plus petit. Tous les paquets sont envoyés dans l’ordre. Nous pouvons vérifier que l’image s’ouvre bien. Vérifiez les checksum de ces deux fichiers, ils sont pareil.
Il est à noter que dans le cas des envois en désordre, la taille du fichier reçu est variable !
Récemment, pour un projet open source, j’ai voulu afficher un classement des contributeur en fonction du nombre de commits. J’ai tout de suite eu l’idée de passer par le webservice de Github (alors que je n’en ai jamais entendu parler).
Il est normal que Github ait un webservice, cela permet de faire des nitégration un peut partout (plugin git par exemple).
Comment mener sa réflexion du webservice à l’affichage des badges pour récompenser les contributeurs?
L’idée de base est de proposer un classement des contributeurs en fonction du nombre de pull requests. Pour avoir les pull request, il faut avoir les commits sur la branch principale (dans notre cas develop).
Il a fallut d’abord trouver un moyen via la documentation de lister les commits d’une branche. Deplus j’ai pris le parti d’utiliser un token pour avoir moins de restriction sur les appels.
Exemple d’appel sans avoir besoin de clé:
https://api.github.com/user/repos
Appel avec un clé
Il faut d’abord générer une clé, plus exactement un PAT (personal Access Token). Pour ce faire aller à Settings, puis dans la barre de gauche aller dans Developper…
Générer le PAT, sélectionner les droits de ce PAT, je n’ai besoin qu’en lecture, donc cocher comme dans l’image. Je vous propose dans un premier temps d’expérimenter avec Postman, pour ceux qui ne le connaissent pas encore, Postman permet de faire des requêtes HTTP sans navigateur, pratique pour tester des webservices.
Vous aurez les repository du propriétaqire de votre token. Maintenant listons les commits d’une branches, il a fallu explorer la documentation, ne la baclez pas, la documentation est riche.
// lister les branches
https://api.github.com/repos/refschool/Clone-Weetransfert/branches
// lister les commits de la branche main
https://api.github.com/repos/refschool/Clone-Weetransfert/branches/main
// lister les commits de la branche par défaut qui est main
https://api.github.com/repos/refschool/Clone-Weetransfert/commits
// lister les commits de la branche develop avec 100 résultats par page.
https://api.github.com/repos/refschool/Clone-Weetransfert/commits?per_page=100&sha=develop
Il faut essayer de se familiariser avec la pagination le jour où l’on a vraiment beaucoup de commit.
Dans cet article on va voir comment déployer des container docker d’une application multicontainer dans un VPS à l’aide de Docker-compose. Sur DigitalOcean, la manipulation est plus facile que sur Azure ou AWS (le plus complexe)
Création du Droplet
Un droplet sur DigitalOcean (un prestataire Cloud abordable et pas trop compliqué à utiliser),
1/Créer un compte sur DigitalOcean
2/créer un droplet l’équivalent d’un VPS, choisir Ubuntu comme OS avec Docker préinstallé en vous aidant du Marketplace. Vous créerez par la même occasion un projet qui apparaitra dans la sidebar.
Ensuite vous choisissez le Datacenter le plus proche de chez vous, en France l’on choisira Amsterdam par exemple.
Dans la section « Choose an image« , cliquez sur l’onglet Marketplace, puis sur l’image Docker on Ubuntu, ici l’image représente une machine virtuelle bien sûr et pas une image Docker.
Ensuite choisissez les options les moins chères, dans Droplet Type, choisissez « Basic« , dans CPU options, choisissez « Regular« , et la formule au mois la moins chère.
Dans « Choose Authentication Method« , choisissez Password pour faire simple, et SSH Key pour faire pratique (pas de mémorisation de mot de passe et login nécessaire). Allez sur ce post pour générer et mettre votre clé sur le service. Enfin cliquez sur « Create Droplet ». Au bout d’un certain temps votre droplet sera créée avec Docker installé, cependant Docker-Compose ne sera pas encore installé. Il vous faudra faire via la console.
Si vous voulez installer à la main Docker, les instruction sont sur cette page :
Pour vous connecter en console dans votre Droplet dans la sidebar « Projects« , cliquez sur votre projet
Installer docker-compose
Une fois dedans cliquez sur « Console« , cela ouvrira un shell. Installez docker-compose
///sudo apt-get install docker-compose-plugin
ou tapez docker-compose, une erreur avec un message sur comment installer docker-compose s'affichera.
apt install docker-compose
Vous êtes en connecté en root, allez à /home et cloner le projet. Le projet consiste en un front end en ReactJS, un backend en Node/Express et de MongoDB. Ce dernier est directement tiré du hub de Docker. Alors que le front et le back vont être transformé en image via leurs Dockerfiles respectifs.
On clone soit en SSH car Github ne supporte plus l’authentification par login et mot de passe. ou alors il faut utiliser un PAT personal access token. L’ennui avec la clé SSH est qu’il faut en créer une sur le droplet et la mettre sur Github, cela fait beaucoup de manipulations. Le mieux est de créer le PAT sur Github. Il s’utilise comme un mot de passe.
Copie du fichier docker-compose-prod.yml depuis votre poste vers votre droplet
A ce stade il n’y a pas grand d’installé ni de configuré pour votre droplet (pas de FTP etc). Le moyen le plus simple d’envoyer un fichier vers le droplet est d’utiliser l’utilitaire rsync pour envoyer ce fichier. Mais le problème est que ce n’est pas simple d’avoir rsync sur Windows (pas un problème sur Linux). On va utiliser scp depuis le terminal DOS.
Comme le fichier docker-compose-prod.yml est dans le git, pas besoin de faire cette manipulation finalement.
Démarrage du docker-compose avec le fichier de prod
Dans la racine du projet où il y a le fichier docker-compose-prod.yml
docker-compose -f docker-compose-prod.yml up -d
Cette unique ligne va créer ou importer les images, démarrer les containers et in fine votre application.
Accès à l’application sur DigitalOcean
Si tout s’est bien passé, vous allez pouvoir accéder à votre application sur le droplet à l’adresse IP que vous pouvez retrouver dans votre interface chez DigitalOcean, sur le port 3000.
Les problèmes rencontrés et leurs solutions
Problème de CORS
La preière fois que vous accédez à votre application, vous allez rencontrer ce message « Could not fetch movie »
Ceci est vraisemblablement dû à un problème de CORS (cross origin), si on ouvre les devtools, onglet Réseau, en inspectant la requête, vous aurez les indices. La requête est faite à http://localhost:3001/api alors que notre serveur possède une adresse IP (voir image)
En effet en phase de développement on a http://localhost:3001/api (voir le fichier src/services/api.js , si la variable d’environnement REACT_APP_API_URL est settée, elle surchargera localhost.
A la racine du projet, il y a un fichier docker-compose-prod.yml. Dans le Dockerfile du front il vous faudra remplacer dans le fichier Dockerfile après COPY
Vérifiez aussi que dans le dossier backend, les fichiers docker-entrypoint.sh et wait-for soient exécutables en faisant la commande chmod +x <fichier>.
Une fois que la commande docker-compose est faite, vous avez la main dans le shell, Inspectez les containers
docker ps
//Voir les logs d'un container
docker logs <id-container>
//Par exemple si le container du backend ne fonctionne pas correctement, lisez les logs.
//Pour arrêter
docker-compose down
//Pour relancer avec un build d'images
docker-compose -f docker-compose-prod.yml up -d --build
//script1.js
let email = document.querySelector('#email')
let password = document.querySelector('#password')
let btn = document.querySelector('#btn')
//script2.js
email.addEventListener('keyup', (event) => {
if (!isEmailValid(email.value)) {
console.log('Email is invalid')
} else {
console.log('Email is valid')
}
})
password.addEventListener('keyup', (event) => {
console.log('Password length ', password.value.length)
})
// Vérifie si l'email est correct
const isEmailValid = (email) => {
return email.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
}
//script3.js
btn.addEventListener('click', (event) => {
event.preventDefault()
if (isEmailValid(email.value) && password.value.length > 8) {
alert("Form is valid")
} else {
alert("Form is invalid")
}
})
Si votre page HTML fait appel à l’inclusion de beaucoup de fichier JS, vous aurez envie de fusionner tous ces fichiers en un seul pour le rendre plus compact. je vous propose d’utiliser Rollup, qui est simple comparé à Webpack. On va installer Rollup
Un socket est une interface de communication entre deux noeuds dans un réseau informatique. Plus concrètement, entre deux ordinateurs, et pour les développeurs web entre un serveur et un client, aka votre ordinateur et plus précisément le navigateur.
Côté serveur
On va pour simplifier utiliser Nodejs, avec un script en local on va pouvoir en quelques ligne mettre en oeuvre un socket grâce à la librairie socket.io
Côté client
Grâce à une librairie socket.io-client, on va pouvoir se connecter à un socket.
Quelle est la différence entre un socket et une requête AJAX?
Dans le monde du HTTP, une communication entre client et serveur ne dure qu’un instant, entre deux communications il n’y a rien. Vous faites une requête AJAX, le client initie une demande (Request), le serveur réponse avec un Response, et puis c’est fini.
Dans le cas d’un socket, une fois que le client initie une demande de connexion, la communication s’établie et dure tant que l’on n’a pas arrêté, elle ne s’arrête pas toute seule. Le socket sert à maintenir une connexion persistente, avec un flot continu de données de part et d’autre.
Le socket répond au besoin de recevoir contnuellement des données, une application comme une courbe de bourse en temps réel a besoin d’un socket, cela permet une mise à jour temps réel du cours de bourse. Bien sû on pourrait faire toute les secondes une requête AJAX, mais c’est très consommateur de données, de par la nature du protocole HTTP. Socket ne fait pas partie de la technologie HTTP.
Démarrer un projet avec socket.io
Créez un nouveau répertoire de projet, ouvrez le avec VSCode, ouvrez un terminal sous VSCode.
vous pouvez lancer le serveur NodeJS avec la commande:
node index.js
ça va initialiser un serveur qui écoute sur le port 3000 (par défaut pour NodeJS). Si dans votre navigateur, vous ouvrez l’url http://localhost:3000. Gardez un oeil sur le terminal de VSCode également.
Ajout d’un fichier HTML
Nous allons améliorer notre code avec une page HTML à servir
Note : Pour éviter de redémarrer à la main à chaque fois qu’il y a une mise à jour du code, je vous suggère d’installer nodemon, un démon NodeJS, qui va redémarrer le serveur automatiquement à chaque enregistrement de fichier.
npm install --save-dev nodemon // installe en tant que dépendance de développement
// redémarrer le serveur
nodemon ./index.js localhost 3000
Intégration de socket.io
Maintenant on va allez voir les choses intéressantes, l’intégration de socket !
Il nous faut agir sur deux fronts, sur le serveur et sur le client. Il y a donc deux partie à la librairie socket.io. (nous sommes toujours en architecture client/serveur)
Socket.io côté serveur
npm install socket.io
et éditons le fichier index.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
// début de code de gestion du socket
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
// fin de code de gestion du socket
server.listen(3000, () => {
console.log('listening on *:3000');
});
Notez qu’on crée le serveur de socket en lui passant dans le constructeur le server NodeJS. « connection » est l’événement qui se déclenche lorsque la page dans le front end se charge pour la première fois et qu’un message donc est envoyé au serveur.
Socket.io côté client
Ajoutez le snippet dans le fichier HTML avant la fermeture de body
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>
ça y est notre application web est équipée!
Maintenant ouvrez la page a port 3000, votre terminal nodeJS va afficher ‘a user connected’. Ce qui se passe c’est que lorsque la page est loadée, la librairie socket.io va envoyer un signal vers le serveur, qui intercepte le message. Si vous bne voyez rien, rafraichissez la page.
Construction de l’application de messagerie instantanée
Maintenant côté client, écrivons le code JS pour émettre les messages entrés au clavier, dans le formulaire:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
var form = document.getElementById('form');
var input = document.getElementById('input');
form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});
</script>
et côté serveur on va afficher le message, ajoutez le snippet suivant :
Tapez les messages dans le formulaire, observez les logs côté serveur.
Le broadcasting côté serveur
Intéressons nous maintenant sur comment le serveur va émettre un message pour tous les client connectés, à la manière d’une station radio, d’où le terme ‘broadcasting’. Il faut utiliser la méthode emit() de l’objet io. Modifiez le code :
Il ya plusieurs façons de débugger NodeJS avec VSCode, le plus simple (mais peut être le plus ennuyant) est d’utiliser l’auto Attach, dès qu’un process NodeJS est démarré, VSCode se met en écoute. On va utiliser cette méthode. Pour plus d’information sur les autres méthodes allez sur cette page.
Utiliser les dev tools pour visualiser les communication via le socket
Vous pouvez voir les connexion socket comme vous pouviez le faire avec les requêtes Ajax. Dans les devtools, allez dans l’onglet Réseau, cliquez sur WS dans les filtres (à côté de XHR, Images etc.
Dans l’onglet Réponse, vous pouvez voir en fait les allées et venues des messages.
Ce que tous les développeurs web connaissent, ce sont les événements du DOM, les clics de souris, les survol, les chargements de page. Soit la page HTML suivante, un formulaire avec une div cachée qui se révèle dès que le formulaire change
let email = document.querySelector('#email')
let name = document.querySelector('#name')
let formulaire = document.querySelector('#formulaire')
let popup = document.querySelector('#popup')
formulaire.addEventListener('change', (e) => {
console.log('form has changed')
popup.style.display = 'block'
})
Mais il est aussi possible de lancer des événement depuis n’importe quel objet en Javascript, et ceci va nous permettre de coder notre application avec des événements, permettant une grande souplesse et découplage.
Par exemple, imaginez un formulaire avec login et mot de passe, di les deux champs sont validés, nous voulion faire apparaitre une popup.
let email = document.querySelector('#email')
let name = document.querySelector('#name')
let formulaire = document.querySelector('#formulaire')
let popup = document.querySelector('#popup')
popup.addEventListener('formEvent', (e) => {
popup.style.display = 'block'
})
//create the CustomEvent
const formEvent = new CustomEvent('formEvent', { detail: "les détails" })
formulaire.addEventListener('change', (e) => {
console.log('form has changed')
popup.dispatchEvent(formEvent)
// popup.style.display = 'block'
})
Il faut mettre bubble à true pour que l’événement se propage
let email = document.querySelector('#email')
let name = document.querySelector('#name')
let formulaire = document.querySelector('#formulaire')
let popup = document.querySelector('#popup')
popup.addEventListener('formEvent', (e) => {
popup.style.display = 'block'
})
//create the CustomEvent
const formEvent = new CustomEvent('formEvent', { detail: "les détails",bubbles:true })
formulaire.addEventListener('change', (e) => {
console.log('form has changed')
popup.dispatchEvent(formEvent)
// popup.style.display = 'block'
})
function test() {
console.log('this in a function', this);
}
const test2 = () => {
console.log('this in arrow function', this);
}
test2() // objet window
test() // objet Window
Comme on est dnas l’espace global (Window) le mot clé this représente Window. En effet les deux fonction sont définie dans l’espace de nom global et non dans un objet.
Le mot clé this dans l’espace de nom global
dans une fonction, la valeur de this dépend de comment la fonction est appelée. Considérez this comme un paramètres caché de la fonction. this est l’objet dans lequel la fonction est appelée.
Le cas des fonctions flèches
function test() {
console.log('this in a function', this);
}
const test2 = () => {
console.log('this in arrow function', this);
}
// test2() // undefined
// test() // objet Window
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
obj1.test = test
obj2.test2 = test2
console.log(obj1.test()) // this est l'objet obj1
console.log('arrow ', obj2.test2()) // this est l'objet Window
Dans les fonctions flèche, le this représente toujours l’objet global Window, alors que dans les fonctions classiques this représente l’objet dans lequel la fonction est définie.
Le cas du strict-mode
"use strict";
function show() {
console.log(this);
}
show(); // undefined
This dans un event listener
Ici c’est plus courant, vous avez déjà vu avec Jquery le $this, en Javascript pur c’est pareil avec this
Dans ce tutoriel nous allons voir comment simplement backtester une strétégie avec Tradingview, cet outil existe en gratuit et fait dans la verison gratuites énormément de chose.
On va écrire le script en Pinescript, et on va exécuter le backtest.
Ceci n’est pas un conseil financier.
strategy("Moving average Cross")
// Pinescript c'est L4G (langage de 4ème génération)
ema20 = ema(close,20)
ema50 = ema(close,50)
// on définit quand est ce qu'on long ou short
long = ema20 > ema50
short = ema20 < ema50
//ouverture du trade
strategy.entry("long", strategy.long,1.0,when=long)
strategy.entry("short", strategy.short,1.0,when=short)
// fermeture du trade
strategy.close("long",when=short)
strategy.close("short",when=long)
Cliquez sur « Update on chart »
Un fois que vous avez fait le script il faut clicker sur « Strategy tester », à côté de « Pine Editor », dans le sous menu, Overview vous pouvez voir la courbe de PNL
//@version=4
strategy("Moving average Cross")
ema20 = ema(close,20)
ema50 = ema(close,50)
long = ema20 > ema50
short = ema20 < ema50
start = timestamp(2021,04,01,0,0)
end = timestamp(2024,04,01,0,0)
if time >= start and time <= end
strategy.entry("long", strategy.long,1.0,when=long)
strategy.entry("short", strategy.short,1.0,when=short)
strategy.close("long",when=short)
strategy.close("short",when=long)
il faut mettre @version=4 sinon la condition if ne marche pas
Ensuite cliquez sur « Update on chart » pour que ça marche
Affichage des deux moyennes mobiles
//@version=4
strategy("Moving average Cross")
ema20 = ema(close,20)
ema50 = ema(close,50)
long = ema20 > ema50
short = ema20 < ema50
plot(ema50, title="ma50", color=#ffc1cc, linewidth=3)
plot(ema20, title="ma20", color=#00ffaa, linewidth=2)
start = timestamp(2021,04,01,0,0)
end = timestamp(2024,04,01,0,0)
if time >= start and time <= end
strategy.entry("long", strategy.long,1.0,when=long)
strategy.entry("short", strategy.short,1.0,when=short)
strategy.close("long",when=short)
strategy.close("short",when=long)
//Version avec Overlay sur la courbe des prix
il suffit de modifier la première ligne de code
strategy("Moving average Cross",overlay=true)
Ensuite cliquez sur « Update on chart » pour que mettre à jour le graphe, les deux moyennes mobiles sont dans le bas de l’écran séparés du graphique des prix. Ajoutez la propriété overlay=true pour superposer les deux graphique, auparavant supprimez le graphique du bas avec les deux moyennes mobiles.
Retarder l’entrée en position à la 10ème bougie en plus de la condition des moyennes mobiles
On fait ceci pour éviter que le croisement des moyennes mobiles ne soit que éphémères.
//@version=4
strategy("Moving average Cross",overlay=true)
ema20 = ema(close,20)
ema50 = ema(close,50)
long = ema20 > ema50
short = ema20 < ema50
//on attend la consolidation pour passer à l'action pour éviter de se faire stopper
longcondition = long and long[10] and not long[11]
shortcondition = short and short[10] and not short[11]
plot(ema50, title="ma50", color=#ffc1cc, linewidth=3)
plot(ema20, title="ma20", color=#00ffaa, linewidth=2)
start = timestamp(2021,04,01,0,0)
end = timestamp(2024,04,01,0,0)
if time >= start and time <= end
strategy.entry("long", strategy.long,1.0,when=longcondition)
strategy.entry("short", strategy.short,1.0,when=shortcondition)
strategy.close("long",when=shortcondition)
strategy.close("short",when=longcondition)
On a pris moins de trade mais le PNL est moins intéressant, mais vous voyez ici l’idée, on joue sur les potard afin d’améliorer le PNL. Une fois que vous avez des résultats intéressants, vous pouvez appliquer pour le futur dans votre stratégie de quant.
Après le réglage
Avertissement sur les limites du backtesting
Le backtesting n’est pas trivial, et ce qui est donnée par Tradingview peut ne pas être juste dans la réalité. Regardez cette vidéo
Le quant marche mieux dans le low time frame intraday avec des bougies d’un quart d’heure. je ne suis pas spécialiste et j’ai fait cet article à des fin didactique, je n’ai pas fait de robot de trading.
Non je ne parle pas de l’événement « click » ou « onclick », mais carrément de créer un événement souris. En javascript nous disposons de MouseEvent, qui ne se limite pas au seul clic gauche ou droit, mais tient compte aussi des déplacements de la souris.
var clickEvent = new MouseEvent('click', {
bubbles: true,
cancellable: true,
clientX: 32,
clientY: 32,
})
Voici le la documentation officielle de MouseEvent. Cependant je dois aussi vous dire qu’il y a un autre événement appelé PointerEvent. Cet événement est plus générique, car il peut gérer les événement touch, multi contact, alors que MouseEvent ne peut pas. Mais dans cet article nous allons nous concentrer sur MouseEvent.
function simulateClick() {
let titre = document.querySelector('h1')
let clickEvent = new MouseEvent('click', {
bubbles: true,
cancellable: true,
/* offsetX: 20,
offsetY: 20,*/
})
console.log('dispatch')
titre.dispatchEvent(clickEvent)
console.log('clicked')
}
let h2 = document.querySelector('h1').addEventListener('click', function (e) {
alert('toto')
})
setTimeout(simulateClick, 300)
La fonction simulateClick() va sélectionner l’élément H1 et la mettre dans la variable titre, construit un objet Event (MouseEvent pour être plus précis, MouseEvent héritant de Event), ensuite et c’est là qu’il faut être attentif, c’est titre qui va émettre (dispatch) l’événement clickEvent.
Dans le programme principal, h2 qui représente le même élément que titre reçoit une simulation de click et donc une alert() va faire apparaitre une popup. au bout de 300 millisecondes.
Liens en rapport avec l’article:
/////////////////////// DOM onunload //////////////////// https://stackoverflow.com/questions/11177233/javascript-event-that-runs-before-page-changes https://stackoverflow.com/questions/446892/how-to-find-event-listeners-on-a-dom-node-in-javascript-or-in-debugging https://stackoverflow.com/questions/4386300/javascript-dom-how-to-remove-all-event-listeners-of-a-dom-object/64484951#64484951
Un module est un fichier tout simplement rien de plus. On parle de module en javascript moderne, en effet ce concept n’existait pas avant ES6. Les modules existent en NodeJs également. Mais dans cet article je ne vais parler que des modules côté client.
Avant l’existence des modules, on ne pouvait pas importer un fichier Javascript dans un autre fichier Javascript comme on peut le faire en PHP.
Ici on ne va parler que des modules front end, dont la syntaxe est différente des modules dans NodeJS, dans ce dernier il existe au moins deux façon de faire, avec require() (commonJs) et import (AMD).
Comment on faisait avant pour importer un script Javascript?
On se servait de la balise script our importer les fichiers, index.js ne pouvait pas importer helper.js. La conséquence est que dans un fichier HTML on pouvait avoir facilement plus d’une dizaine d’import de fichier javascript. Cela pouvait avoir un impoact sur la rapidité du site.
Et vinrent les modules Javascript
Dès qu’il y a un import de modules, on observe au minimum deux fichiers. Soient deux fichiers, un principal.js, et un helper.js
//index.html
<script type="module">
import {sayHi} from './helper.js';
sayHi('John');
</script>
// helper.js
export function sayHi(name){
console.log('Hello ' + name)
}
On peut importer une fonction à condition qu’elle ait été exportée depuis le fichier helper.js. Dans la balise script on doit ajouter l’attribut type= »module » pour que cela marche.
Les caractéristiques de modules
Les modules utilisent le mode strict
Dans un code Javascript vous utilisez le mode strict pour forcer une plus grande rigueur dans la programmation
Par exemple dans un fichier HTML
<script>
a = 5 // autorisé pas d'erreur dans la console
</script>
Par contre
<script>
"use strict"
a = 5 // ReferenceError: a is not defined
</script>
de même dans un module
<script type="module">
a = 5 // ReferenceError: a is not defined bien qu'on n'ait pas eu à mettre use strict
</script>
Le scope des variables
Chaque module a son scope pour ses variables (je parle des variable globales du module, pas les variable locales des fonctions), les modules ne communiquenet donc pas leur variables, autrement dit, un module ne peut utiliser une variable globales d’un autre module.
// user.js
let user = "Jules"
// main.js
console.log(user) //
//index.html
<script type="module" src="user.js"></script>
<script type="module" src="hello.js"></script>
la console va afficher "SyntaxError: The requested module './lib.js' does not provide an export named 'user'"
//on modifie dans user.js
export let user = "Jules" // correct
//Pour illustrer directement dans une page HTML
<script type="module">
export let user = "Jules"
</script>
<script type="module">
console.log(user) // Erreur,et on ne peut faire import du module puisqu'il est inline, la seule façon urait été de faire window.user = "Jules"
</script>
Evaluation une seule fois du module à l’import
//index.html
<script type="module">
window.test = 0
</script>
<script type="module">
import { val } from './util.js'
</script>
<script type="module">
import { val } from './util.js'
console.log(val)
</script>
// util.js
window.test++
export let val = window.test
bien que l’on ait importé deux fois le module util.js, le code va afficher 1 et non 2
Prenons un autre exemple, qui va montrer que cet aspect est en fait pratique, permet aux différents scripts de se « partager » une variable
<script type="module">
alert(typeof button); // ce module est defer, il se télécharge immédiatement, mais il attend que le HTML finisse de se télécharger avant de s'exécuter.
</script>
<script>
alert(typeof button); // s'exécute immédiatement après le téléchargement, avant que le HTML du bouton ne vienne
</script>
<button id="button">Button</button>
Toujours préciser le chemin,
Même si le script est adjacent à votre page HTML, il faut préfixer avec un point slash
import {compteur} from 'util.js'; // dans un navigateur ceci n'est pas autorisé,TypeError: Failed to resolve module specifier "util.js". Relative references must start with either "/", "./", or "../".
import {compteur} from './util.js';
scripts externes et CORS
L’inclusion de script externe en tant que module doit se faire avec précaution dans le cas des modules. Pour les scripts externes de même origine que la page HTML, pas de soucis, mais si le script vient d’un autre nom de domaine, alors le serveur d’origine
<script type="module" src="https://site.com/script.js"></script>
il faut que site.com ait activé le CORS.
async et les modules
Le mot clé async permet de télécharger en parallèle un script javascript externe, mais ne s’utilise pas pour les script inline (à même la page HTML). dans le cas des modules, le mot clé async s’applique aussi aux Javascript inline.
<script async type="module">
import {counter} from './utils.js';
counter.count();
</script>
Ces attributs lorsqu’il sont utilisés sur une page HTML on tous un point commun : ils permettent de télécharger de façon asynchrone les fichiers liés depuis la page web, dans le but d’accélérer le téléchargement de la page et son exécution.
Async et Defer s’appliquent à la balise <script> uniquement
Comment utiliser ces deux attribut dans un script ? voici un exemple
<script src="init.js" defer></script>
ou
<script src="init.js" async></script>
Quelle est la différence entre async et defer? Regardez les deux schémas tirés de ce site,
async ne vas pas attendre la fin du téléchargement de la page HTML pour s’éxécuter, alors sur defer va attendre la fin du téléchargement de la page HTML (le parsing plus exactement).defer va être utile si vous avez besoin que tous les éléments du DOM soient mis en place.
preload permet de télécharger de façon asynchrone les ressources indiquée (css, javascript), mais il ne sont pas exécutés. Ils seront exécutés par le navigateur en cas de nécessité, ce qui est une bonne forme d’optimisation. Pour en savoir plus sur les détails techniques, cette page de la documentation officielle sur MDN.
Cet attribut a pour but de prétélécharger une page (même si on ne va pas la visiter) pour que lorsqu’on clique sur le lien, on n’ai pas à la télécharger. Il faut bien sûr s’assurer que c’est une page souvent visitée. La page prétéléchargée sera dans le cache prefetch.
L’image ci-dessus explique le fonctionnement (source : lien de la source).
The push refers to repository [docker.io/yvonhuynh/hello2]
An image does not exist locally with the tag: yvonhuynh/hello2
Si vous venez de commencer récemment à suivre un tutoriel Docker et que vous avez ce message, alors il y a peut être deux raisons, la seconde raison est plus probable.
Le contexte :
vous avez construit une image docker et vous aimeriez la pousser ver le hub de docker, mais vous rencontrez ce message lorsque vous poussez votre image soit depuis le Docker Desktop, soit depuis la ligne de commande.
Le dockerfile:
FROM node:alpine
COPY . /app
WORKDIR /app
CMD node app.js
Le projet javascript:
console.log('bonjour version 2')
Première raison possible :
Vos identifiants de connexion ne son tpas les bon
Cette raison est à titre préventif, il est fort à parier que ce n’est pas le cas.
docker logout
docker login
// renseignez vos identifiants.
Seconde raison possible:
Vous n’avez pas donné un tag à votre image
Vous avez buildé une image avec la commande:
docker build -t hello2
docker tag nom_image NOM_DOCKERHUB/nom_image
docker tag hello2 yvonhuynh/hello2
docker push yvonhuynh/hello2
Le nom de l’image s’appelle hello2 dans le hub docker.
yvonhuynh est le login de votre registry docker sur le site docker.com (dans notre cas mais il existe d’autres registry). un registry est comme un repository git.
lorsque vous faites des requêtes sur les éléments d’une page HTML et en faisant un affichage des éléments que vous avez obtenus, parfois vous avez des nodelist et parfois vous avez des Element, cela peut prêter à confusion parce que à première vue ce sont deux choses qui sont similaires, mais alors si elles sont similaires pourquoi elle portent deux types différents?
Node (Nœuds) dans le DOM
Dans le DOM, tout est un nœud. Un nœud peut être un élément, un attribut, un texte, un commentaire, un document, ou tout autre type d’objet DOM. Les nœuds sont organisés dans une structure arborescente, avec le nœud de document en haut et tous les autres nœuds en découlant.
Les nœuds ont des propriétés et des méthodes qui vous permettent de les manipuler ainsi que leurs nœuds enfants. Par exemple, vous pouvez utiliser la méthode appendChild() pour ajouter un nœud enfant à un nœud existant.
Éléments dans le DOM
Les éléments sont un type spécifique de nœud qui représente un élément HTML ou XML. Les éléments ont toutes les propriétés et méthodes d’un nœud, mais ils ont également des propriétés et des méthodes supplémentaires qui leur sont propres.
Par exemple, les éléments ont une propriété tagName qui spécifie le nom de l’élément, tel que « div » ou « span ». Les éléments ont également des attributs, qui peuvent être accédés en utilisant la méthode getAttribute() ou simplement en accédant à l’attribut comme une propriété de l’élément.
Il existe 12 types de noeuds
Element est un type de noeud, il y a aussi les noeuds TEXT, COMMENT, etc. Mais le plus familier pour nous est le type Element, les éléments de la page HTML.
Quand obtient l’un ou l’autre?
Lorsque vous faite une requêtes qui peut retourner plusieurs item, vous obtenex un NodeList (ce n’est pas un tableau !)
<div class="container">
<ul id="liste">
<li id="un">Un</li>
<li id="deux">Deux</li>
<li id="trois">Trois</li>
</ul>
</div>
let el = document.getElementById('un')
console.log(el) // retourne un Element
let nodelist = document.querySelectorAll('li')
console.log(nodelist) // retourne un nodeList
Dans l’exemple ci-dessus, même si le second exemple retourne un NodeList, les items de cette nodelist sont des Elements ! Vous pouvez le vérifier avec instanceof
Mais alors pourquoi dans ce cas on ne fait pas un objet ElementList? c’est un choix des ingéieurs qui ont fait Javascript.
Par contre HTML5 définit un objet HTMLCollection, qui est un objet qui ne contient que des Element, qui exclut tous les autres types de noeuds. Vous pouvez voir HTMLCollection à l’oeuvre avec l’exemple suivant:
let el = document.getElementById('liste')
En pratique vous n’avez pas à vous soucier de ces subtiles différences.
Cela fait pas mal de temps que je voulais m’investir dans un projet blockchain, à défaut de blockchain publique, je vais travailler sur une blockchain privée, et c’est sur Hyperledger.
Le type de projet de tokenization immobilière, thème très en vogue actuellement. Le but est de permettre une plus grande liquidité dans l’immobilier et de dépoussiérer les pratiques en vigueur.
RealtyKey.io est la plateforme web2 pour aborder le web3.
Principe de RealtyKey
Le principe est un principe d’investissement. Jusque là rien de différent, là où ça commence à se différencier de la finance traditionnelle, c’est que vous achetez un certificat d’investissement, appelé NFT et ce après avoir créé un compte (vous obtenez un AccountKey), vous détenez un NFT InvestKey prouvant que vous êtes inscrit . Ce dernier NFT vous permet de minter deux autres types de NFT, les Incomekey qui vous permettent de toucher un loyer du bien dans lequel vous avez investi, et les RealtyKey qui vous permettent d’habiter quelques temps dans un bien dans lequel vous avez investi.
La stack technique
La blockchain Hyperledger est contenue dans un Kubernetes, un orchestrateur de conteneurs Dockers. Le langage de développement de la blockchain, celui utilisé pour écrire les smart contracts appelé chaincode dans Hyperledger, est le langage Go, assez facile à appréhender.
Hyperledger expose un webservice directement consommable via Postman par exemple.
C’est assez différent de ce que je connais sur les blockchains publiques, notamment Ethereum et autres EVM compatibles. Le langage pour faire les smart contracts pour les chaine EVM est Solidity , que certains disent assez proche de Javascript, ce que personnellement je ne trouve pas, c’est assez unique.
Les différences entre une blockchain privée et une blockchain publique
Le choix d’une blockchain privée est motivée par les raison suivantes (non exhaustives)
pas de notion de consensus
milieu confiné à priori non sensible aux hacks
grand TPS
beaucoup plus sécurisée
Pourquoi utiliser la blockchain pour investir de façon fractionnée dans l’immobilier?
La blockchain permet de tout tracer sans pouvoir de falsification. Le fait de tokéniser rend l’actif plus liquide, et améliore la facilité de trouver une contrepartie quand on veut vendre.
Meilleure liquidité
Le ticket d’entrée bas permet à tout le monde de participer à la possession d’un bien. Vous n’avez pas à passer les étapes du notaire, DPE, etc. Tout est fait, vous n’avez qu’à entrer dans un investissement après avoir obtenu votre account key. L’opération ne prend que 15 minutes.
Meilleure contrepartie
L’apport de liquidités et l’ouverture à un marché financier cryptonatif plus vaste permettra une plus grande participation d’acteurs (idéalement particuliers comme institutionnels), concrètement si vous ne voulez plus investir dans un bien, où que vous voulez arbitrer vers un autre bien, et que vous voulez vendre vos parts, vous trouverez plus facilement un acheteur pour vos parts.
Ceci est à mettre en opposition avec un produit comme la SCPI, où il est notoire que les conditions de sortie sont plus délicates et de loin (clause de sortie, illiquidité du produit).
J’ai rencontré les plus grandes difficultés lorsque j’ai dû concaténer les colonnes du fichier texte. En effet dans une requêtes SQL de type INSERT, il y les valeurs à entourer d’apostrophe dans le fichier de sortie, donc il faut échapper le caractère apostrophe, et ce n’est pas un simple backslash, car le backslash doit être échappé par une apostrophe pour être reconnu comme un backslash d’échappement !
Contexte : je suis sous MacOS en architecture Intel 64bit (processeur Core i5)
Selenium vous permet programmatiquement d’émuler un navigateur (ici ce sera Chrome), et de télécharge une page et de l’analyser. Pourquoi est ce que c’estintéressant? Pour les test d’interface d’une part, puis d’autre part si vous voulez scraper une page très javascript, où les éléments sont rendus en JS donc non récupérables par un scrap avec BeautifulSoup, Selenium le peut.
Il est juste plus lent que BeautifulSoup, en effet il se repose sur le navigateur Chrome (notre cas ici mais ça peut être n’importe quel navigateur) qui fait le rendu, alors que BeautifulSoup ne fait pas le rendu.
Pré-requis :
connaissez votre version de navigateur Chrome, en allant dans les 3 petits point puis Aide puis A Propos de Google Chrome
Déterminez l’architecture de votre plateforme
Ensuite allez sur le site de Chromium pour télécharger le driver à mettre dans le même répertoire que votre script, le driver permet à Selenium de contrôler Chrome, c’est dépendant de l’OS et du microprocesseur. Il faut suivre le lien pour les dernières versions de Chrome
Installer la librairie Selenium pip install selenium
Script minimal de Selenium pour scraper une page
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome() #mac https://stackoverflow.com/questions/76928765/attributeerror-str-object-has-no-attribute-capabilities-in-selenium
#driver = webdriver.Chrome('./chromedriver'). #ne marche pas sur Mac
browser.get("https://www.python.org")
print(browser.title)
Cette commande permet de charger des données dans une table de votre base de données à une très grande de vitesse. J’ai pu par exemple insérer 500000 lignes en seulement 7 secondes. C’est quelque chose à considérer si vous avez de grosses bases à reproduire dans un environnement. En effet, par le passé, j’ai travaillé dans une entreprise où il fallait attendre 6h pour mettre en place la base de données, je pense que avec cette méthode on pourrait très bien ramener à moins de 10 minutes. Attention, le chargement est très rapide pour une table donnée, je ne tiens pas compte du temps de passage entre une table et une autre, mais normalement il n’y a pas de problème.
La méthode la plus souvent utilisée pour insérer un fichier dans une table, et de faire un fichier constitué de clause insert, cette méthode n’est pas très rapide en effet si vous avez plusieurs lignes insert mais c’est plusieurs requêtes qui sont faites cependant on peut optimiser en faisant un insert multiligne, mais même ça ça ralentit le chargement des données. En effet le fait d’exécuter une requête SQL va faire intervenir le moteur de base de données, qui doit lire la requête SQL l’interpréter l’exécuter et mettre en mémoire les choses.
Avec cette méthode, en outre passe l’exécution de requête SQL, en effet par exemple lorsque en charge un fichier CSV, il n’y a pas de requête SQL qui est faite, on injecte les données simplement colonne par colonne, ligne par ligne. Il n’y a donc pas d’exécution de code SQL, c’est une insertion brute, c’est pour ça que c’est aussi rapide.
Dans vos bases de données de développement, par exemple e-commerce, vous pouvez vous permettre d’utiliser une méthode traditionnelle, mais si vous êtes amené à travailler avec de grosses bases de données, comme un Big Data, analyse des données, vous avez de très grosses bases de données, à reconstituer dans la base de données. Cette méthode est donc très avantageuse.
Le problème avec cette fonction
Le problème avec cette fonction c’est qu’il est assez difficile de le paramétrer, en particulier avec mysql8. Le problème est encore plus ardu lorsque vous utilisez PHPmyadmin pour faire l’insertion de données. En effet, on intercalant entre le fichier et la base de données un logiciel écrit en PHP donc pas forcément très performant, vous augmentez les chances que cela ne marche pas. Je vous recommande donc de faire l’insertion depuis la ligne de commande. Même malgré ça nous allons rencontrer quelques difficultés.
Nous allons prendre un exemple simple, avec un petit fichier test.csv de quelques lignes, nous allons surtout nous concentrer sur la partie amont de l’insertion.
Nous allons disposer d’une table dont voici le code
CREATE TABLE `test` (
`id` int NOT NULL,
`nom` varchar(50) NOT NULL,
`prenom` varchar(50) NOT NULL,
`email` varchar(150) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Première tentative d’insertion dans PHPMyAdmin
Pour information la documentation officielle traitant de cette fonction se trouve sur cette page. La doc officielle dit aussi que MySQL doit avoir les privilèges sur les fichier à importer (logique), par exemple il doit avoir les droit de lecture sur le fichier test.csv.
LOAD DATA LOCAL INFILE 'E:\OneDrive\formapedia_cours\coursSQL\load infile\test.csv' INTO TABLE test
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES
(id, nom,prenom,email);
Ceci se solde par une erreur « #2068 - LOAD DATA LOCAL INFILE is forbidden, check related settings like mysqli.allow_local_infile|mysqli.local_infile_directory or PDO::MYSQL_ATTR_LOCAL_INFILE|PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY« . Ce qui est embêtant car je vois PDO donc j’imagine que ce doit être un réglage PHP dans PHPMyAdmin, il va falloir bidouiller le fichier de configuration. Je vois aussi qu’il y a un réglage dans le fichier de configuration de MySQL. Recherches infructueuses. Ce que je vois est qu’il faut mettre une variable de configuration dans my.ini (Windows) ou my.cnf (Linux)
[mysql]
local-infile=1
[mysqld]
...
local_infile=1 << parfois je vois local-infile=1 (avec le trait d'union)
[mysqldump]
quick
max_allowed_packet=512M
En redémarrant le serveur, je n’ai pas plus de succès…
Je tente de passer par MySQL en ligne de commande
En fait je fais ça, car je sais que PhpmyAdmin introduit une couche de configuration rendant la tâche plus délicate. Je passe donc par la ligne de commande qui est plus simple. En se connectant en ligne, n’oubliez pas de sélectionner la base de donnée avec use.
En collant le texte ci-dessous: Attention \r\n dans le système Windows mais \n tout court pour Linux
LOAD DATA LOCAL INFILE 'E:\OneDrive\formapedia_cours\coursSQL\load infile\test.csv' INTO TABLE test
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES
(id, nom,prenom,email);
J’ai a réponse suivante:
ERROR 2068 (HY000): LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.
C’est donc une question de restriction de lecture de fichier, mais je suis sous Windows, je ne m’inquiète pas trop (Linux est plus subtile), j’essais néanmoins de placer le fichier csv dans le répertoire où se trouve la base de données (dans le répertoire d’installation de MySQL, chaque base de données correspond à un fichier binaire, dans un répertoire de même nom (dans le cas de InnoDB). Mais j’ai toujours le même problème.