Posts with tag Programování

Jak je na tom reCAPTCHA v praxi

Mar| 1 2010

V článku Jak přechytračit CAPTCHA Resolver jsem nedávno psal, jak jsou internetoví roboti šikovní v rozpoznávání různých obrázkových textů, které je mají právě odhalit. Na konci jsem zmínil projekt reCAPCHA, který nabízí snadnou integraci kompletního testu do vašich formulářů.

Nevedly se mi úpravy vlastního CAPTCHA testu, kde ani při použití překrývajícího se jednobarevného textu neměli roboti žádný problém vložit mi do diskusního fóra nesmyslné údaje. Nakonec jsem zkusil právě zmíněný reCAPTCHA a mile mě překvapilo hned několik věcí:

Recaptcha je bezbariérová

Jako první mě překvapila snadná integrace. Vygenerovaný JavaScriptový kód sice vypadá složitě, nicméně pro vložení CAPTCHA testu na stránky stačí registrace na stránkách recaptcha.net a řízení se jasnými pokyny.

Potom mě překvapila rychlost, jakou jsou data z externího serveru recaptcha.net stahovány. Na to, kolik tento server musí vyřizovat požadavků a že požadavky generují netriviální grafický výstup, je to paráda.

A nakonec mě překvapily výsledky. Od nasazení ReCaptchy (dnes přes jeden týden) žádný robot neuspěl. Je možné, že je jen otázkou času, kdy i reCaptcha bude přechytračena, nicméně pro dnešek musím říct, reCaptcha nemá chybu.

Nakonec ještě zmíním, že kromě obrázku lze text i přehrát, tedy stránky budou přístupné i slabozrakým či jinak zrakově postiženým.

Tagy:Osobní | Internet | Programování | Sítě | Bezpečnost



Facebook aplikace - jak uspět

Mar| 1 2010

Marketing za pomoci sociálních sítí, to je trend současnosti i blízké budoucnosti. Ukážeme si pár pravidel, které je dobré dodržovat při propagaci Vašich Facebook aplikací. Článek vychází z jednoho příspěvku na diskusním fóru vývojářů Facebook aplikací.

Základní scénář

Základní scénář každého uživatele Facebook aplikace je následující:

  1. Uživatel je pozván přítelem
  2. Uživatel akceptuje pozvánku
  3. Uživatel autorizuje aplikaci
  4. Uživatel provede nějaké akce v aplikaci
  5. Uživatel posílá pozvánku přátelům
  6. Uživatel přestává používat aplikaci
  7. Uživatel odebírá aplikaci ze svého seznamu

Cílem každého provozovatele aplikace je dostat uživatele přes kroky 1,2 a 3 do stavu, kdy cyklí mezi 4 a 5. Jakmile se dostane do stavu 6 a 7, je zle.

Je dobré si uvědomit, že i přesto, že nejvíce zisku nám uživatel přinese mezi kroky 4 a 5, musíme se při vývoji postupně zaměřit na všechny kroky. Co s nimi budeme dělat? Analyzovat.

Neautorizujte bezhlavě

Za prvé potřebujeme odstranit všechny překážky, které stojí před uživatelem. Například striktní příkaz require_login, který zobrazí okno s přihlášením nebo autorizační okno pro nepříhlášené, resp. neautorizované uživatele, není nutné volat vždy. Naopak je dobré uživateli nabídnout část obsahu aplikace i bez nutnosti mít aplikaci povolenu. Stejně, jako zákazníci rádi chodí po obchodě, aniž by museli předem souhlasit s koupí, brouzdají uživatelé Facebooku po stránkách a aplikacích a jistě nemají potřebu autorizovat každou hloupost jen proto, aby zjistili, oč se jedná.

Dobrý způsob, jak nalákat uživatele k samotné autorizaci, je znefunkčnit některé zajímavé odkazy nebo tlačítka, jejichž popis ale návštěvníkovi napoví, co se pod nimi skrývá.

Distribuční kanály

Pro propagaci své aplikace je nutné využívat ne jeden, ale všechny nabízené distribuční kanály, neboť aplikace se nebude rozmnožovat sama. Ať je to registrace do adresáře aplikací, možnost zasílání pozvánek přátelům, placená Facebook reklama, nebo využití tlačítka sdílet. Každý z těchto způsobů naláká jinou sortu uživatelů, takže nezapomeňme na žádný.

Neusněte na vavřínech

Jakmile dosáhne počet vašich příznivců dostatečného množství, neusněte na vavřínech. S vašimi uživateli/fanoušky je nutné neustále komunikovat a aplikaci různými způsoby dále podporovat (např. obměňovat text na pozvánkách a zkoumat, který má nejlepší úspěch), jinak by časem všichni odešli, a to stejně tak rychle, jako přišli. Naopak je potřeba motivovat uživatele vrátit se zpět a pozvat nové kamarády.

Konkurence skýtá dobrou inspiraci

Inspirujte se u konkurence. Jak to, že některé hry mají tak mnoho uživatelů? Jednoduše motivují současné uživatele, aby lákali své kamarády, neboť za to mají nějaké výhody ve hře. Taková propagace je potom daleko cennější, než jakákoliv jiná reklama. A navíc je zdarma.

Závěrem

Nakonec několik myšlenek, které byste měli mít vždy na paměti.

  • Pokud budete mít opravdu zajímavou aplikaci, uživatelé se vrátí sami a rádi
  • Vyvíjíte v sociální síti, tedy i vaše aplikace musí být sociální (interakce s přáteli, s provozovatelem, ...)
  • Registrace v adresáři aplikací udělá své
  • Využívejte dostupné zdroje komunikace
  • Placené reklamy na Facebooku nejsou úplně od věci
  • Zasílejte fanouškům aktualizace a novinky

Zdroje:
http://forum.developers.facebook.com/viewtopic.php?pid=167262
http://forum.developers.facebook.com/viewtopic.php?pid=120077

Tagy:Internet | Programování | Sítě | Facebook



Jak přechytračit Captcha Resolver

Jan|20 2010

Captcha ("completely automated public Turing test to tell computers and humans apart", tedy "plně automatický veřejný Turingův test k odlišení počítačů a lidí") většinou nutí uživatele opsat text z grafického obrázku. Začínal jsem s velmi jednoduchým barevným textem na pozadí, které zvořily vodoznaky. Po pár dnech jsem se stal objetí jednoho z Captcha Resolverů, který mým testem bez větších problémů prošel.

Začal jsem tedy upravovat výsledný text s cílem ztížit robotům jeho rozpoznání. Přidal jsem bílé linky, které přerušují písmenka a barevné obedélníky, které mají robota zmást.

Co na to roboti? Nemají problém.

Dále jsem zkoučel rozmazání. Co roboti? Opět v pohodě rozpoznají. Zkoušel jsem další a další věci, ale výsledkem toho byla jen obtížnější čitelnost pro člověka, ale zároveň stále příliš dobrá čitelnost pro roboty.

captchaNeplatilo na ně:

  • různá barva písmen
  • vodoznaky na pozadí
  • obdélníky pro zmatení
  • rozmazání (ve skutečnosti je rozmazání jedním z kroků při rozpoznávání textu, takže jsem tím robotům akorát ulehčil práci)
  • dělení zanků bílými linkami
  • jiné barevné linky

captcha lepsiNaopak lepší výsledky (rozuměj větší zmatení protivníka - OCR robota) dosáhneme použitím:

  • jednobarevného textu
  • písmenka blízko u sebe (až se překrývají)
  • netradiční, nejlépe bitmapový font
  • část obrázku zinvertovat

RecaptchaZajímavější a úspěšnější ukázky a dokonce možnost integrovat tamější systém kontroly do vašich stránek nabízí projekt http://recaptcha.net. Vzhledem k přístupnosti není od věci i možnost nechat si text přehrát.

OCR roboti jsou den ode dne chytřejší a vygenerovat dobře čitelnou Captchu pro lidi, ale nerozpoznatelnou pro počítač je den ode dne těžší a těžsí. Zajímavou alternativou jsou pak logické otázky, od základních "kolik je pět a tři?" až po zajímavější "Kolik nohou má stonožka?".

Tagy:Internet | PHP | Programování | Prohlížeče | Počítače | Bezpečnost



Úprava Grub 2 v Ubuntu

Dec| 7 2009

Dlouho jsem hledal komplexní návod, jak upravovat nový Grub 2 v Ubuntu. Konečně jsem našel jeden zajímavější, naleznete ho zde.

Krátké shrnutí, jak nový Grub konfigurovat:

  • původní menu.lst najdete nyní v /boot/grub/grub.cfg, ten by se ale neměl editovat přímo
  • editovat lze soubor /etc/default/grub, kde lze upravit některá nastavení, rovněž editovatelná pomocí balíčku startup-manager
  • v souboru /etc/default/grub ale nelze editovat položky nebo pořadí položek zavaděče (jednotlivé systémy)
  • seznam jednotlivých systémů je generovaný pomocí spustitelných souborů v adresáři /etc/grub.d/
  • soubory jsou ve zmíněném adresáři pojmenovány podle vzoru xx_neco, kde xx je dvojciferné číslo a určuje tím pádem pořadí zpracování souborů
  • všechny instalace linuxu se vygenerují skriptem 10_linux
  • instalace jiných OS, například Windows, se vygenerují skriptem 30_os-proper
  • pokud tedy chceme mít Windows na prvním místě, přejmenujeme 30_os-proper například na 07_os-proper
  • pro změnu pozadí stačí editovat cestu k obrázku ve skriptu 05_debian_theme
  • Po provedení změn v souborech /etc/default/grub nebo po editaci skriptů v /etc/grub.d/ je potřeba aktualizovat samotný /boot/grub/grub.cfg, což provedeme příkazem update-grub

Další návody naleznete ve zmíněném článku.

Tagy:Programování | Počítače



Hledání optimálního dotazu pro filtraci a řazení produktů s atributy

Nov|27 2009

Následující příspěvek se zabývá rychlostí databáze MySQL a hledáním optimálního SQL dotazu pro zobrazení doplňkových atributů u produktů.

Máme dvě tabulky, jednu s produkty, který obsahuje základní atributy a druhou s dodatečnými atributy, jejichž typ je definován sloupcem type. Tabulka atributy obsahuje cizí klíč produkt do tabulky produkty a položky s dvojicí klíčů (produkt, type) nemusí být unikátní. Znamená to, že můžeme mít více atributů stejného typu u jednoho produktu. Tabulky jsou v MySQL definovány následovně:


CREATE TABLE IF NOT EXISTS `produkty` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
  `price` decimal(8,2) NOT NULL,
  `active` int(11) NOT NULL,
  `text` text COLLATE utf8_czech_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `atributy` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product` int(11) NOT NULL,
  `type` int(11) NOT NULL,
  `value_number` int(11) DEFAULT NULL,
  `value_text` text COLLATE utf8_czech_ci,
  `value_datetime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `product` (`product`)
) ENGINE=InnoDB;

Tabulka pro produkty obsahuje 10 000 záznamů a pro atributy 100 000. Problémem, kterým jsem se zabýval, je vypsání produktů spolu s jejich atributy, přičemž produkty mají být omezeny podle hodnot atributů a seřazeny podle jednoho z atributů.

Konkrétně chceme například vypsat prvních 10 produktů, řazených podle datumu, starších jak červen, aktivní, cena větší než 100 a s textem obsahující řetězec 'sd'.

Dotaz pomocí spojování tabulek - JOIN


SELECT 
	main . * , 
	attr1.value_number AS p_price, 
	attr2.value_text AS p_text, 
	attr3.value_datetime AS p_datetime
FROM `produkty` AS main
LEFT JOIN atributy AS attr1 ON main.id = attr1.produkt
	AND attr1.type =2
LEFT JOIN atributy AS attr2 ON main.id = attr2.produkt
	AND attr2.type =3
LEFT JOIN atributy AS attr3 ON main.id = attr3.produkt
	AND attr3.type =4
WHERE 
	attr3.value_datetime < '2009-06-01 00:00:00'
	AND main.active =1
	AND attr1.value_number >100
	AND attr2.value_text LIKE '%sd%'
GROUP BY main.id
ORDER BY attr3.value_datetime
LIMIT 10

Dotaz trval 4s a mě zajímalo, která část dotazu je nejpomalejší. Zkusil jsem odebraz příkaz ORDER, dotaz trval pouze 2,5s, což je samozřejmě stále špatné. Další změnou bylo úplné vyhození omezujících podmíne, tedy celý příkaz WHERE.

Pokud jsem použil řazení, trval opět 4s, ovšem bez použití podmínek i řazení se rychlost změnila o 2 řády, na použitelných 0,04s. Důvod je zřejmý, při použití řazení musí databáze vypočítat všechny řádky a potom teprve seřadit, zatímco bez použití ORDER příkazu stačí připravit několik řádků, aby byl splněn LIMIT, a tím to hasne.

Bohužel se nemůžeme smířit s tím, že na stránkách budeme zobrazovat produkty náhodně seřazené a nenabídneme uživateli jejich filtrování. Pojďme tedy zkusit jiný typ dotazu, který nám vrátí stejná data.

Dotaz pomocí vloženého dotazu


SELECT 
	main.* 
FROM produkty AS main WHERE
(
	SELECT count(*) 
	FROM atributy AS attr1 
	WHERE attr1.product = main.id 
	AND attr1.type = 2 
	AND attr1.value_number > 700
) AND (
	SELECT count(*) 
	FROM atributy AS attr2 
	WHERE attr2.product = main.id 
	AND attr2.type = 3 
	AND attr2.value_text like '%sd%'
) AND (
	SELECT count(*) 
	FROM atributy AS attr3 
	WHERE attr3.product = main.id 
	AND attr3.type = 4 
	AND attr3.value_datetime > '2009-08-07 23:11:22'
)

Výsledek? Rychlost načítání je srovnatelná s předešlým, ale nelze použít řazení. Atributy se musejí načítat dodatečně. Další možnost, jak načítat podobná data je tu:

Dotaz pomocí obyčejného spojení dvou tabulek


SELECT main . *
FROM produkty AS main, atributy AS a
WHERE main.id = a.product 
AND (
	(a.type =2 AND a.value_number >500)
	OR 
	(a.type =3 AND a.value_text LIKE '%sd%')
	OR 
	(a.type =4 AND a.value_datetime > '2009-06-01 00:00:00')
)
GROUP BY main.id

Rychlost opět srovnatelná, při použití ORDER BY příkazu pomalejší o dva až tři řády (v rozsahu uvedeném na začátku článku řádově minuty).

Závěr:

Ať se snažím, jak se snažím, vymyslet dotaz, který by dokázal řadit i filtrovat produkty s atributy takto rozsáhlých tabulek, se mi nedaří. Na druhou stranu, jak rád říkám, "život je kompromis". Jde tedy o to zvolit ten správný kompromis, na výběr máme následující:

  • využít JOIN tabulek a omezit filtrování produktů na jeden, maximálně dva atributy, s rostoucím počtem filtrovaných atributů je dotaz složitější
  • nepoužívat řazení podle atributů, ale pouze podle sloupců tabulky produkty
  • atributy, podle kterých se nefiltruje, nenačítat pomocí spojování tabulek (nebo obecně při samotném výběru produktů), ale načíst je dodatečně

K poslední možnosti ještě poznámka: Často omezujeme výběr pomocí příkazu LIMIT a tím pádem je daleko výhodnější načíst data u všech takto vybraných produktů najednou (maximálně je jich zpravidla desítky) a přiřadit je k produktům až následně v aplikaci. Načtení atributů samotných je celkem rychlé například takto:


SELECT * FROM atributy WHERE produkt IN (...)

Pokud máte někdo nápady, jak sestavit zmíněné dotazy efektivněji, určitě se o ně podělte v diskuzi.

Tagy:Programování | Počítače | Databáze



Testování rychlosti dotazů v MySQL 5.1

Nov|26 2009

V příspěvku jsou popsány základní možnosti, jak zjišťovat rychlost a efektivitu různých SQL dotazů, za účelem výběru nejoptimálnějšího řešení.

Nejdříve něco málo o testování rychlosti dotazů v MySQL. Rychlost dotazů testujeme nejčastěji u sestavování složitějších dotazů, často doprovázené spojováním více tabulek. Pomocí PhpMyAdminu nebo konzole můžeme vytvářet libovolné dotazy a rychlost jejich zpracování je po provedení uvedena.

Musím hned zpočátku upozornit, abyste si předem velmi rozmysleli, jaký dotaz provádíte, pokud to není na vašem stroji, můžete nešikovným dotazem SQL server pěkně potrápit. V případě pochybností nebo i preventivně doporučuji použít na dotaz omezení pomocí LIMIT. To většinou zabrzdí vykonávání dotazu včas.

Při testování rychlosti dotazů často narážíme na první problém - využívání cache. Pokud podobný dotaz provádíme vícekrát nebo jsou data v paměti SQL serveru, výsledky jsou k dispozici rychleji a o složitosti dotazu se mnoho nedozvíme. Zakázat využití cache paměti lze doplněním klíčového slova SQL_NO_CACHE ihned za první SELECT. Tím bychom měli přemluvit server, aby nepoužíval cache.


SELECT SQL_NO_CACHE *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;

Pro pochopení, jak zpracovává složitější dotaz SQL server, lze využívat příkaz EXPLAIN, který se jednoduše přidá před zkoumaný dotaz.


EXPLAIN SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;

Pokud ale hledáme ideální řešení a vybíráme mezi více dotazy, které se liší strukturou, ale vracejí stejná data, příkaz EXPLAIN nám toho moc neřekne, místo něho je lepší použít proměnou last_query_cost. Optimalizátor tuto hodnotu nastaví vždy při vykonání nějakého dotazu v daném sezení a obsahuje nějaké číslo. Čím větší číslo, tím složitější dotaz.


SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;
SHOW SESSION STATUS WHERE Variable_name LIKE 'last_query_cost';

Samozřejmě mi nedá abych nepřipomenul, že velkou práci databázovému serveru ušetříte správným používáním indexů. Nakonec přikládám malý prográmek v Pythonu 3, který generuje náhodné hodnoty do tabulek produkty a atributy. Definice tabulek je následující:


CREATE TABLE IF NOT EXISTS `atributy` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`produkt` int(11) NOT NULL,
`type` int(11) NOT NULL,
`value_number` int(11) DEFAULT NULL,
`value_text` text COLLATE utf8_czech_ci,
`value_datetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `produkt` (`produkt`,`type`)
) ENGINE=InnoDB;


CREATE TABLE IF NOT EXISTS `produkty` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
`price` decimal(8,2) NOT NULL,
`active` int(11) NOT NULL,
`text` text COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

A slíbený program je zde:


import random

alphabet = 'abcdefghijklmnopqrstuvwxz '

def getString(mn, mx):
"""
Generate random string with the length between mn and mx.
"""
s = ''
for i in range(random.randint(mn, mx)):
s += alphabet[random.randint(0, len(alphabet) -1)]
return s

# generates data for table produkty
for i in range(1000):
print('INSERT INTO `produkty` (`id`, `name`, `price`, `active`, `text`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'%s\');'
% (getString(3, 20), str(random.randint(10, 1000)),
str(random.randint(0, 1)), getString(50, 500)))

# generates data for table atributy
for i in range(1000):
for j in range(5):
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'NULL\', \'NULL\');'
% (str(i), '2', str(random.randint(10, 1000))))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'%s\', \'NULL\');'
% (str(i), '3', getString(10, 1000)))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'NULL\', \'%s\');'
% (str(i), '4', ('2009-' + str(random.randint(1, 12)) + '-'
+ str(random.randint(1, 28)) + ' 19:08:23')))
Tagy:Internet | Programování | Python | Počítače | Databáze