IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: Rozpoznání v textu, url, videa, obrázku a vytvoření náhledu cizí stránky

V předchozím kvízu, Online test znalostí PHP, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
katrincsak
Člen
Avatar
katrincsak:31.5.2018 10:08

Zdravím,

potřeboval bych pomoc, nebo nasměrovat na nějaké hotové řešení.
**
Potřebuji v textu provádět detekci: **

  • detekovat odkazy a převádět na a href=''
  • detekovat youtube videa a do budoucna možná i další, ale zatím stačí youtube a embed.
  • v případě vložení odkazu na obrázek, tak převést na <img src=''>
  • aby toho nebylo málo, tak provádět náhled na cízí stránku jako to má např. FB.

První 3 body jsem skoro vyřešil za pomocí regulárních výrazů, které teda tak dobře neumím. Zjišťuji ale, že se jedná o mocnou věc takže bude stát za to si mezeru doučit a začít jednoduchými příklady, ale potřebuji dokončit práci co nejdříve. Problém ale nastává v momentě spojení všech tří replace, protože výsledná úprava na <a href=''> mi již udělá bordel a pak nefunguje zbytek. Moje funkční řešení, ale nemohu používat převedení odkazů.

Moje kompletní funkce

public function regExpCode($text){

        // youtube
        $text = preg_replace("/\s*[a-zA-Z\/\/:\.]*youtube.com\/watch\?v=([a-zA-Z0-9\-_]+)([a-zA-Z0-9\/\*\-\_\?\&\;\%\=\.]*)/i","<iframe width=\"420\" height=\"auto\" src=\"//www.youtube.com/embed/$1\" frameborder=\"0\" allowfullscreen></iframe>",$text);

        //images
        $regex = '~(([^"\'])|^)(https?://.+/.+\.(bmp|gif|jpeg|jpg|png))(([^"\'])|$)?~imUx';
        if(preg_match($regex, $text, $url)) {
                $text = preg_replace($regex, '<img src="'.$url[0].'" width="420" height="auto">', $text);
        }

        // link
        $text = preg_replace("#(https?://[a-zA-Z0-9:\.\-]+(?:/[a-zA-Z0-9/!,;:?%\#&=@~_.\-\[\]\(\)\'\$\*\+]*)?)#i", '<a href="$1">$1</a>', $text);
        /*
        $reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
        if(preg_match($reg_exUrl, $text, $url)) {
                $text = preg_replace($reg_exUrl, '<a href="'.$url[0].'">'.$url[0].'</a>', $text);
        }
        */

        return $text;
}

Co se týká načítání náhledu cizího webu, tak to jsem již ztracen úplně. Předpokládám, že budu muset udělat klasické načítání obsahu webu file_get_contents a nebo nějaký curl dotaz. Zda není jiná metoda, tak je to ale až druhý krok. nejdříve potřebuji detekovat jakýkoliv odkaz, což bych asi zřejmě udělal podobně jako je detekce klasických odkazů a pak url poslal na načtení obsahu. Pak už jen načíst title, description a případně první obrázek. Zda moje teorie je správná, pak bych potřeboval pomoct s regulárním výrazem, který mi umožní to vše spojit.

Moc děkuji za rady a pomoc.

Editováno 31.5.2018 10:11
 
Odpovědět
31.5.2018 10:08
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:31.5.2018 10:53

Nahledy si predstavujes jak, ze by to fungovalo. Php pracuje s php kodem, generuje html, posila www prohlizeci uzivatele. Prohlizec generuje www stranku. Takze to musis odeslat nejakemu programu, ktery ti dokaze poslat pak screen. Bud mas takovy nainstalovany a pustis to pres cmd. Nebo mozna jde prohlizec na serveru takto spustit pres cmd. Jenze na serveru prohlizec nemusi byt, k cemu, ze?
A nebo mne napada moznost pouzit javascript a canvas, asi. Otevrit nove okno, vykreslit, udelat screen a odeslat na server nebo do canvasu. Nebo to mozna canvas nejak umi. Nevim, nikdy jsem to neresil.

Moc nechapu to spojeni reg. vyrazu. Proc to chces spojit? Poud to funguje, tak je lepsi mit 3 vyrazy nez to spojovat do jednoho. Hlavne, pokazde pouzivas jine modifikatory a jakysi hodne zjednoduseny tvar pro url.
Napad bych mel. Pouzit call back a jediny vyraz, ktery vybere url. V call-backu se pak rozhodnes, co je to za url. Uplne by stacilo pres strpost najit youtube nebo pres substring posledni znaky porovnat s priponami obrazku cyklem. Ale muze to byt reg vyraz, to uz je fuk.

function replaceCb($matches) {return '123'.$matches[1];}
$patt = "~(http|https|ftp|ftps)(\S*)~";
$str  = preg_replace_callback($patt, 'replaceCb', $str);

http://php.net/…ace-callback

Editováno 31.5.2018 10:54
 
Nahoru Odpovědět
31.5.2018 10:53
Avatar
katrincsak
Člen
Avatar
Odpovídá na Peter Mlich
katrincsak:31.5.2018 11:06

Vše probíhá tak, že vytvoříš příspěvek, který se ajaxově odešle. Z důvodu některých omezení, kterým jsem se musel přizpůsobit, tak si html generuji v php a pošlu zpětně hotové html, které vložím javascriptem. Takže kontrola textu probíhá již na straně serveru tedy php, které provede úpravy v obsahu. S tím náhledem si nejsem jistý zda jsme se pochopili správně. Asi nepoužíváš FB že? Tam když vložíš jakýkoliv odkaz na web, tak se rovnou vytvoří náhled. Aktuálně není řešením zda se ukáže hned, nebo až po odeslání příspěvku. Jde pouze o ten samotný proces jak dojde k načtení těch dat, nebo aspoň tohle jsem se snažil popsat.

 
Nahoru Odpovědět
31.5.2018 11:06
Avatar
katrincsak
Člen
Avatar
Odpovídá na Peter Mlich
katrincsak:31.5.2018 11:11

Zapomněl jsem odepsat na další věci:

Sjednocovat to nepotřebuji, což by asi možná ani nešlo, nebo nevím jak daleko jsou schopný reg výrazy. Ale jde o to, že jakmile se mi převedou odkazy na a href, tak v tu chvíli to je již špatně pro ostatní regulární výrazy. Kdybych to uměl, tak si to asi přepíšu, ale musel bych se tomu teď věnovat delší dobu a hrát si s tím.

Na ten cllback se podívám co vše umí a zda by vytáhl všechny odkazy, které bych pak jen přepsat za pomocí např str_replace, tak bych si s tím poradil.

 
Nahoru Odpovědět
31.5.2018 11:11
Avatar
katrincsak
Člen
Avatar
Odpovídá na Peter Mlich
katrincsak:31.5.2018 11:43

Nechal jsem se inspirovat a použil jsem callback. lze ten regexp upravit, aby to vracelo jenom ty url ?

Udělal jsem:

$text = "Mám tento obrázek: http://www.skrabal-auto.cz/images/PNGPIX-COM-BMW-M2-Coupe-White-Car-PNG-Image.png také jsem si vložil video https://www.youtube.com/watch?v=25dswmOUmD0 a rád bych to vše odkázal na http://Seznam.cz a ideálně i s náhledem.";

function replaceCb($matches) {return ";".$matches[0];}
$patt = "~(http|https|ftp|ftps)(\S*)~";
$str  = preg_replace_callback($patt, 'replaceCb', $text);

foreach(explode(";",$str) AS $url){
        echo $url."<br>";
}

Výsledek

Mám tento obrázek:
http://www.skrabal-auto.cz/images/PNGPIX-COM-BMW-M2-Coupe-White-Car-PNG-Image.png také jsem si vložil video
https://www.youtube.com/watch?v=25dswmOUmD0 a rád bych to vše odkázal na
http://Seznam.cz a ideálně i s náhledem.
 
Nahoru Odpovědět
31.5.2018 11:43
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:31.5.2018 12:46

http://php.net/…n.syntax.php
https://www.regextester.com/pregsyntax.html

$patt = "~(http|https­|ftp|ftps)(\S*)~";
\S = any character that is not a whitespace character
* = 0 or more quantifier
To ostatni je klasicky or http|https|ftp|ftps = http or https or ftp or ftps

$patt = "~(?:http|htt­ps|ftp|ftps)(?:\S*)~";
(?: = non-capturing subpattern

Proc si tam nedas var_dump($matches); ? Ten vyraz jsem se snazil jenom zjednodusit. Hledej cokoliv, co zacita http a pod a pokracuj, dokud nenarazis na mezeru nebo konec.

function replaceCb($matches) {return ";".$matches[0];} // to je normal funkce
function replaceCb($matches)
{
//echo "<br>".$matches[0];
echo '<pre>';
var_dump $matches;
echo '</pre>';
}

Ty delas to, ze tam vracis puvodni text a jen pridas strednik. Takze explodovani podle stredniku ti vygeneruje, co ti vygeneruje.

"123aaa456aaa789" -> najdi aaa, nahrad ';'+'aaa' -> "123;aaa456;aaa789" -> explode(;)
123
aaa456
aaa789

Tak, to se dalo ocekavat, ne?

Pozor. To me zjednoduseni neresi pripad, kdy je adresa ukoncena teckou.
\S = any character that is not a whitespace character
whitespace = space, formfeed, newline, carriage return, horizontal tab, and vertical tab
' ' = space, klasicka mezera
\f formfeed (hex 0C)
\n newline (hex 0A)
\r carriage return (hex 0D)
\t tab (hex 09)

Editováno 31.5.2018 12:46
 
Nahoru Odpovědět
31.5.2018 12:46
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:31.5.2018 12:52

Pro zajimavost, v javascriptu pattern (hledany vzor) pro link vypada takhle. Uvazujes domenu jako ip adresu nebo text. Kdyz to cele nahradis jenom \S, tak je to fakt hooodne zjednodusene.

function filterLink()
{
var ip0       = "(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"; //255-250 249-200 199-100 99-10 9-0
var ip        = "(?:(?:"+ ip0 +"\\.){3}"+ ip0 +")";
var hostsep   = "_\\.-";
var linksep   = "#%&-;\\.\\/\\=\\?_~";
var xProtocol = "(?:(?:(?:file|ftp|ftps|gopher|http|https|news|nntp|sftp|telnet)\\:\\/\\/)|"+ ip +")";
var xDomain   = "(?:(?:[A-Za-z0-9](?:(?:["+ hostsep +"]?[a-zA-Z0-9]*)*)\\.[a-zA-Z]{2,9})|"+ ip +"|localhost)";
var xPort     = "(?:\\:[0-9]{2,4})?";
var reg_link  = "\\[("+ xProtocol +"("+ xDomain +")"+ xPort +"(?:\\/)?(?:[A-Za-z0-9" + linksep +"])*)\\]"
//alert(new RegExp(reg_link,'g').test("[http://msdn.microsoft.com:80/scripting/default.htm]"));
//alert(new RegExp(reg_link,'g').test("[http://www.quantum-star.com]"));
/*link*/
return reg_link;
}
 
Nahoru Odpovědět
31.5.2018 12:52
Avatar
katrincsak
Člen
Avatar
Odpovídá na Peter Mlich
katrincsak:31.5.2018 13:37

Děkuji za rozepsání.
Výstup se dá samozřejmě očekávat, ale šlo mi o to že to nevrátilo jen URL, ale i zbytek textu a jakmile to narazilo na další url, tak z toho vytvořilo další záznam v poli a takhle až do konce řetězce. Právě že by to chtělo vytvořit pole, ale jen s url a to v momentě kdy to dorazí na konec a nejlépe i v momentě kdy to narazí na tečku. Je pravda, že z toho výstupu není tak jednoduše zřejmé co jsem tím chtěl přesně ukázat, to se omlouvám.

To poslední co posíláš jsem našel také, ale je pravda, že s tím potřebuji pracovat mnohem víc komplexně a to z důvodu výše vypsaných věcí, které potřebuji do toho všeho implementovat.

Rozhodně jsi mi dal myšlenku a i když jsem včera na callback narazil, tak jsem stále řešil problém zaujatě. Na konec díky tobě mě napadlo to vyřešit pomocí funkcí str* a aspoň si to vše můžu upravit přesně jak chci. Není to možná úplně nejlepší řešení, ale ten regexp v pozadí bude dělat stejně něco podobného. Takhle je to pro moje znalosti mnohem srozumitelnější a již to jen přepsat na trochu lepší konstrukci a do mého MVC. Následně s tím aspoň mohu pracovat a lépe delegovat. Nemůže se mi aspoň takto stát, že se mi přepíše znova ta samá věc. Chybí tam samozřejmě ještě čtení toho obsahu, ale ověřím si jaké jsou ještě možnosti, ale curl, nebo file_get_contents asi bude jediná možnost. Možná curl by mi mohl vytáhnout jen hlavičku, ale to se musím ještě podívat.

Moje hrubé řešení, které ještě zásadně upravím (testuji to jen jako samostatný script).

$text = "Mám tento obrázek: http://www.skrabal-auto.cz/images/PNGPIX-COM-BMW-M2-Coupe-White-Car-PNG-Image.png také jsem si vložil video https://www.youtube.com/watch?v=25dswmOUmD0 a rád bych to vše odkázal na http://Seznam.cz a ideálně i s náhledem.";

kontrolaUrl($text);

function kontrolaUrl($text){
        preg_match_all('#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $text, $match);

        foreach($match[0] AS $url){
                $relace = false;

                $arrYTB = array(
                        "youtube.com/watch?v"
                );

                $arrIMG = array("jpg","jpeg","png","gif","bmp");

                foreach($arrYTB AS $key=>$ytb){
                        if(strpos($url,$ytb)){
                                $relace = true;
                                $text = embed($url, $text);

                                break;
                        }
                }

                if($relace == false){
                        foreach($arrIMG AS $key=>$img){
                                $sufix = substr($img, -5);
                                if(strpos($url,".".$sufix)){
                                        $relace = true;
                                        $text = img($url, $text);

                                        break;
                                }
                        }

                        if($relace == false){
                                $text = alink($url, $text);
                        }
                }
        }

        var_dump($text);
}

function embed($url,$text){
        parse_str(parse_url($url, PHP_URL_QUERY ),$get);

        if(!empty($get['v'])){
                $embed = '<iframe id="ytplayer" type="text/html" width="420" height="auto"
          src="https://www.youtube.com/embed/'.$get['v'].'?autoplay=1&origin=http://vajeno.cz"
          frameborder="0"></iframe>';

          return str_replace($url, $embed, $text);
        }else{

        }
}

function img($url,$text){
        $img = "<img src='".$url."' width='420' height='auto'>";
        return str_replace($url, $img, $text);
}

function alink($url,$text){
        $alink = "<a href='".$url."'>".$url."</a>";
        return str_replace($url, $alink, $text);
}
 
Nahoru Odpovědět
31.5.2018 13:37
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:31.5.2018 14:50

Stale nechapu, proc to delas slozite :) Proc to komplikujes pres str_replace. Cykly navic. Proc to hned nenahradis? Zkusils tam dat ten var_dump? Ne. Njn...
V tom calbacku mas primo tu url. Uvnitr si rozhodni, co vratis returnem, co tam ma to replace dat.

function replaceCb($matches)
{
$url = matches[0];
if (strpos($url,'youtube')!==false) return htmlEmbed($url,'text')
if (strrpos($url,'jpg')===0) return htmlImage($url); //rpos je reverse, ale to je fuk, pouzi klidne dalsi replace na tu url, viz tvuj puvodni kod
}
$patt = "~(?:http|https|ftp|ftps)(?:\S*)~";
$str  = preg_replace_callback($patt, 'replaceCb', $str);

Mozna to pochopis z tohoto:

$patt = 'aaa';

$str  = preg_replace($patt, '<a href="$1">$1</a>', $str );

$str  = preg_replace_callback($patt, 'replaceCb', $str);
function replaceCb($matches) {return  '<a href="'.$matches[1].'">'.$matches[1].'</a>',];} // to dela uplne to same $matches[1], mozna $matches[0], z pameti nevim, musel bych testnout
// najde 'aaa' a nahradi ho za  '<a href="aaa">aaa</a>',

Jinymi slovy, ty to pole vubec nepotrebujes.

Editováno 31.5.2018 14:53
Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
31.5.2018 14:50
Avatar
katrincsak
Člen
Avatar
Odpovídá na Peter Mlich
katrincsak:31.5.2018 18:32

Zkoušel jsem var_dump, ale nějak nevím co by mi zásadního udělalo krom toho, že mi vypíše pole/objekt. A také uvedený příklad chápu, je to sice jednodušší, ale nejsem si jistý zda-li v praxi je to skutečně použitelné.

Jenže tenhle tvůj příklad si zkus spojit a udělat to vše v rámci jednoho textu. Ve kterém budeš mít embed video, obrázek a ještě potřebuješ udělat hypertext. Bez ohledu na pořadí, jakmile to najde http/https, tak k tomu přidá tagy + potřebuji dotáhnout data z webu, další dotaz provádí kontrolu zda je to youtube video. Hlavně potřebuješ vědět co chceš převádět na co, respektive nelze převést každý url na hypertext a href, protože je nutné ten samý odkaz použít např u obrázku a nebo v embed.

Praxe:

URL převedeš na hypertext a href, ten samý odkaz je ale obrázkem a nebo youtube a buď se stane append, nebo prepend v tu chvíli máš v sobě tagy a to je to co je nežádoucí, protože to následně nemůže fungovat.

Proto je potřeba konkrétní odkaz kontrolovaně převést jenom jednou a toho nelze docílit, aby to bylo takhle jednoduché. Na víc jsem psal, že těch embed může být více a tak to pole mi na konec připadá v pořádku. A ten preg_replace_ca­llback dělá následně to samé, prochází jedno pole za druhém a volá volanou funkci. Možná bych dokonce řekl, že toho musí dělat více a funkce se stává náročnější, ale použít by se dala.

Samozřejmě si rád nechám poradit, ale tohle neřeší veškeré věci. Pravda je, že mě napadá ještě jedna věc pro výsledné zobrazení a možná řeším věc, kterou na konec nevyužiji. Protože si uvědomuji, že když tam vložím 2 odkazy na obrázek, jedno video a odkaz na web, tak nebudu vše zobrazovat na jednou. Vyberu pouze první odkaz a ten detekuji co je zač, podle toho vytvořím náhled a zbytek se stane hypertextovým odkazem.

Koukal jsem jak načítat náhled cizího webu, ale jinak než jak jsem psal to asi skutečně nepůjde. Ale za pomocí xpath a file_get_contents je to proces na 3 řádky, jen budu muset ještě detekovat zda cesta k obrázku je absolutní a nebo relativní a zda to začíná lomítkem a nikoliv.

Každopádně moc děkuji za snahu, určitě si mi otevřel oči a donutil mě nad tím přemýšlet trochu jinak.

 
Nahoru Odpovědět
31.5.2018 18:32
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:1.6.2018 7:24

U ciziho webu musis sledovat take tagy base. S cestou by to bylo mnohem slozitejsi, tam se muzou vyskytovat vylomeniny jako ./ ../ nebo /

 
Nahoru Odpovědět
1.6.2018 7:24
Avatar
katrincsak
Člen
Avatar
Odpovídá na Peter Mlich
katrincsak:1.6.2018 8:01

To máš asi pravdu, ten může tak docela měnit relativní cestu, která se pro mě musí stát absolutní a byla by tedy chybná. A já to na svém webu do konce používám také. Děkuji za připomínku.

Za tvou ochotu ti dám řešení i když tomu tak není na 100% protože by problém jak jsem psal výše nevyřešil, ale dle konečné úvahy na zobrazení prvního odkazu a převedení zbytku se dá již použít.

 
Nahoru Odpovědět
1.6.2018 8:01
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 12 zpráv z 12.