Autor Zpráva
danhill
Profil
Dobrý den,
opět řeším regexy :)
Mám dva dotazy.
1. Mám:
$htmlcache = file_get_contents("http://coord.info/GC19905");
a
echo $htmlcache;
Jak docílím toho aby se mi na stránku vypsal html kod a ne formátovaná stránka?
(to jen taková drobnost)

2.A to je mnohem horší. Potřebuji v html, které dostanu vyhledat kraj.
Ten ale znám dopředu a mám ho u sebe v databázi.
Takže si ten daný název kraje vyberu SQL dotazem a pak bych potřeboval pomocí foreach projít určitou část externího html a najít tam patřičné hledané slovo.
S tím, že return by byl název toho slova,který by se podařilo nalézt.
Tedy v uvedeném příkladě bych hledal někde v sekci:
<div class="minorCacheDetails Clear">
   <div>
        In Moravskoslezsky kraj, Czech Republic
    </div>
</div>
slovo "Moravskoslezsky",které mám mezi ostatními v databázi.
Jakmile by na něj foreach narazil, tak by funkce vrátila jednoduše "Moravskoslezsky"
Je akorát potřeba ošetřit to, aby to hledalo právě jen v určitých místech, protože není vyloučeno, že se kdekoli jinde v textu nebude vyskytovat třeba slovo Praha.

A další věc je ta, že jsou dva případy,kde se takto může kraj vyskytovat. Buď zde jak jsem uvedl, a nebo v případě např.:
$htmlcache = file_get_contents("http://coord.info/GC5CXXJ");
je kraj "Jihomoravsky" uveden v jiné části html a to hned dvakrát:
<meta name="description" content="Blanensky most ? (GC5CXXJ) was created by Joska4 on 09/19/2014. It's a Micro size geocache, with difficulty of 2.5, terrain of 2.5. It's located in Jihomoravsky kraj, Czech Republic.
a
<head id="ctl00_Head1"><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /><title>
    GC5CXXJ Blanensky most ? (Unknown Cache) in Jihomoravsky kraj, Czech Republic created by Joska4
</title

Samozřejmě by se na to dalo jít i obráceně. že prostě v textu kraj vyhledám a pak jen zkontroluji, zda ho mám v databázi a výsledek by byl stejný.
Ale problém je, že ne vždy ve staženém html je to stejné. Některé země totiž kraje nemají a je uveden pouze stát. Jako třeba zde:
$htmlcache = file_get_contents("http://coord.info/GC1A0KM");
kde potřebuji najít hned několik slov "Saint Kitts and Nevis" ...
A těch variant je prostě ještě mnohem více, proto bych byl raději za obecnou php funkci,která by mi prostě v html našla co potřebuji, respektive, která z mnou hledaných možností se v getnutém html nachází.
Pokud žádná, funkce frátí prázdnou hodnotu nebo false a já budu upozorněn, že u kodu XYZ nebyl kraj nalezen, s tím už si poradím,ale jak na samotnou funkci,která by tohle dokázala, s tím potřebuji pomoci.

Předem moc děkuji.
juriad
Profil
1) Viz Výpis zdrojového kódu » Ošetření HTML řídicích znaků

2) Použij nějakou knihovnu na zpracování HTML, pěkné je simplehtmldom.sourceforge.net/manual.htm

$html = str_get_html("http://coord.info/GC19905");
$sekce = (string) $html->find('div[class=minorCacheDetails Clear] div div', -1);

Pak v té sekci můžeš hledat pomocí regexů, zkusil bych něco jako:
/In (?:([a-z ]*), )?([a-z ]*)/i
Keeehi
Profil
juriad:
Co se té druhé části týče, tak jdeš na to přesně z druhé strany. Nemůžeš přece vyhledávat podle toho co neznáš a ani nevíš, zda to tam bude. Musíš na to jít přesně obráceně. Vyhledávat podle toho, co víš že tam vždy bude. A to jsou ty HTML tagy, jejich struktura je na každé stránce stejná. Tudíž ten regulár v obecnosti může vypadat takto
'~<div class="minorCacheDetails Clear">
   <div>
        (.*?)
    </div>
</div>~'
Dříve, když jsem se zabýval geocachingem jsem taky parsoval stránky kešek a dělal jsem to přesně takto.

Pár vzpomínek z té doby:
- Bílé znaky jsou zrádné. Taky záleží na modifikátorech za samotným reguláem a podle toho tečka žere konce řádků.
- V pár případech bylo nutné "řezat" ve více kolech, protože to lépe nešlo kvůli nejednoznačnosti. Prvně se vybrala nějaká větší část kódu a v něm se teprve hledalo to zajímavé.
Radek9
Profil
K tomu parsování HTML pomocí regexu si neodpustím poslat jeden nádherný příspěvek ze Stack Overflow. :-)
danhill
Profil
Děkuji moc za rady a názory, no to zas bude dílo na týden práce ... jdu se do toho pustit, no uvidíme kam až se dostanu a kdyžtak napíšu doplňující dotazy.
Keeehi
Profil
Radek9:
Ten příspěvek znám a částečně má pravdu. Kdybych měl nějaký neznámý kód a chtěl v něm najít třeba všechny odkazy, které mají všechno co každý slušný odkaz mám mít, tak na to regulárním výrazem opravdu nepopíšu. Pokud využívám regulární výraz na to, co by zvládlo pár funkcí strpos a substr, pak se to popsat rozhodně dá.

Je to však validní poznámka, že existují HTML/XML parsery a dá se využít jich. Já jsem je proto takové účely nikdy nepoužil, jen na tahání nějakých XML feedů. Alespoň pro ten případ vím, že byl velmi striktní, co se týče toho, jak XML má vypadat. Pokud tam byla nějaká chyba, odmítlo to spolupracovat.
Proto bych se chtěl zeptat, jak se to staví k nevalinímu HTML kódu? Největším problémem těch stránek titiž je, že část jeho obsahu je tvořena hráči samotnými, kteří mají možnost si tam vložit své HTML.
juriad
Profil
Keeehi:
XML parser je něco úplně jiného než HTML parser. HTML parsery pracují stejně jako prohlížeče, znají pravidla HTML i pravidla opravování nevalidního HTML.
Výsledkem obou parserů je DOM reprezentace, která nabízí vysokoúrovňové funkce na procházení dokumentu.

Pokud je něco deklarováno jako XML a je v tom sebemenší chybička, tak XML parser musí odmítnout zpracovat takový dokument. Pokud na nějaký takový nevalidní XML feed narazíš, kontaktuj jeho autora.
danhill
Profil
S regexy mi to prostě nechodí správně ... je tam mnoho variant a určitě bych ne všechny dokonale odladil a objeví se další a další a já bych se uifoval k smrti ...

Chtěl bych se v tomto případě vrátit ke své původní myšlence a sice hledá v html známý text.
Ze všeho co jsem zkusil mi nejlépe chodí tohle a je to až podezřele jednoduché :) :
$htmlcache = file_get_contents("http://coord.info/GC19905");

$select_regions = $mysqli->query("SELECT * FROM `region_type` ");
foreach ($select_regions as $select_region){
  if(preg_match('['.$select_region["region"].']', $htmlcache)) { 
    $region = $select_region["region"];
  }
}
echo 'Kraj :'.$region;

Jen bych tedy potřeboval do toho regexu
['.$select_region["region"].']
ještě definovat konkrétní úseky html, ve kterých se má hledat,aby se neprohledával jednak zbytečně celý html a jednak by se mohlo stát, že hledaný text bude použitý jiný jinde.
juriad
Profil
danhill:
Vyzobnutí si té části jsem ti ukázal v [#2] pomocí navigace v DOMu; Keeehi ti ukázal, jak to udělat pomocí regexů (osobně tomu nevěřím).
Nějakými takovými manipulacemi bys měl dostat jen obsah toho divu, ve kterém je ta lokace uvedená. S tímto textem budeš pracovat dále.

Kolik regionů máš v databázi? Pokud jsou jich alespoň desítky, tak na toto zapomeň. Musíš si navrhnout něco, co ten region získá, aniž by to vědělo, jaké regiony existují.
Pokud všechny lokace jsou popsatelné: In něco, země nebo In země, tak by ti ten regex v [#2] měl fungovat.
Pokud je víc formátů, tak buď můžeš zesložitit regex, nebo zkusit postupně několik regexů s tím, že jeden z nich se chytne.

Ještě poznámky k tomu tvému regexu:
Hranaté závorky slouží k něčemu jinému; obsah regexu musíš escapovat pomocí preg_quote.
danhill
Profil
No, ono se to řekne :) použij knihovnu ... Ale nerozchodil jsem to ... nevím proč .. teda vím, protože nechápu definici funkce "find" ...

regionů je konkrétně 707 v db a jsou to všechny na světě. Myslíš, že to může vadit, prohledat si to foreachem? Mě to zatím funguje nejspolehlivěji,co jsem zkoušel. jen vyeliminovat ty místa a nemůže se to splést a nemusím požít více než jeden if/else

No já takhle sestavený regex používám celkem běžně - s těmi hranatými závorkami,když potřebuji najít někde konkrétní text. A funguje to bezvadně :)
Spíše bych ho potřeboval umět rozšířit, jak jsi právě psal.
Tedy do něčeho v tomto stylu:
'div[class=minorCacheDetails Clear] div['.$select_region["region"].']div div' - samozřejmě tohle nechodí, protože nevím jak to zapsat.
nebo jak psal Keeehi:
'~<div class="minorCacheDetails Clear">
   <div>
       ['.$select_region["region"].']
    </div>
</div>~'
Což takto napsáno chodit taky nebude ...
juriad
Profil
Ok, ty závorky nevadí; nevěděl jsem.
php.net/manual/en/regexp.reference.delimiters.php

Zkusil jsem si to a toto funguje:
include 'simple_html_dom.php';

$html = file_get_html("http://coord.info/GC19905");
$sekce = (string) $html->find('div.minorCacheDetails div', -1);
echo $sekce; 

Já bych se v případě 700 regionů bál.
Schválně si to změř; vezmi si text, který neobsahuje žádný region v databáze a zkus na to spustit svůj skript. Je možné, že to bude dostatečně svižné, je možné, že ne.
Keeehi
Profil
Pro názornost, velmi jednoduchý regulár 3v4l.org/PGScl
Jelikož se mi nelíbí, že je na několik řádků, tak ho upravím 3v4l.org/JKU8e

A teď postup, jak takový regulár zkonstruovat.
1) Zkopírujš část HTML, která tě zajímá.
            <div class="minorCacheDetails Clear">
                
                
                <div>
                    In Moravskoslezsky kraj, Czech Republic
                </div>
            </div>

2) Část která tě zajímá nahradíš (.*?)
            <div class="minorCacheDetails Clear">
                
                
                <div>
                    (.*?)
                </div>
            </div>

3) Bílé znaky kterých se chceš zbavit nahradíš \s*
            <div class="minorCacheDetails Clear">
                
                
                <div>
                    (.*?)
                </div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>
                    (.*?)
                </div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>
                    (.*?)
                </div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>\s*(.*?)
                </div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>\s*(.*?)
                </div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>\s*(.*?)\s*</div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>\s*(.*?)\s*</div>
            </div>
            
-------------------- vvvv ------------------            
            
            <div class="minorCacheDetails Clear">\s*<div>\s*(.*?)\s*</div>\s*</div>
            
4) To co ti vzniklo je tvůj regulární výraz. Stačí ho jen vzít a použít. Pokud se nad tím člověk trochu zamyslí, dá se upravit a zkrátit. Nutné to není, jen to trochu pomůže výkonu. Optimálně by vypadal nějak takto:
'~minorCacheDetails Clear">\s*<div>\s*(.*?)\s*</~'
V tomto konkrétním případě musí script přečíst 20765 znaků. Hledaná část končí na pozici 20607. Přidaná náročnost je tedy 0.77% oproti lineárnímu čtení.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: