Design doc Crawler

De la dexonline wiki
Sari la navigare Sari la căutare

Sumar

Construirea unui crawler și a unui index pentru site-uri românești (cu texte corecte din p.d.v. gramatical):

  • Găsirea de cuvinte pe care DEX nu le conține pentru a le adauga (cu definiții)
  • Când un utilizator caută definiții vrei să îi oferi și exemple în care s-ar folosi cuvântul respectiv.
  • O statistică a folosirii diacriticelor


Proiectarea in ansamblu

Crawlerul indentifică si salveaza paginile raw, extrage text-ul relevant si il salveaza ca fisier, depune in DB o linie in tabelul CrawledPage(id, timestamp, url, httpStatus, rawPagePath, parsedPageText), apoi din parsarea paginii respective vor rezulta link-uri, acestea vor fi adaugate in tabelul Link(descriere la Crawler mai jos). Dupa un timp de asteptare, crawler-ul poate incepe accesarea unei noi pagini. Astfel se cere o singura data pagina de la site, crawler-ul este separat de indexer, putand fi apelate independent.

Indexer-ul se uita in baza de date in CrawledPage si selecteaza urmatoarea linie cu textul neindexat(verifica eventual in tabela index daca exista), apoi dupa indexare o marcheaza.

Codul acesta zice ca se face exec in background

<?php
function execInBackground($cmd) {

    exec($cmd . " > /dev/null &");
}
?>

L-am testat, putem deci rula mai multe instante de indexer, in cazul in care indexarea ar merge mai greu decat parsarea + T wait.

Trebuie avută in vedere o verificare a fisierelor text, dacă au fost indexate. Pentru asta as propune un log + program php de monitorizare, in eventualitatea unui crash al sistemului.


Folosim log-uri peste tot si un panou (eventual meniu care alege fisierul log) care arata o statistica a activitatii.

Ex 1) timestamp | URL saved raw page parsed text 2) . . . 42) timestamp | URL saved raw page parsed text

Adaugam linii pe masura ce indeplinim activitati, astfel la un crash stim unde s-a oprit, in loc de saved raw page sau parsed text putem pune 0/1, T/F, etc

Astfel cand facem back-off si revenim putem avea unde s-a oprit.

Proiectarea in detaliu

Crawler

- un mecanism prin care să mint site-ul că programul meu e browserul (se rezolvă cu php curl), între accesul paginilor din site putem pune un T wait (timp de așteptare) 1 min.


Baza de date

   CrawledPage(id, timestamp, url, httpStatus, rawPagePath, parsedTextPath)


   Link(id, url, canonicalUrl, domain, urlHash, crawledPageId)


   httpStatus - HTTP status code
   Verificam daca linkul a fost expandat prin a vedea dacă a intrat în tabelul CrawledPage
   crawledPageId - același cu id de la CrawledPage, pagina în care am gasit link-ul
   urlHash - comparăm ca să nu inserăm același link din nou(md5(canonicalUrl)).

Totuși o să avem o problemă la linkuri relative vs linkuri absolute. Putem să eliminăm partea de Domain atunci cănd procesăm linkul, dar în final trebuie să compunem linkul absolut. Trebuie ignorate linkurile care n-au extensii relevante (imagini, etc).

   Nu am găsit încă o bibliotecă pentru asta asemănător cu realpath. Dacă e, facem noi un mecanism, compunem link-ul și apoi îl testăm încercând să accesăm pagina respectivă și vedem ce întoarce.


Parsare HTML

- o bibliotecă pentru parsat pagini html, am folosit simple_html_dom (e mică dar își face treaba) - as merge si pe o biblioteca care face strip html tags, acum depinde ce gasim si cum se comporta cu broken html

- dacă nu găsim nimic, putem trânti un regexp chior -- găsim

-ul în care stă conținutul care ne interesează și facem un regexp doar pentru acela. Asta va insemna ca o sa folosim regexp-uri diferite per site

Indexer

Indexer - php baza de date - paris


Tabele

Cuvant

id cuvantCuDiacritice cuvantFaraDiacritice idLexem=null (null daca nu are definitie)


Freq

id caleFisier idCuvant freq(de aparitie sau de cate ori apare?)


UseCase

id idCuvant text (propozitii)


Mecanism diacritice



Am să fac un tabel cu toate zonele care contin diacritice pe 8 sa 10 litere lungime și probabilitatea să se intample așa, plus un tabel în care o să țin aceste zone fără diacritice

zonaFaraDiactice

id text(string(10))


ZonaNeformatata

id idZonaFaraDiacritice count(default 1) text(string(10))


Facem count idZonaFaraDiacritice, apoi toate zonele cu același id (idZonaFaraDiacritice), calculam freq in tabelul FreqZone

FreqZone

idZonaNeformatata freq(float)


Putem face un panou ca la diacritice.com (idee Catalin), dropdownselect cu cea mai probabilă variantă (urmate de restul).

S-ar putea să nu aibă logică pe undeva că am scris-o în grabă. O mai revizuiesc.


Old Post

   - să găsesc cuvinte pe care DEX nu le știe, e destul de challenging cum aș face asta rapid, dacă cuvintele sunt ordonate alfabetic, atunci e mai simplu puțin, depinde și dacă ai copii de tabele ordonate invers sau nu (așa am putea folosi LIMIT 1 pentru a opri query-ul).


   Cred că asta se poate face în timpul indexării. Pentru căutarea full text pe dexonline, de exemplu, indexez un cuvânt găsit cu toate lexemele de care ar putea aparține. Astfel, „copii” poate proveni din „copie” sau din „copil”. Cele care nu aparțin de niciun lexem sunt cele pe care le dorim (ai să râzi, dar și baza de definiții a dexonline conține câteva cuvinte necunoscute).
   Mai trebuie tratate câteva cazuri speciale:
   - Forme contrase (am uitat exact cum se numesc), de exemplu „gândindu-” din „gândindu-se”.
   - Forme compuse, care adesea nu apar ca atare în dicționar: nemai+verb (doar la participiu sau la gerunziu -- nemaipomenit, nemaicrezând), prefixe ca re-, ne-, anti- etc.
   - Nume proprii

- daca avem o biblioteca toleranta la broken html, am putea face un parser general cu mai multe blocuri try-catch, (eventual logam tot ce se intampla aici), astfel evitam scrierea unei expresii regulate pentru fiecare site in parte.


- la partea de store-tuples n-aș pune definiția în DB, mai degrabă un url local de fișier.

index, timestamp, URL, raw_page_path, parsed_text_path


Inserarea de diacritice

- o tabelă de cuvinte care contin diacritice (cu toate formele), eventual un associative array în memorie folosind memcached sau altceva, cu cheia *cuvantul fara diacritice* si valoarea *cuvantul cu diacritice* - pentru fiecare cuvânt parsat, verificăm dacă se poate scrie cu diacritice și îl înlocuim cu varianta respectivă.

prelucrare tarball

- daca link-urile sunt relative, ar merge crawler-ul si local, dar eu zic ca n-are rost sa functioneze la fel ca pentru parsare online, deoarece ar insemna salvarea din nou a paginii raw. In plus e mai rapid sa parcurgem toate fisierele si directoarele dintr-un director radacina. De aceea propun să avem un crawler local care doar extrage text si inserează în DB în tabelul CrawledPage.


checkpoint & resume

Ca sa nu parcurgem de mai multe ori acelasi link, ideea e sa facem hash pe linkuri si sa comparam hash-urile.


+ adaugare la panoul de monitorizare + functiuni la panoul de monitorizare cu posibilitatea de a reporni crawler-ul sau indexer-ul pe o anumita pagina/fisier text

fisier de configurare

2 variante 1) conf.ini [db] key1=value1 key2=value2 Global.php care incarca intr-o variabila array statica generată de parse_ini_file(config.ini)

2) conf.php

define("_KEY1_", "VAL1"); define("_KEY2_", "VAL2");



Documentația Crawlerului

[outdated]

Nu uitați să instalați php-curl

Baza de date

Setările de bază

1) în /DEX/wwwwbase/Crawler trebuie: .htaccess, AbstractCrawler.php, AppLog.php, Crawler.php, MemoryManagement.php, simple_html_dom.php, WhiteList.txt, directoarele ParsedText și RawPage

2) în /DEX/: la dex.conf trebuie adăugat fișierul /DEX/wwwbase/Crawler/crawler_dex.conf (commentarii în fișier)

3) /DEX/phplib/models/CrawledPage.php și Link.php trebuie puse în același director pe server

4) Trebuie să puneți un fișier conținând user_agent-ul crawler-ului undeva într-un director care nu este public (am observat că unele site-uri nu cer user_agent (ex: romlit)).

Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0

Și apoi să-i dați calea absolută în dex.conf :: user_agent_location

5) Editați fișierul /DEX/wwwwbase/Crawler/WhiteList.txt, instrucțiuni de utilizare în fișier

Rulare crawler

în directorul /DEX/wwwwbase/Crawler/ rulați comanda

php Crawler.php

va porni crawlerul, acesta va lua primul domeniu din WhiteList.txt și va căuta linkuri nedescoperite în baza de date.

Crawler-ul dispune de un fișier de log: /DEX/wwwwbase/Crawler/crawler_log, iar ]n dex.conf puteți seta log2screen și log2file.

Setări pentru monitorul din browser

URL-ul relativ va fi /Crawler/index.php, nu /Crawler/

1) Necesitatea fișierului /DEX/wwwwbase/Crawler/index.php

2) În directorul /DEX/phplib/ trebuie adăugat fișierul SmartyWrap.php ce conține metoda

 static function smartyDisplay($skin) {
   
   self::$theSmarty->display($skin);
 }

3) În directorul /DEX/wwwbase/ajax trebuie adăugat fișierul fetchCrawlerStatus.php din același director de pe SVN

4) În directorul /DEX/wwwbase/styles trebuie adăugat fișierul crawler.css din același director de pe SVN

5) În directorul /DEX/templates/crawler trebuie adăugat fișierul crawler.ihtml din același director de pe SVN. Dacă am uitat ceva și programul nu merge, încercați să verificați scripturile încluse din crawler.ihtml.

6) În directorul /DEX/wwwbase/Crawler/ trebuie adăugat fișierul .htaccess din același director de pe SVN.