Statische kopieën van sites maken met wget
Het up-to-date houden van oude websites van eerdere projecten op het gebied van software en beveiliging kost tijd en geld. Met de commandlinetool wget kun je ze in plaats daarvan veranderen in statische websites en ze in hun huidige staat bevriezen.
De blog van de lang vervlogen grote reis ligt stil, de website van een oud studentenproject is al jaren in de lucht en de startpagina van de bruiloft inclusief het RSVP-formulier is nog steeds online. Eigenlijk zou je voor al die websites moeten blijven zorgen en vooral de achterliggende blog- en contentmanagementsystemen (CMS) moeten updaten. Maar vooral bij oude en niet meer interessante projecten is daar vaak niet genoeg tijd (en motivatie) voor. Ze kunnen op zich wel weg, maar vaak hangen er toch te veel herinneringen aan en kost het bijna niets om ze in de lucht te houden. Dus blijft alles zoals het is en blijven de verouderde projecten stof en veiligheidsgaten verzamelen.
Maar er is een alternatief: de website omzetten in een statische kopie. Dus in plaats van ze in een CMS of blogsoftware te laten staan, wordt het huidige uiterlijk van de website bevroren – als een verzameling HTML-, CSS-, afbeeldings- en andere bestanden.
VOORBEREIDINGEN
Elke webserver kan dergelijke statische HTML-pagina’s makkelijk verwerken, daar zijn geen softwarepakketten, databases of scripttalen zoals PHP voor nodig. Bovendien kunnen de webserver en het besturingssysteem zichzelf automatisch updaten, er is geen reden om je zorgen te maken over incompatibiliteiten bij dergelijke eenvoudige constructies.
Dynamische elementen vallen daarbij echter buiten de boot. Bezoekers kunnen geen commentaar meer schrijven op de blog en een persoonlijk inlog
deel of een interne zoekactie werkt niet, ook al staat alles vast – de inhoud van het inlogdeel en de zoekresultatenpagina zouden immers ook altijd hetzelfde zijn. Dergelijke beperkingen zijn echter meer een probleem als het betreffende project nog in leven is. In dat geval is het niet mogelijk om te werken zonder een klassiek CMS, een Static Site Generator of een soortgelijke oplossing om ten minste delen van de website interactief te houden.
Omdat dynamische elementen zoals formulieren niet werken bij statische pagina’s, moeten ze worden verwijderd, gedeactiveerd of verborgen. Dat is het makkelijkst te doen terwijl de betreffende website nog in een CMS draait. De exacte details zijn afhankelijk van het gebruikte systeem.
WGET
Nadat je de dynamische elementen verwijderd hebt, kun je de eigenlijke kopie gaan maken. Meestal moet die kopie de website tonen zoals de bezoekers die op dit moment zien. De kopie kan worden gemaakt zonder administratieve toegang tot de server, je hoeft alleen maar de website te kunnen openen – net zoals een bezoeker. Om de pagina volledig op te slaan, gebruik je in plaats van een browser een daarvoor geoptimaliseerde tool. Het commandlineprogramma wget wordt daar vaak voor gebruikt. Vrijwel elke Linux-distributie heeft dat in zijn pakketbronnen en voor Windows heeft het project een installer in zijn FAQ staan – zie de link op de laatste pagina.
Het programma wget is een Zwitsers zakmes voor verschillende toepassingen waarbij bestanden gedownload moeten worden. Het beschrijven van alle functies en parameters valt ver buiten het doel van dit artikel. Hieronder noemen we de belangrijkste instellingen voor het klonen van een website. Andere toepassingsmogelijkheden en parameters worden uitgelegd in de projectdocumentatie (zie de link).
De ‘Beginner HTML Site’ uit Mozilla’s tutorials over webdevelopment is een zeer goed demonstratieobject (zie de link). Het eenvoudige voorbeeld bevat slechts één pagina, met wat styling en een afbeelding. Als eerste poging start je wget met die website als parameter: wget https://mdn.github.io/ »beginner-html-site-styled/
Het resultaat is wat uitvoer van wget en het downloaden van index.html. Dat bestand kan worden geopend met een normale browser, maar helaas ziet het resultaat er niet uit als het origineel. De inhoud van de tekst is er, maar die heeft vrijwel geen opmaak en de afbeelding ontbreekt ook.
DOWNLOADEN VAN RESOURCES
Om dat probleem op te lossen, helpt het om de ontwikkelaarshulpmiddelen in de browser te activeren terwijl het indexbestand is geopend. Dat kun je bij de meeste desktopbrowsers doen met F12. Browsers die op Chromium gebaseerd zijn laten op het tabblad Netwerk zien welke resources van welke bronnen worden geladen. De ontwikkeltools van andere browsers bieden vergelijkbare mogelijkheden. Daar kun je zien dat de browser index.html lokaal opent, maar de stylesheet met de paginaopmaak (style.css) en de afbeelding (firefox-icon.png) niet kan laden. Een andere stylesheet (‘css?family=Open+Sans’) kan worden geopend door de browser, maar hij laadt die van fonts.googleapis.com en niet als lokaal bestand – daarover later meer.
De afbeelding en style.css kunnen niet worden gevonden door de browser, omdat wget ze niet heeft opgeslagen. Dat kun je verhelpen met de parameter --pagerequisites of -p: wget -p https://mdn.github.io/ beginner-html-site-styled/
Met die parameter downloadt wget niet alleen de gevraagde website, maar ook alle bronnen die nodig zijn voor de weergave, zoals stylesheets, lettertypen en afbeeldingen – voor zover daar door de betreffende HTML-code naar gerefereerd wordt.
Een probleemgeval zijn de JavaScript-bestanden: wget downloadt de externe scripts net als andere bestanden. Eenvoudige scripts, die bijvoorbeeld een paar animaties op de pagina aansturen, werken bij een statische kopie vaak nog wel, maar wget voert geen JavaScript-code uit. Complexere programma’s die resources naladen of andere JavaScript-modules bevatten, werken daarom niet bij de kopie omdat wget niets wist van die extra resources en ze niet kon opslaan.
De oproep met -p laat meteen zien dat wget style.css en firefox-icon.png downloadt naast index. html. Bovendien slaat het programma de bestanden niet meer direct op in de huidige directory, maar in een map genaamd mdn.github.io met verschillende submappen. Als wget meer dan één bestand moet downloaden, begint het de mappenstructuur van de website in kaart te brengen. De eigenlijke startpagina bevindt zich dan in mdn.github.io/beginner-htmlsite-style/index.html.
De door wget gecreëerde mappenstructuur kan op vele manieren worden aangepast. Met de parameter --no-hostdirectories (-nH) verkort je bijvoorbeeld de map mdn. github.io. Dat is vaak nuttig als er toch maar één domein gekloond wordt.
Als je de lokale index. html dan opent, ziet het resultaat er vrij goed uit. Het ontwerp is correct en de afbeelding is geladen. Bij de netwerkanalyse van de
slaat dat lokaal op. De browser leest echter later ook de volledige referentie in index.html met de verwijzing naar fonts.googleapis.com en downloadt het bestand dus nog steeds van daaruit.
Dergelijke gevallen doen zich bij vrijwel elke website voor, en zelfs naar bestanden op dezelfde host wordt vaak met absolute links doorverwezen. Gelukkig hoef je niet iedere link handmatig om te buigen: de wget-parameter --convertlinks of -k doet dat. Het past de links naar de bestanden die wget heeft gedownload aan, zodat ze naar de lokale kopieën verwijzen. Het programma zet de links eenvoudigweg om naar relatieve links.
ONTBREKENDE BESTANDSEXTENSIES
Zelfs het toevoegen van -k is niet genoeg om de pagina volledig en lokaal in de browser weer te geven. De parameter corrigeert de links, maar een ander probleem blijft: het extra stylesheet-bestand van Google heet css?family=Open+Sans, wat mogelijk is onder Linux, maar eigenlijk geen gangbare bestandsnaam is. Windows staat dergelijke namen helemaal niet toe, dus noemt wget het bestand daar css@ family=Open+Sans en past de links dienovereenkomstig aan.
Dat is niet alleen een esthetisch probleem. De browsers interpreteren de informatie achter het vraagteken als query-parameters en niet als onderdeel van de bestandsnaam. Wat overblijft is css, maar een bestand met die naam bestaat niet en de stylesheet wordt niet geladen. Je kunt wget het vraagteken ook bij Linux laten vermijden (--restrictfilenames=windows), maar die oplossing is niet echt fraai en ook niet betrouwbaar. Door het ontbreken van een bestandsextensie weet een browser namelijk niet dat dit een CSS-bestand is en geeft daarom een foutmelding.
De server van Google levert die informatie in de HTTP-header van het originele bestand, waardoor een bestandsextensie niet nodig is. In het geval van een statische kopie moet die echter wel aanwezig zijn, en niet alleen voor het testen in de lokale browser. Als een webserver later de statische kopie van de website moet leveren, moet hij ook weten wat de afzonderlijke bestanden zijn, zodat hij de juiste HTTPheaders kan versturen.
De oplossing is wederom een parameter voor wget: --adjust-extension of -E instrueert het programma om HTML-bestanden de extensie .html en CSS-bestanden de extensie .css te geven (wget ondersteunt momenteel geen andere bestandsextensies, behalve sommige voor gecomprimeerde bestanden, die hier niet relevant zijn). Het volledige commando voor het kopiëren van de website ziet er als volgt uit: wget -p -nH -H -k -E »https://mdn.github.io/ »beginner-html-site-styled/
De stylesheet voor het lettertype is css?family=Open+ Sans.css (onder Linux). Ook de links naar het bestand zijn aangepast: css%3Ffamily=Open+Sans.css. Daar heeft wget niet alleen de bestandsextensie toege
voegd, maar ook het vraagteken gecodeerd. Browsers zien het daarom niet langer als het begin van de query-parameters en laden het bestand zonder problemen.
Het resultaat van de wget-aanroep is nu niet meer te onderscheiden van de originele pagina, hoewel de browser alle bronnen van de eigen harde schijf laadt – de statische kopie is klaar. De bestanden kunnen dus ook in de documenten-root van gangbare webservers worden gezet en worden dan op de juiste manier uitgeleverd. Dat mag je natuurlijk alleen doen als je je eigen website kopieert en niet de site van een derde partij, zoals in het voorbeeld hier.
GOEDE DINGEN HEBBEN TIJD NODIG.
Deze kopie bevat slechts één pagina. De meeste websites bestaan echter uit meerdere subpagina’s die aan elkaar gelinkt zijn, bijvoorbeeld via een menu. Dergelijke grotere projecten kunnen door wget ook afgehandeld worden door recursief van link naar link te gaan.
Voorzichtigheid is daarbij echter geboden, want er kunnen makkelijk enorme hoeveelheden data en requests gegenereerd worden, vooral als je wat onzorgvuldig te werk gaat. In de recursieve modus kan wget niet alleen je eigen internetlijn overbelasten, maar ook anti-DDOS-mechanismen op de betreffende servers in werking stellen en je eigen ip-adres blokkeren. Sommige websites hebben zulke slechte ervaringen met onzorgvuldige wget-experimenten
dat ze die volledig blokkeren – Wikipedia doet dat bijvoorbeeld via een robots.txt-bestand.
Dat kun je voorkomen door wget te instrueren de requests niet zo snel als mogelijk te versturen. De parameter --wait (of -w) bepaalt hoeveel seconden wget moet wachten tussen twee requests. Ook nuttig zijn de parameters --limit-rate, --quota en --random-wait. De eerste twee bepalen hoeveel bandbreedte wget gebruikt en hoeveel er maximaal gedownload mag worden. De derde parameter varieert de wachttijd ingesteld door --wait met een willekeurige factor tussen 0.5 en 1.5, wat kan voorkomen dat anti-DDOS-maatregelen van de serveroperator gaan werken.
Met die voorzorgsmaatregelen in gedachten kun je de parameters --recursive en --level respectievelijk -r en -l gebruiken om complexere websites te downloaden. Door het toevoegen van -r zal wget
ook normale links volgen die verwijzen naar andere pagina’s, en niet alleen degene die verwijzen naar bronnen voor de huidige pagina. Standaard volgt wget tot vijf links na elkaar. Dat kan te weinig zijn als de doelpagina diep genesteld is, maar het kan ook veel te veel zijn als de doelpagina grotere linklijsten bevat. Het is daarom aan te raden om de parameter -l te gebruiken om in te stellen hoe diep wget een pagina moet induiken. Begin voorzichtigheidshalve met kleine waarden.
Onze voorbeeldpagina bevat slechts één link, naar het Mozilla Manifesto. Dat is te vinden op www. mozilla.org, maar dankzij -H springt wget ook naar andere hosts. Mozilla.org is echter een zeer grote website met verschillende niveaus en veel links. Beperk wget daarom tot één recursief niveau (-r -l
Laat de parameter -nH weg, voor aanvragen via meerdere hosts heeft het meer zin om wget ook de bijpassende mappen aan te laten maken: wget -p -H -k -E -w -r -l »https://mdn.github.io/» beginner-html-site-styled/
Het resultaat is overtuigend: Een klik op ‘Mozilla Manifesto’ in de statische paginakopie leidt niet langer meer naar internet, maar naar een statische kopie die niet te onderscheiden is van het origineel. Alleen de links daar verwijzen wel naar internet, omdat je wget hebt verboden om de structuur nog verder te reproduceren.
CONCLUSIE
Hoe ver je komt met de hier beschreven parameters hangt af van de beoogde website. Problematisch zijn websites die veel gebruik maken van JavaScript. Als je de verschillende scripts niet echt nodig hebt – wat verrassend vaak het geval is – kan het helpen om ze simpelweg niet te downloaden. Voor dergelijke gevallen biedt wget een verscheidenheid aan Accept/ Reject Options, die worden uitgelegd in de programmadocumentatie.
Ook websites die gebruik maken van zeer actuele webtechnieken kunnen problemen veroorzaken. Zo ondersteunt wget bijvoorbeeld geen srcset-attributen in
Maar zelfs de huidige versie kan al goed van pas komen. Ten eerste is er een tendens om oudere websites af te sluiten en ten tweede biedt wget nog veel meer parameters dan hier uitgelegd kan worden. Als je de documentatie zorgvuldig bestudeert, kun je diverse problemen voorkomen en kunnen ook complexere blogs, forums en uitgebreide websites worden omgevormd tot statische kopieën.