Programio

Jak vytvářím interní odkazy

Pokud píšete nějaký obsahově bohatý web, asi jste řešili, jak nějak rozumně zapisovat v kódu odkazy na ostatní interní stránky vašeho webu. Popíši způsob, který používám na svém webu matematika.cz.

Představme si, že máme v textu nějakého článku větu “A k tomu použijeme kvadratické rovnice.” Pokud chceme uživatelům poskytnout větší komfort, bude sousloví “kvadratické rovnice” odkazem na odpovídající článek. Otázkou je, jak to nějak jednoduše zařídit. Podobných spojení je totiž ve většině článků hromada, takže čím jednodušším způsobem odkaz vyrobíme, tím lépe pro nás. A za tu dobu, po kterou podobné články píši už vím, že ta jednoduchost udělá hodně (aneb v současnosti jsem schopný napsat jeden článek odhadem za dva až třikrát kratší dobu než před osmi lety).

Popíšu fáze, kterými jsem prošel:

  1. Prosté HTML. To je pro masochisty, protože abych vytvořil odkaz, musím zkrátka napsat <a href="/kvadraticke-rovnice">kvadratické rovnice</a>. Nic složitějšího neexistuje.

  2. Markdown zápis. Místo čistého HTML napíši [kvadratické rovnice](/kvadarticke-rovnice). Je to kratší, je to čitelnější, chytřejší editory mi to dokonce nějak barevně zvýrazní a některé poloWYSIWYG editory mi z toho udělají reálný klikací odkaz. Ale pořád musím psát hromadu věcí ručně.

  3. Pak jsem si vzpomněl, že taková Wikipedie umožňuje vytvořit jednoduše tím, že text, který má být odkazem, obalím do dvou závorek a systém z toho vytvoří odkaz sám. Tj. stačí napsat [[kvadratické rovnice]] a systém sám vytvoří odpovídající odkaz.

  4. Předchozí řešení zní jako svatý grál, jenže jsem rychle narazil. Většina spojení, ze kterých jsem chtěl udělat odkaz, nebyla v základním tvaru, ve kterém byl název článku. Co když by věta zněla “A k tomu použijeme kvadratickou rovnici.”? Pokud bych použil syntax [[kvadratickou rovnici]], systém by mi vytvořil odkaz na špatný článek. Mohl jsem se tak u takových spojení buď vrátit k předchozí možnosti a vypisovat odkaz ručně … nebo jsem mohl vytvořit jednu velkou databázi, kde klíčem byl text odkazu a hodnotou adresa stránky. To jsem udělal, takže jsem měl jednu databázi, ve které jsem měl záznamy typu

    kvadratickou rovnici -> /kvadraticke-rovnice

    kvadratická rovnice -> /kvadraticke-rovnice

    ...

    a při parsování odkazu jsem se vždy podívat do tabulky, našel odpovídající záznam a vytvořil jsem správný odkaz. Výhodou bylo, že jsem každou kombinaci zapsal jen jednou na jedno centrální místo a pak už jsem v článcích mohl opakovaně použít syntaxi [[kvadratickou rovnici]] a systém mi vygeneroval správný odkaz.

  5. Předchozí řešení mě ale docela rychle přestalo bavit, protože jsem pořád dokola aktualizoval databázi s odkazy a to mě prudilo. Všiml jsem si, že text odkazu není stejný jako cíl odkazu, ale je velmi podobný. Napadlo mě tedy, že můžu vzít text odkazu a najít adresu článku, která je nejvíce podobná textu odkazu a na tento článek nasměrovat odkaz. Pokud napíši [[kvadratickou rovnici]], tak by nemělo být těžké algoritmicky poznat, že chci odkázat na /kvadraticke-rovnice a ne na /derivace. Zbývalo už jen vymyslet implementaci.

    Ta byla kupodivu jednoduchá. Funkci, která dělá přesně takové porovnání, totiž většina z nás používá každý den – obsahuje ji kontrola pravopisu, která vám v případě překlepu nabízí, jaké slovo jste asi chtěli napsat. V nejjednodušší formě algoritmus v prvním kroku rychle zjistí, že toto slovo není ve slovníku známých slov a ve druhém kroku projde všechna slova a najde slovo, které je tomu nesprávnému nejpodobnější. To je přesně to, co jsem potřeboval.

    Python ve standardní knihovně obsahuje funkci difflib.get_close_matches, která dělá přesně to, co jsem potřeboval. Předáte jí seznam známých slov (v mém případě seznam existujících článků na Matematice) a slovo (v mém případě text odkazu). Funkce vám vrátí ze seznamu slov to, které je předanému slovu nejbližší. V mém případě by pro slovo “kvadratickou rovnici” vrátila funkce adresu “/kvadarticke-rovnice”.

    Toto řešení doteď používám a je extrémně efektivní. Ve chvíli, kdy chci vytvořit z nějakého spojení odkaz, zkrátka ho jen obalím [[...]] a v drtivé většině případů se vytvoří odkaz na článek, na který jsem chtěl odkaz. Ve zbytku případů buď napíšu odkaz ručně anebo přidám záznam do tabulky odkazů (kterou jsem si ponechal).

    Hledání odkazů samozřejmě provádím ve fázi preprocessingu, na serveru už se nic neprohledává.

  6. Chvíli jsem koketoval s myšlenkou vytvářet ty odkazy zcela automaticky. Tj., že bych napsal skript, který projde text článku a v něm sám najde spojení, ze kterých by šel udělat odkaz na jiný článek. Například by vzal všechny po sobě jdoucí dvojice slov a pokud by našel článek, jehož název se s dvojicí slov shoduje v nějaké vysoké míře (90 % apod.), tak z tohoto spojení udělá odkaz na zmíněný článek. Asi už by to nebylo úplně jednoduché, musel bych pořešit, aby v článku nebylo deset odkazů na stejný článek (to už spíš otravuje), případně aby v jednom odstavci nebylo deset odkazů a ve druhém žádný. Zkrátka nejspíš by z toho bylo víc problémů, než kolik by to přineslo užitku. Zatím tak zůstávám u pátého řešení.

    Nicméně stále mám toto řešení na paměti, protože kromě zjevné výhody, že nemusím ručně označovat odkazy, má ještě jednu výhodu: pokud napíši článek o derivacích, ve kterých zmíním slovo “integrál” a článek o integrálech napíši až o měsíc později, tak stačí o ten měsíc později článek o derivacích přeformátovat a rázem mám ze slova “integrál” odkaz na nový článek, aniž bych v článku o derivacích cokoliv změnil. Samozřejmě takto mohu po publikaci nového článku projet všechny články, které na webu mám. Nevýhodou je, že tvorbu odkazů nemáte vůbec pod kontrolou – jednou zkontrolujete, jestli se odkazy vytvořily správně, ale o týden později už může být vše jinak.

Popsané páté řešení určitě není pro každého, protože ne vždy je v textu tak silná podobnost mezi textem odkazu a cílem odkazu, ale pokud už nějaký takový web máte, můžete to také vyzkoušet. Je to silně návykové 8-)