Yep, I'm a geek !


14 juillet 2007

Extraction automatique de .torrents

Deux programmes (PHP et Bash) pour parcourir tout ThePirateBay et choper tous ses torrents sans s'embèter. Le principe étant que le script Shell/Bash commande un script PHP qui lui, se charge de tout le travail de reconnaissance de chaînes et d'extraction d'URLs. Ces deux scripts sont censés s'exécuter depuis la ligne de commande, un navigateur ne nous sert à rien ici.

#!/bin/bash

#"The Pirate Bay"-specific
BaseURL="http://thepiratebay.org"
ListPagePattern='/<a[ \n\r\t]*href="(.*tor\/[^">]*)[^>]*>(.*)<\/a>/'
TorrentPagePattern='/<a href="(.*\.torrent)">.*<\/a>/'
OtherPagePattern='/<a href="(\/brwsearch[^>"]*page=[0-9]+)"/'
FirstPage=brwsearch.php\?b\=1\&c\=501

./extract.php $BaseURL $FirstPage --get-pages "$OtherPagePattern" | sort | uniq > pages.list

pageIndex=0
for page in `cat pages.list`; do
    PageURL=page$pageIndex.html
    wget $BaseURL$page -O $PageURL      #Download de la page du site en question
    ./extract.php $BaseURL $PageURL --extract-torrents "$ListPagePattern" "$TorrentPagePattern" > page$pageIndex.list
    #
    for url in `cat page$pageIndex.list`; do
        grep $url torrents.list.done > /dev/null
        [[ $? == 0 ]] && echo "$url est déjà mis à télécharger" && continue;
        echo "On doit télécharger $url"
        dcop ktorrent KTorrent openTorrentSilently "$url";
        echo $url >> torrents.list.done
    done
    exit 12
    #
    ((pageIndex++))
done

Script shell qui appelle le PHP pour déterminer ce qui reste à télécharger
#!/usr/local/bin/php -q
<?
//Extrait d'une page toutes les sous-pages contenant un torrent et pour chaque sous-page, stocke l'URL du torrent dans un fichier local.
//
//Syntaxe :
//
//./extract.php <BaseURL> <FirstListPage> <ListPagePattern> <TorrentPagePattern> <OtherPagePattern>
//
//1 : BaseURL, l'URL de base du site visé, préfixe de toutes les adresses internes.
//2 : adresse d'une des pages contenant les liste d'éléments à extraire.
//3 : pattern repérant l'URL d'une page descriptive d'un torrent sur une page-liste.
//4 : pattern repérant l'URL d'un fichier torrent sur une page descriptive.
//5 : pattern repérant les URL des autres pages-listes à partir de celle passée en paramètre 2.
//
//Exemples d'utilisation :
//
//The Pirate Bay :
//./extract.php http://www.thepiratebay.org brwsearch.php\?b\=1\&c\=501 '/<a[ \n\r\t]*href="(.*tor\/[^">]*)[^>]*>(.*)<\/a>/' '/<a href="(.*\.torrent)">.*<\/a>/' '/<a href="(\/brwsearch[^>"]*page=[0-9]+)"/'

//TODO : penser à l'option --debug pour afficher des choses...
//DONE : faire deux options, --get-pages et --extract-torrents pour différencier les deux types d'appels de ce script

function    displayArray(&$arr)
{
    $Output = "<ul>\n";
    foreach($arr as $key=>$val)
        $Output .= "<li>$key : ".(is_array($val) ? displayArray($val) : $val)."</li>\n";
    return $Output."</ul>\n";
}
    
    $Args =& $_SERVER["argv"];
    if (count($Args) == 1)      die("Rien...");
    $BaseURL = isset($Args[1]) ? $Args[1] : "";
    $SrcFile = isset($Args[2]) ? $Args[2] : "";
    if (!strlen($SrcFile))      die("Pas d'URL de fichier source à analyser.\n");
    if (!file_exists($SrcFile))     die("Pas de fichier source à analyser.\n");
    $Contents = file_get_contents($SrcFile);
    if ($Debug) echo "Taille du fichier : ".strlen($Contents)."\n";
    //
    $Action = isset($Args[3]) ? $Args[3] : "";
    if (!strlen($Action))       die("Aucune action passée en paramètre.\n");
    switch ($Action)        {
        case    "--get-pages":
            $PatternPages = isset($Args[4]) ? $Args[4] : "";
            break;
        case    "--extract-torrents":
            $Pattern = isset($Args[4]) ? $Args[4] : "";
            $Pattern2 = isset($Args[5]) ? $Args[5] : "";
            if (!strlen($Pattern) || !strlen($Pattern2))    die("Extraction de torrents : il manque des patterns...\n");
            break;
        default:    die("Action inconnue : $Action.\n");
    }
    //Sous-pages
    if (strlen($PatternPages))      {
        $SubPages = array();
        $NbSubPages = preg_match_all($PatternPages, $Contents, $SubPages);
        //echo "Nb de matches : $NbSubPages\n";
        foreach($SubPages[1] as $PageURL)       echo $PageURL."\n";
        //echo displayArray($SubPages);
        die();
    }
    //Extraction de torrents
    $Matches = array();
    if ($Debug)     echo "Pattern = $Pattern\n";
    $NbMatches = preg_match_all($Pattern, $Contents, $Matches);
    if ($Debug)     echo "NbMatches = $NbMatches\n";
    //echo displayArray($Matches);
    $SubMatches = array();
    for($i=0; $i<$NbMatches; $i++)      {
//      echo $Matches[1][$i]."\n";
        if ($Debug) echo $Matches[2][$i]."\n";
        $DownloadPage = file_get_contents($BaseURL.$Matches[1][$i]);
        if (!$DownloadPage)     echo "Problème !\n";
        //Tentative de détection de pages erronées...
    if (strlen($DownloadPage) < 100)    {
        continue;
        echo "$DownloadPage\n";
    }
//      echo "Taille de la page téléchargée : ".strlen($DownloadPage)." octets\n";
        $Ret = preg_match($Pattern2, $DownloadPage, $SubMatches);
        $TorrentURL = $SubMatches[1];
        if ($Debug) echo "Torrent : ";
        echo (strlen($TorrentURL) ? $TorrentURL : "FALSE")."\n";
//      echo displayArray($SubMatches);
    }

?>
Moteur d'analyse de page qui extrait la hiérarchie liste-page-torrent

Aspirateur de site utilisant Fusker

Au départ était Fusker, qui agrégeait des sites épars et permettait leur consultation depuis un lieu unique. A partir d'un site de ce type on peut très facilement, en couplant Bash et PHP, rapatrier ces images localement. Voici :

#!/bin/bash

BaseURL="http://kladblog.funwithbabes.com/"
IndexURL=$BaseURL"index.php?offset=0"
for page in `wget -q -O- $IndexURL | grep "special=preview" | awk 'BEGIN{FS="<a href=\"[^\"]*\">.*</a>"} {URL=substr($0, index($0, $1)+length($1)+9); print substr(URL, 1, index(URL, "\"")-1)}'`; do
    page=$BaseURL$page
    pixList=$(wget -q -O- $page | php -r '$Contents=file_get_contents("php://stdin"); preg_match_all("@You are looking at <b>(http://[^<]*)</b>@", $Contents, $Matches); $Nb=count($Matches[1]); if ($Nb != 1) die("Oups"); $ToFusk=$Matches[1][0]; if (!preg_match("@(http://[^\[]*)\[[^\]]*\](.*)@", $ToFusk, $Matches)) die("Oups2\n"); $FP=$Matches[1]; $SP=$Matches[2]; $NbPix=preg_match_all("@(".$FP."[^<:]*).*<img src=\"\\1\"@", $Contents, $Matches); if (!$NbPix) die("Oups3"); $DirName=str_replace("/","",str_replace("http://","",$FP)); mkdir($DirName); echo $DirName."\n";  foreach($Matches[1] as $Item) echo $Item."\n";' 2>/dev/null)
    DirName=""
    for pix in $pixList; do
        if [[ ${#DirName} -eq 0 ]]; then
            DirName=$pix
            echo "Dossier : $DirName"
        else
            echo "Image : "$pix
            wget -P $DirName -nd -q $pix
        fi
     done
done

En changeant la déclaration de la variable IndexURL on peut rapatrier toutes les pages restantes. Ceci peut se faire automatiquement, mais je me suis arrêté là :).

C'est sûr que ça n'est pas ni du grand code ni du code propre mais ça fait le boulot. Il ne faut pas avoir envie de revenir dessus au bout de 6 mois c'est tout...

Accueil