Egy Wiki oldal költöztetése WordPress-re

Avagy hogyan migráltam a webni.innen.hu tartalmát erre a WordPress oldalra? Ebben a bejegyzésben a költöztetés első lépéséről olvashatsz: milyen, nem teljesen erre a feladatra kitalált technológiával exportáltam a régi oldalam tartalmait.

A webni.innen.hu költöztetése című bejegyzésben már nagyvonalakban leírtam a folyamatot, most egy jóval részletesebben mutatom be az első lépéseket. Ez a tapasztalatok azoknak lehetnek érdekesek, akik komplexebb weboldalak tartalmait szeretnék maguk is költöztetni.

Első lépés: a Zwiki-vel készült szájt szkrépelése

Strukturált tartalom előállítása

Egy webszájt költöztetésének első lépése, hogy a megszüntetésre ítélt szájton fent lévő tartalmat valamilyen strukturált formába öntsük. Mivel hogy esetünkben egy Zwiki tartalomkezelő rendszerrel készült szájtról beszélünk, a strukturált szón különös hangsúly van. A Wiki jellegű weboldalak egyik fő ismérve, hogy igen gyakran egy adott weblapon megjelenített összes tartalom egyetlen mezőben játszik: a Zwiki esetében például ugyanabban a sima szöveges mezőben kapott helyet az oldal tulajdonképpeni tartalma és az összes, az oldalhoz szó szerint hozzáfűzött komment. A rendszer pedig ebből az ömlesztett szövegből egy egyszerű speciális szövegformázási megoldás értelmezésével ismerte fel, hogy melyek a hozzászólások, és melyik az oldal szövegtörzse. Emiatt az oldal tartalmának strukturált letöltése során az én szkriptemnek is meg kellett találnia ezeket a jellegzetességeket, hogy külön el tudjam raktározni a szövegtörzset és az egyes hozzászólásokat.

Speciális markup formázás kezelése

További kihívás volt, hogy a Zwiki, a többi wiki rendszerrel egyetemben, egy speciális, a HTML-nél jóval egyszerűbb formázási nyelvet használ, amit egy mai tartalomkezelő rendszer nem ismer fel. Pl. a fejezetcímet és annak szintjét úgy lehetett renkívül egyszerűen meghatározni, hogy hány szóköz szerepelt egy sortörés után következő sor elején. Emiatt a szerkesztőfelületen a szerkesztőmezőből kinyert szöveggel nem sokat tudtam volna kezdeni, így a publikus felületről kellett leszedni a már HTML formátumban renderelt tartalmakat.

Letöltés emberi látogatók imitálásával

Mivel alaposan ismerek és használok egy pár web scraper / web automatizáló eszközt, ezért a költöztetésnél is erre – az eredendően az emberi látogatók imitálására épülő –technológiára esett a választásom. Mintha egy átlagos látogató végigkattintaná az összes elérhető weblapot, és lementené a saját meghajtójára. Ezzel a módszerrel nem kötött az, hogy milyen export formátumokat kínál fel a régi oldal (semmilyet), és milyen import formátumokat tesz elérhetővé az új CMS. Ezekkel az online marketing programozás-ról szóló bejegyzésemben is említett rendszerekkel bármelyik részét meg tudom szerezni egy weboldalnak, bármilyen reguláris kifejezéssel fel tudom dolgozni, továbbá tetszőleges formátumban le tudom menteni.

A letöltő (szkréper) szkript logikája

A Wiki alapú tartalomkezelő rendszereknek van egy jó tulajdonsága, ami nagyban megkönnyíti az általuk kezelt oldal összes tartalmának megtalálását: szinte mindegyiküknél van ugyanis egy tartalomjegyzék-szerű weblap, ahol az összes wikilap ki van listázva. Emiatt a letöltő szkriptnek nagyon egyszerű logikája lehetett: kinyitni a tartalomjegyzék-oldalt, leszedni az összes ottani linket, majd egyenként végigmenve a linkek listáján, megnyitni az összes lapot, megtalálni a html kódban a címet, a szülőoldalakat, a szövegtörzset és a hozzászólásokat, majd átkattintani változtatástörténet aloldalára, ahonnan a legutolsó módosítás dátumát lehetett megszerezni.

Kimenetként készült egy .csv fájl, ami az oldalhierarchiát tartalmazta: hogy melyik weblapnak melyek a szülőoldalai, egy másik .csv táblázat, ami a lapok főbb adatai (mint az eredeti URL, cím, módosítás dátuma) mellett a tulajdonképpeni szövegtörzset tartalmazta egy-egy sorban. Készült még egy fájl, ami soronként tartalmazta az összes hozzászólást, szintén főbb adataikkal, végül egy hibakeresésre szolgáló fájl, ahol ömlesztve volt a fent említett egyetlen szöveges mező tartalma a szövegtörzzsel és a hozzászólásokkal – ha netán valamit elrontottam volna az utófeldolgozásnál.

A szkript megvalósítása UBot Studio-val

Mivel az egész folyamat nem tűnt túl bonyolultnak, ezért az Ubot Studio-ra esett a választásom a megvalósításnál. Ez egy olyan vizuális programozási felületet ad, ami elsősorban kevés elágazással és egymásba ágyazott ciklussal rendelkező, lineáris logikájú szkripteknél tud jól működni. Ezt a szoftvert tulajdonképpen egy online marketingeseknek való autmatizáló rendszerként hírdetik, de a fejlesztésének eredeti célja a weboldalak szkrépelése és szpemmelése volt linkbeküldésekkel, hozzászólások elküldésével, stb. Ettől függetlenül még számos egyéb webes automatizációs feladatra használható. Egyik legjobb funkciója, hogy segítségével .exe formátumú, Windows operációs rendszereken egyszerűen futtatható programokat építhetünk. A botok futtatásához tehát nincsen szükség magára – az egyébként elég borsos árú – szoftverre. Mivel magyar nyelvterületen kb. rajtam kívül még egy-két helyen használták ezt a tartalomkezelő rendszert, ezért ezt az .exe fájlt nem raknám ki letölthető formában, de ha írsz nekem, vagy hozzászólsz ehhez a bejegyzéshez, akkor szívesen elküldöm, ha szükséged lenne a saját ZWiki oldalad költöztetéséhez erre.

A másik érdekes koncepciója az Ubot Studionak, hogy a vizuális programozási nézetből – ahol dobozokat húzkodsz, majd nyitsz meg a paraméterek beállítása céljából – át lehet váltani kódnézetbe is, ahol egy egyszerű szintaxisú kódnyelv segítségével is áttekintheted illetve szerkesztheted a szkriptedet. Egy kis türelemmel és az alábbi kód elolvasásával megértheted, hogy pontosan hogyan is épült fel ez a szkript:

 ui text box("Domain to scrape (without http(s)://):",#domain)
 allow javascript("No")
 navigate("{#domain}/FrontPage/contents","Wait")
 wait for browser event("Everything Loaded","")
 wait(5)
 set(#scraped,$scrape attribute(<class="formcontent">,"innerhtml"),"Global")
 add list to list(%pageurls,$find regular expression(#scraped,"(?<=href=\")[^\"]+"),"Delete","Global")
 loop($list total(%pageurls)) {
     set(#pageurl,$list item(%pageurls,1),"Global")
     navigate(#pageurl,"Wait")
     wait for browser event("Everything Loaded","")
     wait(5)
     set(#content,$scrape attribute(<class="content">,"innerhtml"),"Global")
     set(#content,$replace regular expression(#content,"<a\\ class=\"new\\ .+?(?=</a>)</a>","<!-- no wikipage yet -->"),"Global")
     set(#content,$replace(#content,$new line,$nothing),"Global")
     set(#content,$replace regular expression(#content,"\\t"," "),"Global")
     set(#contentonly,$replace regular expression(#content,"<p><div\\ class=\"subtopics\"><a\\ name=\"subtopics\">.+",$nothing),"Global")
     set(#contentonly,$replace regular expression(#contentonly,"<p><a name=\"comments\">.+",$nothing),"Global")
     set(#contentonly,$replace regular expression(#contentonly,"<a name=\"bottom\">.+",$nothing),"Global")
     add list to list(%parents,$scrape attribute(<class="outline expandable">,"innertext"),"Delete","Global")
     set(#parentlist,$list item(%parents,0),"Global")
     clear list(%parents)
     add list to list(%parents,$list from text(#parentlist,$new line),"Delete","Global")
     set(#parentlist,$replace(#parentlist,$new line,";"),"Global")
     set(#posttitle,$list item(%parents,$eval($subtract($list total(%parents),1))),"Global")
     set(#posttitle,$replace(#posttitle," ...",$nothing),"Global")
     if($comparison($list total(%parents),"> Greater than",1)) {
         then {
             set(#parent,$list item(%parents,$eval($subtract($list total(%parents),2))),"Global")
         }
         else {
             set(#parent,$nothing,"Global")
         }
     }
     append to file("{$special folder("Desktop")}\\{#domain}-page-hierarchy.csv","{#pageurl}    {#posttitle}    {#parent}    {#parentlist}    {$new line}","End")
     clear list(%parents)
     add list to list(%comments,$find regular expression(#content,"<p><a[^>]+name=\"msg.+?(?=<p><a[^>]+name=\"msg.+)"),"Delete","Global")
     loop($list total(%comments)) {
         set(#comment,$list item(%comments,0),"Global")
         set(#date,$find regular expression(#comment,"(?<=name=\"msg)[^@]+"),"Global")
         set(#title,$find regular expression(#comment,"(?<=<b>).+?(?=</b>\\ --)"),"Global")
         set(#title,$replace regular expression(#title,"<[^>]+>",$nothing),"Global")
         set(#author,$find regular expression(#comment,"(?<=</b>\\ --).+?(?=<a\\ href=\"{#pageurl})"),"Global")
         set(#author,$replace regular expression(#author,"<[^>]+>",$nothing),"Global")
         set(#author,$replace regular expression(#author,",\\ *$",$nothing),"Global")
         set(#comment,$find regular expression(#comment,"(?<=<br(|\\ /)>).+"),"Global")
         set(#comment,"<p>{#comment}","Global")
         set(#comment,$replace regular expression(#comment,"\\t"," "),"Global")
         append to file("{$special folder("Desktop")}\\{#domain}-page-comments.csv","    {#pageurl}    {#date}    {#title}    {#author}    {#comment}    {$new line}","End")
         remove from list(%comments,0)
     }
     navigate("{#pageurl}/history","Wait")
     wait for browser event("Everything Loaded","")
     wait(5)
     scrape table(<outerhtml=w"<table>*">,&edithistory)
     set(#lastedited,$table cell(&edithistory,0,4),"Global")
     clear table(&edithistory)
     append to file("{$special folder("Desktop")}\\{#domain}-page-content-raw.csv","{#pageurl}    {#lastedited}    {#content}    {$new line}","End")
     append to file("{$special folder("Desktop")}\\{#domain}-page-content-only.csv","{#pageurl}    {#posttitle}    {#lastedited}    {#contentonly}    {$new line}","End")
     remove from list(%pageurls,0)
 }

2. lépés: A tartalom feltöltése erre a WordPress oldalra

Miután végigment a szkript az összes tartalmon és strukturált formátumba hozta a tartalmat, majd lementette .csv fájlokba, további szkriptekre volt szükségem ahhoz, hogy ezekből a táblázatokból „táplálkozva” feltöltsem a tartalmakat a WordPress oldalra. Ennél a résznél is egy hasonló technológiához folyamodtam, tehát a szkriptem bejelentkezett az oldal admin felületére, majd megnyomta az „Új oldal létrehozása” gombot, kitöltötte a szerkesztő űrlap különböző mezőit, kiválasztotta a legördülő menükben a megfelelő opciókat, majd megnyomta a „Közzététel” gombot. Hogy hogyan és miért így csináltam, és mi volt ennek az értelme, arról talán majd egy következő bejegyzésben lesz szó.