My Ranker

J’adore SolR mais certaines choses sont plus compliquées à faire. Je voulais me faire un algorithme de ranking personnel basé sur la pertinence (de base) et sur une valeur numérique (karma ou date par exemple), ainsi que donner plus d’importance à certains champs. Ainsi, sur mon SolrR 8, cela se formule comme ça (je ne mets ici que les paramètres différents d’une utilisation) :

defType=edismax

&q=remix test

&sort=product($qq,karma)+desc

&qq={!edismax v="remix test" qf="title^20 description^2 author^10 "}

J’aurais aimé mettre dans qq, v=$q au lieu de répéter la requête mais j’ai l’impression que ce n’est pas valable. En tout cas mon ranking est alors bien meilleur.

Enfin, pour ce qui est mettre un bonus aux documents les plus récents, toujours en edismax, j’ajoute: &bf=sqrt(sqrt(ms(dated))). Il est pertinent d’ajuster la fonction ms avec une origine temporelle du décompte: ms(mydatefield, 2000-01-01T00:00:00Z)


My Ranker

No Return (mails)

Je gère pour un site des envois d’e-mails personnalisés en masse. J’envoyais mais, au bout d’un certain temps, j’en ai eu mare de recevoir, par exemple, les messages d’absence en retour.

Au final, j’ai ajouté à ces messages des headers qui font le job, globalement car certains serveurs me résistent de par leur implémentation.

'X-Auto-Response-Suppress:' => 'OOF, DR, RN, NRN, AutoReply'
'Auto-Submitted:' => 'auto-generated'

C’est assez élégant et fonctionnel.


No Return (mails)

Deep structure JSON

J’aime bien le JSON mais je dois avouer que quand il s’agit de traiter des structures plus profondes (au delà des tables et des listes, et surtout les cas où la profondeur est variable (exemple: JORF), je suis assez démuni alors que je n’ai pas du tout ce problème avec du XML où il est facile de naviguer entre enfants et parents, etc.

J’avais fais des essais avec JSON::XPath et autres mais la syntaxe me dépasse car j’y vois des tonnes de confusions avec le XPath du XML (notamment à propos de la notion de. parent). Plutôt que de me prendre la tête , je me suis mis à chercher un convertisseur JSON vers XML.

Trouvé ici : https://stackoverflow.com/questions/17398601/how-to-create-xml-from-json-string-in-perl

use JSON::Any;
use XML::Simple;

my $convertor = JSON::Any->new();
my $data = $convertor->decode($json);
my $xml = XMLout($data);

J’adore!


Deep structure JSON

robots.txt

Historiquement, un fichier texte posé à la racine d’un serveur Web, nommé robots.txt, est une pseudo norme supposée déclarer le comportement toléré de la part des agents logiciels le visitant.

Grand amateur de scraping, je connais cette norme mais elle ne m’est d’utilité que pour refuser à mes intermédiaires certaines taches. Au fond je ne les respecte pas. Je suis même surpris que cette idée perdure. A vrai dire, je n’ai jamais participé à un site sur lequel ce serait une préoccupation.

L’idée est de lister les URLs autorisées/interdites. Le format de ce fichier fait même en sorte qu’il soit possible de décrire des politiques différentes robot part robot (agent par agent).

Imaginons un site public (administration d’Etat). Accepterions nous ouvertement qu’il procède à des distinctions agent par agent ? Discriminations non acceptables. Notons que j’ai vu un paquet de sites répondant à de tels critère mettre en place des restrictions type captcha sur des fichiers type robots.txt ou même des flux RSS !

Derrière chaque robot/agent il y a une personne/structure. Cela en ferait un sous-citoyen ? On ne scrape pas pour assouvir un désir malsain d’accrétion. On le fait globalement pour des traitements postérieurs que l’on aurait autrement fait sur des jeux de données, sous réserve qu’ils soient d’accès intelligibles et techniquement raisonnables. Surtout sur des sites étatiques. 

Je fais ce focus sur des sites publics car ils sont plus loin de ces parties du web où la monétisation par la publicité s’impose (?!?), et car ils sont redevables aux citoyens dont ils relèvent. 

Ce n’est pas un plaidoyer contre les distinctions droïds/humains, ni browser/agents. La distinction logiciels/agents est de plus en plus complexe. Un proxy vaut il un script à fin d’accessibilité, un curl pour archivage ?

Un dernier mot sur la tendance actuelle qui est de passer de jeux de données à des API publiques. Je n’ai pas de religion sur ce point mais au final j’aime que les cohabitent et que cela n’exclue pas le scraping.

Resistance is futile.


robots.txt

Let it be dark in javascript

Au début on se dit qu’il faut questionner le choix utilisateur avec une mediaQuery:

 (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)

Reste que si l’on est dans un contexte incertain, développement d’un add-on dans mon cas, se résoudre à cette simple requête est une mauvaise stratégies car le site sur lequel on vient se pluger lui peut ne pas adapter son style aux choix utilisateur (dark mode ou non).

Sur Firefox qui est mon browser de dev, je ne comprenais pas pourquoi j’obtenais « rgba(0, 0, 0, 0) » avec la ligne suivante sur des pages où le fond est blanc mais le système est en dark mode:

window.getComputedStyle(document.body,null).getPropertyValue('background-color');

A vrai dire, mon erreur était de comprendre cette ligne comme parlant de la couleur noire (0,0,0) alors que le dernier 0 indique en rgba que le fond est transparent, or le fond d’écran d’une page web est blanc. La proprieté (‘background’) retourne d’ailleurs ‘none’. Chrome et safari retournent une valeur rgb qui est plus simple à comprendre.

Au final, je pense être pas mal avec ce bloc:

var dark = false;
let bs = window.getComputedStyle(document.body,null);
let baco = bs.getPropertyValue('background-color');
  if(bs.getPropertyValue('background') == 'none') {
  if(!(/rgba.*,\s?0\)$/.test(baco))) {
    dark = true;
  }
} else {
  let sacos = baco.split(/,/);
  let moy = 128;
  if((sacos != null) && (sacos.length > 2)) {
    moy = (parseInt(sacos[0].replace(/\D+/g,''))+parseInt(sacos[1].replace(/\D/g,''))+parseInt(sacos[2].replace(/\D/g,''))) / 3;
  }
 
  if((moy < 128) && !(/rgba.*,\s?0\)$/.test(baco))) {
    dark = true;
  }
}

Au final, j’aime bien mon petit calcul pour essayer d’évaluer si la couleur est plutôt sombre ou claire.


Let it be dark in javascript

Linkedin’s code tag

Mon user script puis mon prototype d’add-on avait de manière récurrente des problèmes avec Linkedin.

Après moultes débugs, j’ai fini par comprendre qu’ils font en fait un usage très spécial de la balise <code>

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code

Pour linkedin, code se retrouve être une balise cachée dans laquelle ils embarquent des données.

Mon content script évitait déjà les balises script et autres styles mais de la à penser que le contenu des balises code doivent être ignorés, il faut vraiment avoir l’esprit tordu.

Avant ou après le rachat par MSFT ?

https://stackoverflow.com/questions/42530879/why-does-lindkedin-use-hidden-code-tags-in-their-updated-website


Linkedin’s code tag

Dans un browser, ça a l’apparence du web mais ce n’est pas du web

Faites une requête avec le mot « emprise » dans un moteur de recherche type google. Vous obtenez des résultats. C’est sur le web et dans un browser. Mon test pour dire que c’est du web est que c’est modifiable/remixable: par exemple:

document.evaluate('//text()[contains(.,"emprise")]',document.body,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);

Cela retourne des résultats avec lesquels ont peut jouer (console JS/DOM, user scripts, ..etc)

Par contre, si je vais sur docs.google.com, que je crée un document dans lequel je tape juste le mot « emprise », même en faisant attention à la case du texte, je n’obtiens rien avec le test précédent, mis à part un script de données qui contient ce même mot. En clair, il ne trouve pas de nodes textuelles contenant le mot en question. Et pour cause, l’affichage est en fait une balise canvas donc hors du DOM qui est l’essence du web/html. Autant mettre un binaire qui pointerai vers une VM. Ici le web n’est qu’un transport et le browser un simple réceptacle.

Ensuite le site en question trouve malin d’intercepter tous les événements clavier. Tout y passe sauf bien sûr ceux qui sont interceptés auparavant par le browser. Bref, on ne peux pas jouer ici. Ce n’est pas du web, désolé. Mauvais karma.


Dans un browser, ça a l’apparence du web mais ce n’est pas du web

URL Safari Oddities

Ma seconde machine est un mac sur lequel j’utilise Safari. Lors d’un partage d’URL, je constate que lorsque je copie l’adresse d’un site avec un accent dans le chemin, il embarque

https://jadetognet3.wixsite.com/cap-eana/je-découvre-mon-école-1

Alors que Chrome et Firefox utilisent

https://jadetognet3.wixsite.com/cap-eana/je-d%C3%A9couvre-mon-%C3%A9cole-1

C’est un biais de ces dernières années d’afficher dans la barre d’adresse l’URL déséchapée et de copier l’URL réelle. Safari a tort ce qui pose des problèmes lors de la réutilisation de l’URL (par exemple document traitement de texte) même si Pages.app s’en accommode fort bien.

Cela me semble l’occasion de s’amuser un peu avec Automator.app avec pour objectif de mettre un lien avec l’URL bien échappée et le libellé du lien bien normalisé donc deux expressions d’une même adresse.

Je crée donc un workflow qui accepte en entrée des URLs depuis Safari.app, puis j’utilise le module « Get current web page from Safari » (qui envoie l’URL brute échappée), puis le module « Run shell Script » dans lequel je sélectionne perl et « pass input as arguments ». Reste à copie ce script:

#!/usr/bin/perl
use URI::Escape;
$str = $ARGV[0];
$tito = uri_unescape($str);
print `echo "<meta charset="utf8"><a href="\&quot;$str\&quot;">$tito</a> " | textutil -stdin -format html -convert rtf -stdout | pbcopy`;

Le gros hook dans ce script est de bien inclure le meta charset dans le bout de html passé à textutil puis pbcopy (j’ai perdu pas mal de temps avec des problèmes d’encodage).

Plus qu’a créer un raccourci clavier dans les préférences d’accessibilité et le tour est joué. Cependant, sous Monterey, Automator.app est en cours de déprécation. Je passe conduire sur Shortcuts.app avec quelques hooks en raison de bugs de leur part ou d’incompréhensions de ma part. Reste que la solution suivante est fonctionnelle:

Je mets ici le script:

use URI::Escape;
$loco = $ARGV[0];
open(J,'<',$loco);
$str = <J>; 
chomp($str);
close(J);
print($str);
$tito = uri_unescape($str);
print `echo "<meta charset='utf8'><a href='$str'>$tito</a>" | textutil -stdin -format html -convert rtf -stdout | pbcopy`;
Cheers


URL Safari Oddities