Majme databázu rozmerov rádovo 10^3 až 10^7 záznamov, verejne prístupnú cez web formulár (naraz po jednom alebo niekoľkých záznamoch). Táto databáza je pre nás niečím zaujímavá, a chceme mať jej off-line kópiu.
V skratke je potrebné:
- Zmyslieť si, že danú databázu chceme stiahnuť.
- Stiahnuť.
- Tešiť sa.
- Dúfať, že ak sa to prevalí, tak novinári nebudú prekrucovať.
Disclaimer ----
Budeme popisovať rôzne úskalia pri ťahaní verejne prístupnej databázy. Nejedná sa o prienik do systému a surové "ukradnutie" databázy - to je síce spôsob efektnejší (nie nutne efektívnejší), nie vždy však jednoduchý, o zákonnosti ani nehovoriac.
Nežiadajte o detailnú implementáciu pre váš cieľ, ani o poskytnutie nejakej "stiahnutej" databázy, spravte si sami -- regexp powa.
Príklady tu uvedené sú v bashi, samozrejme dá sa to rovnako alebo aj lepšie spraviť v perli alebo inom skriptovacom jazyku. Bolo by takisto možné napísať to celé v C alebo vyšších kompilovaných jazykoch, či už volaním systémových API alebo pomocou nejakého frameworku, ale zrejme by to trvalo dosť dlho a
najmä by šlo z veľkej časti o "znovuvynaliezanie kolesa" -- v *nix shelli už totiž máme hotové všetky potrebné nástroje na sťahovanie a parsovanie výstupu, stačí ich pozliepať v skripte (rýchlosť kompilovaných programov v tomto prípade nepotrebujeme, viď. ďalej časť "Obmedzenia").
Používatelia Windows môžu siahnuť po Cygwine.
Ilustračná DB ----
Ako príklad poslúži jednoduchá jednotabuľková databáza. Budeme ťahať tabuľku zvieratiek a ich rodných čísel s L=5,396,168 riadkami (k 30.6.2007). Empiricky zistíme, že budeme pracovať s údajmi: id (v databáze), meno, mesto,
cislo.
ZvieratkoDB <http://zvieratko.db/> má 2 dynamické web stránky (podobne, ako veľa reálnych DB):
- search.cgi
- vyhľadávacia stránka s formulárom --- zadáme meno alebo časť mena
(?query=), vypľuje tabuľku s odkazmi na zobrazenie detailov
- show.cgi
- zobrazovacia stránka pre jedno zvieratko --- podľa odkazu z vyhľadávania
(?id=) zobrazí detaily o zvieratku, teda okrem mena a mesta, ktoré boli aj
vo výsledkoch vyhľadávania, aj rodné číslo
1. Jednoduchý prípad ----
Nech ZvieratkoDB nemá žiadne obmedzenia a id v databáze sú za sebou nasledujúce od 1 do L. Ručne stiahneme niekoľko exemplárov show.cgi?id=..., aby sme určili presný formát výstupu a spôsob, ako z toho HTML vyparsovať čisté dáta (pomocou grep, sed, prípadne awk, cut apod.). Celú databázu potom
stiahneme:
#!/bin/bash
# priklad c.1, tahanie zvieratiek so za sebou nasledujucimi id
i=0; L=5389180
while [[ $i -lt $L ]]; do
i=$((i+1))
wget -O - http://zvieratko.db/show.cgi?id=$i \
| grep ... | sed ... >> LokalneZvieratkoDB.txt
done
1.1 Ide to pomaly... ----
Dajme tomu, že jeden takýto wget zbehne za sekundu, čas parsovania a ukladania na disk zanedbáme. Potom stiahnutie bude trvať približne 5389180/3600/24 = 62.4 dní.
Preto skript rozdelíme na viaceré (rôzne počiatočné a koncové $i, a tiež rôzne .txt [aby sme nemuseli riešiť race conditions]) a pustíme na jednom alebo viacerých strojoch. Kým to cieľová mašina stíha (aj naša mašina a [bez]drôt), môžeme dosiahnuť až N-násobné zrýchlenie (N je počet navzájom
rýchlostne neovplyvnených "tahačov").
1.2 Ošetrovanie chýb ----
Wget nemusíme smerovať priamo do paličky, jeho výstup môžeme napríklad ukladať do /tmp/bla$$ a následne zisťovať, či sa podarilo daný záznam stiahnuť - pri chybe opakovať, alebo zapísať hlášku do logu, apod. Konkrétna implementácia závisí od detailov danej DB a prístupu k nej, toto necháme na
rozcvičku pre čitateľa.
-----------
V ďalšej časti (ak bude o ňu záujem) si povieme, ako na zložitejšie prípady, t.j. ako vypiecť s niektorými obmedzeniami.
----------
#1: Úvod, Disclaimer, Ilustračná DB, 1. Jednoduchý prípad, 1.1 Ide to pomaly..., 1.2 Ošetrovanie chýb
#2: 2. Obmedzenia, 2.1 Náhodné id, 2.2 Limit na IP, Ako získať veľa IP
#3: 2.3 Captcha, 2.4 Ďalšie obmedzenia, 3. Iné vychytávky, 4. Záver
Hmm skor by som povedal ze problem s rychlostou bude opacny... teda pokial samotne parsovanie cez X programikov (vratane obrovskeho perlu) nebude trvat 3x tolko co stahovanie. Na rychlej linke je to jasne, na pomalej linke az tak velmi viacnasobne spustenie nepomoze.
Server ktoremu trva sekundu kym vygeneruje stranku je dost pomaly/pretazeny. Realne by som ratal s hodnotami tak 0.1 az 0.5 sekundy a aj to su uz vyssie cisla. Ja skor do takychto programov (ktore sa viackrat za sebou pripajaju na webserver) davam maly delay (100 ms), aby som dany server nepretazoval.
Co sa tyka parsovania tak sa osvedcil jeden postup. Vacsina takychto dat je vypisovana html tabulkou, akurat ze kazdy ten html kod inac formatuje. Idealne na parsovanie je mat kazdy riadok tabulky na jednom riadku zdrojoveho suboru. Takze najdeme si napr "<tr>" a vlozime pred neho "\n". Dalej si najdeme "\n<td>" (pokial bola kazda bunka tabulky v zdrojaku na osobitnom riadku) a nahradime samotnym "<td>". Mensou obdobou sa to da pouzit na naformatovanie kazdeho html zdrojaku (hlavne tych co maju 30kb dat na jednom riadku a neda sa to citat). Ked mame kazdy riadok tabulky na osobitnom riadku v zdrojaku, je hracka napisat regexp, ktory z toho spravi napr. tab-delimited textovy subor, ktory sa da naimportovat do db.
Ta sekunda bola samozrejme nadsadena hodnota, iba ilustracna. Parsovat nemusis cez Perl, staci grep a sed, pripadne awk. Ucelom clanku nie je naucit sa parsovat vystup html :)
V dalsom dieli trilogie sa vyjasni, preco nam na case tahania+parsovania jednej stranky az tak velmi nezalezi, ked budeme obmedzovani ovela vyssimi casmi.
Takze zoberme si teoreticky databazu ktora obsahuje 500tisic udajov
Urobime si nejaky retardovany program ktory ju stiahne... Naco?
existuje tolko offline browserov pri ktorych si urcim co chcem stiahnet ze
pisat si soft je zbytocnost lenze niektore stranky ktore nechcu prist o udaje
roznimi onimy co cucaju obsah z webov (napr.: google) maju proti tomu zabespecenie
cize mozem si z jednej ip pozriet 1stranu za 1m. (cca) cize to mame vela minut
sak co napiseme si soft cez proxyni ne? no ani velmi ne kym najdem aspon 5%
proxin ktore pouzijem nato bude neskoro ale zoberme si ze tie proxyni mam
tak to pojde pomali cize ak ma web zabespecenie (trosku vyssie ako je NBU)
tak sme proste f <>. Cize jedine co mi ostava je pisat nadalej maily adminovi nech
mi odblokuje ip lenze admin je chuj a nedokaze mi napisat ani mail ze
ziadost zamietnuta ..... Cize vysledok ak web nechce prist o databazu a robi nieco proti tomu
tak su len dve moznosti najebat im server alebo pekne prosit a dufat ze admin nieje
az taky ...... ako v skutocnosti je .... Nic tym nechcem povedat ale som najebany a chcem pit
a mam nervy a tak ....... a ak sa nekomu nepaci ze neviem gramatiku nech ide do ....
Ja som to asi nemal rozdelovat na viac dielov :) ...len mi to ako 1 pripadalo moc dlhe. Offline browsery ti samozrejme na jednoduchy pripad pomozu, ale naco skladovat vela 10kB stranok, ked z kazdej potrebujes len 200 B. O "zabezpeceniach" bude pisane v dalsom dieli (dalsich 2), tento ber ako taky dlhsi uvod.
> V ďalšej časti (ak bude o ňu záujem) si povieme, ako na zložitejšie prípady,
> t.j. ako vypiecť s niektorými obmedzeniami.
Offline browsery ti samozrejme na jednoduchy pripad pomozu, ale naco skladovat vela 10kB stranok, ked z kazdej potrebujes len 200 B...
No ale ide tu o to že tak či tak aj tvoja metóda stiahne celú stránku, čiže sa tu skôr jedná o zaťaženie linky, stroja, servera ako nejaký problém s kapacitou.
Zdravim,
Myslíš, že každý select databázy je vedený cez URL? Čo ak "id" bude poslané napr. v hlavičke?
Ďalej, myslíš že človek bude mať doma N strojov a N navzájom nezávislými linkami?(o tie stroje ani tak nejde ako o tie linky)
Pod pojmom "id" je myslene cokolvek, co show.cgi zozerie. Vacsinou je to ciselna hodnota, a vacsinou je to priamo id zhodne s tym, co je v databaze (je to totiz najjednoduchsi sposob, ako webovy frontend implementovat). Ci je posielane cez GET, POST alebo ako je jedno, vzdy existuje nejaky sposob, ako sa na konkretny zaznam zo search.cgi na show.cgi dostat.
Pre zaujemcov odporucam pozriet slovensky web, vytipovat si nejaku peknu db hodnu stiahnutia, a pozriet si, akym sposobom su odkazy zo "search.cgi" na "show.cgi" (nazvy samozrejme nemusia sediet, ide o princip) riesene.
Na stiahnutie ZvieratkoDB, ak kazda show.cgi ma 10kB a nie su dalsie komplikacie (o ktorych az v dalsom dieli), budeme musiet stiahnut cca 52 GiB. Bezny smrtelnik to samozrejme nezvladne, bezny smrtelnik ani nevie co je to parsovat. Ani ho nenapadne, ze by to slo stiahnut. No a Ty to nemusis tahat doma :)
Zdravim,
Tu ide o to, že keby išiel select DB cez HTTP hlavičku, tak je tvoj skript uplne nahovno, ak ma chapeš chcem tým povedať len toľko, že tvoj príklad funguje iba za špecifických podmienok, čiže veľmi obmedzene.
No a už vidím ako by niekto ťahal v práci 52 Giga a to ani nehovorim v škole, kvôli jednej DB. Proste metóda, ktorú si zvolil je neefektívna lebo z celkového objemu čo musíš stiahnuť je drviva väčšina balast, ktorý aj tak zahadzuješ, je to podla mna svinstvo takto zaťažovať linku.
> select DB cez HTTP hlavičku
Co konkretne mas tym na mysli? Alebo daj priklad stranky, lebo neviem kde konkretne chces schovat data tak, aby o nich web prehliadac vedel, ale Ty sam nie.
> už vidím ako by niekto ťahal
No podla toho ako cenna ta db pre Teba je. Nehovorim ze to kazdy bude robit, to by bol iny bordel :) A ano, je to svinstvo, ale co ma clovek robit, ked chce vyhladavat zvieratka podla rodneho cisla a to sa cez webovy frontend ZvieratkoDB neda?
Co konkretne mas tym na mysli? Alebo daj priklad stranky, lebo neviem kde konkretne chces schovat data tak, aby o nich web prehliadac vedel, ale Ty sam nie.
Zdravím,
Mám na mysli to, že tvoj skript sa spolieha nato že "id" bude predávané metódou GET, čiže cez URL, keby bolo však predávané metódou POST, čiže v hlavičke a nie v URL tvoj skript je nefunkčný...
Skript je, samozrejme, uvedený ako ilustračný. Zjavne si si to nevšimol. Nabudúce budem zrejme musieť dať do disclaimera komentár ku všetkému. Totiž ani
| grep ... | sed ... |
Keby som chcel uviesť konkrétne a kompletné skripty, uvediem ich na konkrétnu cieľovú databázu. Keďže som sa chcel vyhnúť prípadnej zodpovednosti za taký čin, písal som všeobecne.
Pre posielanie metódou POST je možné jednoducho použiť prepínač
--post-data
, viď. wget(1). Napríklad:wget --post-data="id=$i" -O - <a href="http://zvieratko.db/show.cgi" title="http://zvieratko.db/show.cgi">http://zvieratko.db/show.cgi</a>
A práve že ten skript nielen že je ťažko použitelný, ale je aj príliš zjednodušený. Sorry, ale to si rovno mohol napísať aby sme použili v cykle wget, kde tu istu premennu pouzijeme na pocitadlo cyklu a aj ako premennu do "id" a nemusel si pisať celý článok, chce to proste viac ozrejmit problematiku, mal si vytvoriť alebo stacilo aj vymysliet fiktívnu db a konkrétne na príklade ukázať celý skript ako by fungoval, poprípade ešte pár alternatív, hned by to bolo prehladnejšie ako skript s tromi ...
ale to si rovno mohol napísať aby sme použili v cykle wget, kde tu istu premennu pouzijeme na pocitadlo cyklu a aj ako premennu do "id" a nemusel si pisať celý článok
Práveže som chcel naznačiť, ako také ťahanie môže prebiehať - príklad je oveľa názornejší, ako litánie o cykloch a premenných. A nechcel som zachádzať až príliš do detailov - to by potom nikto pre rozsah nečítal. Napríklad môj posledný all-in-one (čo sa funkcionality týka):
$ wc get.sh
(aj s poznámkami).369 1198 8328 get.sh
Fiktívnu db by som musel vyrobiť a zavesiť niekam na web. A nosiť drevo do lesa sa mi nechcelo, veď web je plný všakovakých databáz.
Je mi ľúto, ak Ti táto forma článkov nevyhovuje. V budúcnosti sa budem snažiť byť veľmi konkrétny. Dovtedy odporúčam man wget(1), grep(1), sed(1), gawk(1).