Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [xml][DOM] sama podstawa
Forum PHP.pl > Forum > XML, AJAX > XML
marcinek37
Mam kod:
  1. <?
  2. $doc = new DOMDocument();
  3. $doc->loadHTML('<HTML><A HREF="ss">asd</A>');
  4. echo $doc;
  5. ?>


Dopóki nie dodam ostatniej linijki, błędu nie ma. Jeśli tylko dowiem się w czym leży problem, od razu będę mógł iść dalej z tematem.

phhpinfo() mówi:
DOM/XML enabled
DOM/XML API Version 20031129
libxml Version 2.6.26
HTML Support enabled
XPath Support enabled
XPointer Support enabled
Schema Support enabled
RelaxNG Support enabled
nospor
Cytat
Dopóki nie dodam ostatniej linijki, błędu nie ma.
Masz na mysli to:
echo $doc;
?

Mówisz że dostajesz błąd.... super... a może byś raczył się tym błędem z nami podzielić, czy może oczekujesz wróżenia z fusów?
marcinek37
kompletnie wypadło mi z głowy, taki jest błąd:

Catchable fatal error: Object of class DOMDocument could not be converted to string in D:\Programy\WebServ\httpd-users\dom\index.php on line 4
marcinek37
czy macie może linka do jakiegoś poradnika, ale w języku polskim?
może być też temat na tym forum, gdzie ktoś to jakoś objaśnił
zegarek84
niestety większość materiałów jest po angielsku... jeśli cokolwiek kumasz to jak najwięcej czytaj i próbuj pisać kod trochę metodą prób i błędów - prędzej i zapamiętasz i zrozumiesz... jednak musisz nauczyć się z grubsza rozumieć bądź po swojemu debugować kod...

jeśli chodzi o DOM to świetne narzędzie - nie wszędzie wygodnie jest korzystać z wyrażeń regularnych a i to nie wszyscy je rozumią...
jeśli dokument chcesz odpytywać przez selektory CSS jak w jQuery to w google znajdziesz odpowiednie biblioteki jak PHPQuery itd... ale bez tych bibliotek możesz też skorzystać ze ścieżek XPath (łatwo jest sobie przekształcić selector CSS na wyrażenie XPath) DOMXPath::query
marcinek37
zrobiłem postępy w temacie, ale zatrzymałem się na tym:
  1. <?
  2. $dom = new DOMDocument('1.0', 'utf-8');
  3. $dom->preserveWhiteSpace = true;
  4. $dom->loadXML('<categories>
  5. <category>
  6. <id>1</id>
  7. <name>AAA</name>
  8. </category>
  9. <category>
  10. <id>2</id>
  11. <name>BBB</name>
  12. </category>
  13. </categories>');
  14.  
  15. foreach($dom->getElementsByTagName('category') AS $category){
  16. echo'id: '.$category->getElementsByTagName('id')->nodeValue;
  17. echo'name: '.$category->getElementsByTagName('name')->nodeValue;
  18. }
  19. ?>


Chcę zrobić pętle, aby wybrane pola w konkretnym <category> pokazały się w odpowiedni sposób:

id: 1
name: AAA

id: 2
name: BBB

Wszystko jest ok, ale wartości się nie pokazują
nospor
Cytat
Wszystko jest ok, ale wartości się nie pokazują
No to skoro się nie pokazują to chyba jednak nie jest ok....

Włączy wyświetlanie wszytkich błędów, a dowiesz się czemu się nie pokazują.
Temat: Jak poprawnie zada pytanie

getElementsByTagName zwraca listę elementów a trudno oczekiwać by lista miała parametr nodeValue. To pojedynczy element ma taki parametr a nie lista
marcinek37
Po dodaniu tego kodu pokazują się takie błędy:
Notice: Undefined property: DOMNodeList::$nodeValue in D:\Programy\WebServ\httpd-users\dom\index.php on line 20


Ale jeśli dam ten kod:
  1. <?
  2. ini_set('display_errors','1');
  3.  
  4. $dom = new DOMDocument('1.0', 'utf-8');
  5. $dom->preserveWhiteSpace = true;
  6. $dom->loadXML('<categories>
  7. <category>
  8. <id>1</id>
  9. <name>AAA</name>
  10. </category>
  11. <category>
  12. <id>2</id>
  13. <name>BBB</name>
  14. </category>
  15. </categories>');
  16.  
  17. foreach($dom->getElementsByTagName('category') AS $category){
  18. echo $category->nodeValue;
  19. }


to pokazują się wartości, ale wszystkie na raz i nie wiem, jak z tego $category->nodeValue; wziąć konkretne parametry, najpierw id a potem name
szukałem na tym forum i w internecie i wszędzie wyszukuje się tylko jedno pole, a ja szukam jednego pola, pod którym są kolejne dwa - i w tym problem
zegarek84
  1. <?php
  2. ini_set('display_errors','1');
  3.  
  4. $dom = new DOMDocument('1.0', 'utf-8');
  5. $dom->preserveWhiteSpace = true;
  6. $dom->loadXML('<categories>
  7. <category>
  8. <id>1</id>
  9. <name>AAA</name>
  10. </category>
  11. <category>
  12. <id>2</id>
  13. <name>BBB</name>
  14. </category>
  15. </categories>');
  16.  
  17. $xpath = new DOMXPath($dom);
  18.  
  19. foreach($xpath->query('//category') AS $category){
  20. // opcjonalnie zamiast '//category'
  21. // '//category[id and name]' co znaczy '//category[child::id and child::name]'
  22. // czyli mające te dzieci
  23. $itemList = $xpath->query('./id|./name',$category);
  24. echo 'id: ',$itemList->item(0)->nodeValue, '<br />';
  25. echo 'name: ',$itemList->item(1)->nodeValue, '<br />';
  26. }


u Ciebie brakowało pobranie elementów z listy czyli ->item(0)... foreach sam przeiterował tą listę... poczytaj o podobieństwach XPath do selektorów CSS to na szybki start da Ci wiele możliwości, czytelniejszy i krótszy zapis... ale jak wolisz...

przykład dałem z XPath a swój popraw i staraj się czytać błędy ze zrozumieniem, gdyż DOMNodeList nie ma własności nodeValue ;p

PS. przy bardzo duuuużych plikach XML i tak będziesz musiał skorzystać z SAX ;] - to dopiero dla Ciebie pewnie będzie hardkor ;]
marcinek37
świetnie, działa!

a powiedz mi, "duże" pliki, tzn. jakie? 10MB?
zegarek84
to zależy od ustawień serwera... ale każdy plik wczytywany w całości zawsze w pamięci zajmuje co najmniej tyle, co sam plik, a do tego parsowany w całości obiektami typu DOM znacznie więcej, gdyż na każdy z elementów jest tworzony nowy obiekt...

póki nie będziesz musiał zbyt często otwierać duuużych plików XML to wystarczy zwiększyć pamięć wykonywanego skryptu... ale jeśli pliku już będą naprawdę duże lub mniejsze a duże pliki będziesz musiał otwierać baaaaaaardzo często to już coś z tym będziesz musiał zrobić... pierwsza rzecz, jaka przychodzi większości to czytanie pliku po fragmencie i korzystanie z wyrażeń regularnych (nie polecam - choć polecam znać wyr. reg. ;]), w zasadzie jeśli pliku nie będzie można rozbić i będzie on na prawdę duży to przy niskim zużyciu pamięci można go odczytać poprzez SAX (w google hasło "php SAX"), jednak na razie tego tematu nie ruszaj puki nie potrzebujesz a tym bardziej, jeśli nic nie programowałeś zdarzeniowo...
marcinek37
ok, a masz może jakiegoś linka do jakiegoś poradnika, który krok po kroku wyjaśni DOM?
ale coś innego niż to, co daje manual
zegarek84
popisz trochę w JavaScript, korzystaj z narzędzi typu firebug i narzędzi deweloperskich np. w google chrome i przeglądaj drzewo DOM, tam już z musu musisz wszystkie elementy html traktować jako osobny obiekt, a przeglądając drzewo DOM w narzędziach deweloperskich widzisz, jak dokument został wygenerowany itd... DOM to ogólny model obiektowy dokumentu i jest uniwersalny nie ważne, w jakim języku programowania... bardzo modne i wygodne stało się wyszukiwanie elementów DOM po selektorach CSS - stąd m.in. pierwotna popularność jQuery... choć w czystym JS są już metody typu .querySelektorAll...

jednak jeśli po stronie PHP nie chcesz korzystać z musu z zewnętrznych bibliotek wyszukujących po selektorach CSS to musisz poczytać o podobieństwach XPath do selektorów CSS i o konwersji selektorów CSS do XPath, ale jak załapiesz podstawy to zauważysz duże podobieństwo... dodatkowo XPath oferuje Ci więcej niż wyszukiwanie po selektorach CSS operując na dokumencie XML czy ściągniętej parsowanej stronie np. przez CURL... niestety większość materiałów jest po angielsku i tak z kopyta nie przytoczę Ci linków, jedynie daję wskazówki jak spróbować to łyknąć... dodatkowo w manualu PHP jeśli nie bardzo rozumiesz jak z danej funkcji korzystać lub co może być nie tak to warto przejrzeć też komentarze pod daną funkcją...

samo pisanie styli CSS też daje spojrzenie jakie elementy zostaną ostylowane, tj. prawie jakbyś szukał te elementy po selektorach CSS... więc możesz trochę pobawić się z "grafiką", ale przecież tym nie musisz się zajmować, jednak da Ci szersze spojrzenie... poczytaj o selektorach CSS3 gdyż jak załapiesz XPath to szybko je też dasz radę przekonwertować - ale to do bardziej zaawansowanego wyszukiwania...

pisząc kod HTML staraj się robić odpowiednie wcięcia, domknięcia na tym samym poziomie zagłębienia - będziesz wtedy lepiej w czystym kodzie HTML widział "obiekty"

jeśli chodzi o parsowanie stron to wygodny też jest silnik WebKit ale w C++ w Qt - jednak jest to inny język programowania...
marcinek37
1. mam już troszkę rozszerzony kod:
  1. <?
  2. /* funkcja z manuala */
  3. function DOMinnerHTML($element)
  4. {
  5. $innerHTML = "";
  6. $children = $element->childNodes;
  7. foreach ($children as $child)
  8. {
  9. $tmp_dom = new DOMDocument();
  10. $tmp_dom->appendChild($tmp_dom->importNode($child, true));
  11. $innerHTML.=trim($tmp_dom->saveHTML());
  12. }
  13. return $innerHTML;
  14. }
  15.  
  16.  
  17. /* mój kod */
  18. $xml = '<categories>
  19.  
  20. <category>
  21. <id>1</id>
  22. <name>AAA</name>
  23. <photos>
  24. <photo1>nazwa.jpg</photo1>
  25. </photos>
  26. </category>
  27.  
  28. <category>
  29. <id>2</id>
  30. <name>BBB</name>
  31. <photos></photos>
  32. <text>
  33. <b>opis</b>
  34. </text>
  35. </category>
  36.  
  37. </categories>';
  38.  
  39.  
  40. $dom = new DOMDocument('1.0', 'utf-8');
  41. $dom->loadXML($xml);
  42. foreach($dom->getElementsByTagName('category') AS $category){
  43. $id = $category->getElementsByTagName('id')->item(0)->nodeValue;
  44. $name = $category->getElementsByTagName('name')->item(0)->nodeValue;
  45. $photo1 = $category->getElementsByTagName('photo1')->item(0)->nodeValue;
  46. $text = $category->getElementsByTagName('text')->item(0)->nodeValue;
  47.  
  48. echo'id: '.$id.'<br />';
  49. echo'name: '.$name.'<br />';
  50. echo'photo1: '.$photo1.'<br />';
  51. echo'text: '.$text.'<br />';
  52. }?>


2. $photo1 ściąga dane bezpośrednio z elementu "photo1", w ogóle omija fakt, że jest jeszcze wcześniej w elemencie "photos" - to chyba nieeleganckie, próbowałem wielu metod, aby w kodzie to zapisać, że element "photo1" jest w elemencie "photos", jednak zawsze wyrzucało błędy albo nie pokazywało niczego - jak mogę to ładnie zapisać?

3. element "text" ma kod HTML, który jest od razu kasowany, wyczytałem, że jest potrzebna funkcja DOMinnerHTML jednak przykład z manuala był o wiele prostszy niż ten, dlatego nie potrafię go zastosować - chcę, aby HTML został jedynie w elemencie "text"; tutaj również próbowałem na kilka sposóbów i żaden nie dał efektu... domyślam się, że moje oba problemy to pikuś, ale nie wiem, jak się za to zabrać :/
zegarek84
przede wszystkim zawsze wyświetlaj sobie błędy:
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', '1');

po drugie nie zawsze miałeś elementy których szukałeś
  1. <?php
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. function DOMinnerHTML($element) {
  6. $children = $element->childNodes;
  7. $tmp_dom = new DOMDocument();
  8. foreach ($children as $child) {
  9. $tmp_dom->appendChild($tmp_dom->importNode($child, true));
  10. }
  11. return trim($tmp_dom->saveHTML());
  12. }
  13.  
  14. $xml = '<categories>
  15.  
  16. <category>
  17. <id>1</id>
  18. <name>AAA</name>
  19. <photos>
  20. <photo1>nazwa.jpg</photo1>
  21. </photos>
  22. </category>
  23.  
  24. <category>
  25. <id>2</id>
  26. <name>BBB</name>
  27. <photos></photos>
  28. <text>
  29. <b>opis</b>
  30. </text>
  31. </category>
  32.  
  33. </categories>';
  34.  
  35. $dom = new DOMDocument('1.0', 'utf-8');
  36. $dom->loadXML($xml);
  37. foreach ($dom->getElementsByTagName('category') AS $category) {
  38. $oId = $category->getElementsByTagName('id')->item(0);
  39. $oName = $category->getElementsByTagName('name')->item(0);
  40. $oPhoto1 = $category->getElementsByTagName('photo1')->item(0);
  41. $oText = $category->getElementsByTagName('text')->item(0);
  42. // ->item() zwraca Element lub NULL
  43. $id = $oId ? $oId->nodeValue : '';
  44. $name = $oName ? $oName->nodeValue : '';
  45. $photo1 = $oPhoto1 ? $oPhoto1->nodeValue : '';
  46. $text = $oText ? DOMinnerHTML($oText) : '';
  47.  
  48. echo 'id: ', $id, '<br />';
  49. echo 'name: ', $name, '<br />';
  50. echo 'photo1: ', $photo1, '<br />';
  51. echo 'text: ', $text, '<br />';
  52. }
marcinek37
bardzo Ci dziękuję!

1. specjalnie w drugim przypadku nie dałem wszystkich pól, aby sprawdzić, jak się zachowa skrypt
2. a co z nieeleganckim zapisem?
od razu szuka photo1 $oPhoto1 = $category->getElementsByTagName('photo1')->item(0); omijając fakt, że jest w "photos"
3. mógłbyć zapisać mi ten kod w najproszej wersji $id = $oId ? $oId->nodeValue : '';? normalnie w instrukcji warunkowej:
  1. <?
  2. if($oId == questionmark.gif){ $id = $oId; }
  3. else{ $id = $oId->nodeValue : ''; }
  4. ?>

4. i dlaczego jest ten dwukropek na końcu? $oId->nodeValue : '';
5. naprawdę nie przypominasz sobie jakiegoś prostego poradnika, choćby w języku angielskim? albo jakąś książkę? nie chcę tutaj ciągle pytać o takie bzdety...
zegarek84
Cytat(marcinek37 @ 11.04.2013, 12:11:22 ) *
2. a co z nieeleganckim zapisem?
od razu szuka photo1 $oPhoto1 = $category->getElementsByTagName('photo1')->item(0); omijając fakt, że jest w "photos"
3. mógłbyć zapisać mi ten kod w najproszej wersji $id = $oId ? $oId->nodeValue : '';? normalnie w instrukcji warunkowej:
...

przeanalizuj poniższy kod
Cytat(marcinek37 @ 11.04.2013, 12:11:22 ) *
4. i dlaczego jest ten dwukropek na końcu? $oId->nodeValue : '';

brakuje Ci podstaw programowania w dowolnym języku, jest to skrócony zapis przypisania wartości odpowiadający instrukcji if(){$zmienna = ...}else{$zmienna = }
Cytat(marcinek37 @ 11.04.2013, 12:11:22 ) *
5. naprawdę nie przypominasz sobie jakiegoś prostego poradnika, choćby w języku angielskim? albo jakąś książkę? nie chcę tutaj ciągle pytać o takie bzdety...

How to map CSS selectors to XPath queries
XPath and CSS Selectors
Korzystając z wyrażeń xpath masz większe możliwości i kontrolę wyszukiwanych elementów
  1. <?php
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. function DOMinnerHTML($element) {
  6. $children = $element->childNodes;
  7. $tmp_dom = new DOMDocument();
  8. foreach ($children as $child) {
  9. $tmp_dom->appendChild($tmp_dom->importNode($child, true));
  10. }
  11. return trim($tmp_dom->saveHTML());
  12. }
  13.  
  14.  
  15. $xml = '<categories>
  16.  
  17. <category>
  18. <id>1</id>
  19. <name>AAA</name>
  20. <photos>
  21. <photo1>nazwa.jpg</photo1>
  22. </photos>
  23. </category>
  24.  
  25. <category>
  26. <id>2</id>
  27. <name>BBB</name>
  28. <photos></photos>
  29. <text>
  30. <b>opis</b>
  31. </text>
  32. </category>
  33.  
  34. </categories>';
  35.  
  36. $dom = new DOMDocument('1.0', 'utf-8');
  37. $dom->loadXML($xml);
  38. $xpath = new DOMXPath($dom);
  39. foreach ($xpath->query('//category') AS $category) {
  40. $oId = $xpath->query('./id', $category)->item(0);
  41. $oName = $xpath->query('./name', $category)->item(0);
  42. $oPhoto1 = $xpath->query('./photos/photo1', $category)->item(0);
  43. $oText = $xpath->query('./text', $category)->item(0);
  44. // ->item() zwraca Element lub NULL
  45. $id = $name = $photo1 = $text = '';
  46. if($oId)
  47. $id = $oId->nodeValue;
  48. if($oName)
  49. $name = $oName->nodeValue;
  50. if($oPhoto1)
  51. $photo1 = $oPhoto1->nodeValue;
  52. if($oText)
  53. $text = DOMinnerHTML($oText);
  54.  
  55. echo 'id: ', $id, '<br />';
  56. echo 'name: ', $name, '<br />';
  57. echo 'photo1: ', $photo1, '<br />';
  58. echo 'text: ', $text, '<br />';
  59. }
marcinek37
czyli muszę korzystać z DOMDocument oraz DOMXPath, aby elegancko przerobić konkretne dane - bardzo dziękuję

co do tych totalnych podstaw... to fakt, mam duże braki, dlatego czytam książkę z heliona, ale nie ma tam rozdziału o spodobach zapisu kodu, jeśli i w tym temacie masz coś ciekawego do poczytania, zrobię to bardzo chętnie
zegarek84
z PHP tak na szybko to przeczytaj sobie np. to:
http://framework.zend.com/manual/1.12/en/c...g-standard.html
nie patrz, że to ze strony ZF, na stronie SYMFONY gdzieś była chyba podobna strona...

zresztą jak nie wybrałeś jakiegoś FW to i tak kiedyś będziesz musiał ;]... a stosując się do dobrych praktyk nawet jak nie będziesz mógł skorzystać z gotowego FW to bez problemu napiszesz szkielet podstawowy mini FW...

jest wiele konwencji zapisu zmiennych, sposobów formatowania kodu... ogólnie musisz próbować do jakiejś się dostosować ale bez przesady...

np. zmienne prywatne w prototypach obiektów w JavaScript w zasadzie da się zdefiniować tylko przez konwencję stosując znak podkreślenia na początku _zmienna... choć tą konwencję niektórzy wykorzystują też przy innych językach zważywszy na podpowiadanie kodu przez IDE... nazwy klas powinno zaczynać się dużymi literami, a instancje tych klas z małych już... przy wielu podstawowych zmiennych często pierwsza litera zmiennej może określać typ jak np. $iLat - zmienna typu integer - stąd np. w powyższym kodzie niektóre zmienne poprzedziłem "o" (od obiekt). Jeśli nazwa zmiennej jest wieloczłonowa to należy zdecydować się na jeden ze sposobów zapisu np. moja_dluga_zmienna lub mojaDlugaZmienna

szczerze powiedziawszy to musisz kodować - większe projekty wymusza na tobie stosowanie dobrych praktyk, odpowiedniego nazywania zmiennych, wcięć, odpowiedniego ułożenia i nazywania plików

do PHP na helionie wielu poleci Ci książkę typu "PHP obiekty wzorce i narzędzia" "PHP object, pattern and practice" 3 edycja (jak możesz to czytaj po ang.), ale nie pamiętam, czy jest tam o formatowaniu kodu, jednak ja też tą pozycję polecam...

czytając nowsze pozycje raczej autorzy stosują dobre praktyki...

samych książek z formatowaniem kodu to za bardzo nie pamiętam czy są dobre czy nie ale nasuwają mi się 2 pozycje:
"Clean Code"
"Code Craft - the practice of writing excellent code"

marcinek37
troszkę ciekawych informacji z tego linka wyciągnąłem, bardzo Ci dziękuję - ponownie wink.gif

powstał u mnie kolejny problem:
  1. <?
  2. $dom = new DOMDocument('1.0', 'utf-8');
  3. $dom->formatOutput = true;
  4. $products_list = $dom->appendChild($dom->createElement('products_list'));
  5. $product = $products_list->appendChild($dom->createElement('product'));
  6. $product->appendChild($dom->createElement('id', 1));
  7. $product->appendChild($dom->createElement('name', '<b>AAA</b>'));
  8. $dom->save('nazwa.xml');
  9. ?>


jak widzisz, tworzę już swoje pliki xml (progress jest), jednak element <name> ma zamieniane znaczniki HTML w np. &lt;b&gt;.

całość wygenerowanego pliku wygląda tak:
<?xml version="1.0" encoding="utf-8"?>
<products_list>
<product>
<id>1</id>
<name>&lt;b&gt;AAA&lt;/b&gt;</name>
</product>
</products_list>

a powinna tak:
<?xml version="1.0" encoding="utf-8"?>
<products_list>
<product>
<id>1</id>
<name><b>AAA</b></name>
</product>
</products_list>
zegarek84
Cytat(marcinek37 @ 12.04.2013, 00:01:47 ) *
jak widzisz, tworzę już swoje pliki xml (progress jest), jednak element <name> ma zamieniane znaczniki HTML w np. <b>

nadeszła chwila relaksu, a że nie jestem programistą z zawodu to nie znam funkcji na pamięć więc pewnie wkradną się błędy lub możesz nie bardzo zrozumieć to co piszę skoro jestem pod wpływem ;p

no właśnie to poprawne działanie - w JavaScript podobny fragment kody wykorzystuje się do implementacji funkcji typu escapeHTML encodeHTML gdzie na odpowiednie znaczniki zamienia się znaki typu <> itd... by np. wyświetlić potem kod HTML... pod tym kontem spojrzyj, to nie będziesz widział błędu ;]

rozwiązanie na szybko jak jestem pod wpływem - luknij która funkcja zamienia tak na encje w manualu php i są jej odpowiedniki do dekodowania - więc w XML'u możesz przechowywać strukturę zakodowaną - co w sumie było by poprawne, gdyż nie przewidywałeś tam dodatkowych elementów drzewa XML (nie patrz na kod HTML ;p) - ps. jeśli jest tam możliwość niepoprawnej struktury XML to niech lepiej to zostanie zakodowane by nie rozwaliło Ci pliku XML (analogia do HTML iniection)...

drugie rozwiązanie na szybko jak jestem pod wpływem przypomina tą funkcję którą robiłeś z innerHTML - po prostu pokombinuj, jak zaimportować już Elementy a nie tekst ;] - jak sam poprubujesz to nawet jak nie dasz rady prędzej to zapamiętasz... daj kod co tam zrobiłeś, jak będę miał czas to może poprawię ;] - ale nie nastawiaj się, że będę miał czas gdyż mam swoje zajęcia jutro (już dziś ;p) w sobotę też a w niedzielę rodzinkę pasuje odwiedzić ;p

pozdro...
marcinek37
1. wydaje się, że to ta metoda: createElement przerabia kod HTML na encje
a nie ma ona żadnych dodatkowych parametrów... zrobiłem to łopatologicznie - wygenerowałem plik, a potem go przerobiłem innym skryptem - innego rozwiązania chyba nie znajdziemy

2. postanowiłem wgłębić się w DOM, dla przykładu chcę wykonać listę linków znajdujących się na onecie
  1. <?
  2. $objDOM = new DOMDocument();
  3. $objDOM->load("http://www.onet.pl");
  4. ?>


no i pokazała się ogromna lista błędów - czyja to wina? skryptu czy onetu?
zegarek84
HTML nie zawsze jest poprawnym dokumentem XML ;] - więc ze stronami niemal zawsze będzie podobnie... mając tego świadomość możesz wyłączyć wyświetlanie błędów przy tej funkcji
@$objDOM->load("http://www.onet.pl");

kod strony zapisanej z obiektu DOM może trochę odbiegać od źródeł... gdy będziesz więcej programował i poznawał inne języki to polecam korzystanie z silnika przeglądarki np. WebKit w Fremworku Qt (ale to już język C++)...

na razie nie przejmuj się tymi błędami... jednak możesz mieć problemy z kodowaniem niektórych stron - czytaj dokumentację i komentarze jak sobie z tym poradzić ;] - dasz radę...

wszystkie linki w wyr. xpath nie ważne gdzie się znajdują to po prostu "//a", z kolei wewnątrz jakiegoś obiektu o określonym id nie bezpośrednie dzieci to "//*[@id="cos_tam"]//a" - zwracaj uwagę kiedy np. stosować "//" a kiedy "/", ale to już chyba doczytałeś... i podobnie jak przykłady wyżej jeśli szukasz xpath względem jakiegoś elementu to zaczynaj wyrażenie od "./"

ps. względem wyr. CSS "//" odpowiada spacji " " a "/" odpowiada ">"

Cytat(marcinek37 @ 12.04.2013, 02:04:14 ) *
1. wydaje się, że to ta metoda: createElement przerabia kod HTML na encje
a nie ma ona żadnych dodatkowych parametrów... zrobiłem to łopatologicznie - wygenerowałem plik, a potem go przerobiłem innym skryptem - innego rozwiązania chyba nie znajdziemy

  1. <?php
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. $oDom = new DOMDocument('1.0', 'utf-8');
  6. $oDom->formatOutput = true;
  7. $oProductList = $oDom->appendChild($oDom->createElement('products_list'));
  8. $oProduct = $oProductList->appendChild($oDom->createElement('product'));
  9. $oProduct->appendChild($oDom->createElement('id', 1));
  10. $oName = $oProduct->appendChild($oDom->createElement('name')); /* @var $oName DOMNode */
  11. $sName = '<b>AAA';
  12. $oDomTmp = new DOMDocument('1.0', 'utf-8');
  13. // na wszelki wypadek doklejamy body żeby nie tworzyło paragrafów w czystym tekście,
  14. // jeśli wystąpi 2 razy body to zostanie pominięte
  15. @$oDomTmp->loadHTML('<body>'.$sName); // wyciszenie błędów przy niepoprawnych html'ach
  16. $oBody = $oDomTmp->getElementsByTagName('body')->item(0); /* @var $oBody DOMNode */
  17. foreach ($oBody->childNodes as $oEl) { /* @var $oEl DOMNode */
  18. $oName->appendChild($oDom->importNode($oEl, TRUE));
  19. }
  20. echo $oDom->saveXML();

Cytat(marcinek37 @ 12.04.2013, 02:04:14 ) *
2. postanowiłem wgłębić się w DOM, dla przykładu chcę wykonać listę linków znajdujących się na onecie
  1. <?
  2. $objDOM = new DOMDocument();
  3. $objDOM->load("http://www.onet.pl");
  4. ?>

no i pokazała się ogromna lista błędów - czyja to wina? skryptu czy onetu?

  1. <?php
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. $oDOM = new DOMDocument();
  6. @$oDOM->loadHTMLFile("http://www.onet.pl");
  7. $oXPath = new DOMXPath($oDOM);
  8. echo $oXPath->query('//a')->length;
marcinek37
bardzo dziękuję za informacje

szkoda tylko, że trzeba używaż dwóch modeli: DOM i xpath, ale to chyba da się ogarnąć wink.gif
zegarek84
poprawiłem powyższy kod - xpath nie musisz używać ale problem był, jeśli miałeś np $sName = 'cokolwiek' jako zwykły string np. z bazy - dodawało paragraf to to poprawiłem i będziesz miał poprawne elementy nawet przy nie poprawnym HTML'u - luknij teraz...
marcinek37
na razie nie chcę korzystać z DOMXPath, a skupić się wyłącznie na DOMDocument
martwi mnie, że ten kod nie pokazuje niczego:
  1. <?
  2. $objDOM = new DOMDocument();
  3. $objDOM->load("http://www.onet.pl");
  4. echo $objDOM->SaveHTML();
  5. ?>


to przez te błędy, czy po prostu bez DOMXPath nic nie zrobię?
zegarek84
przecież napisałem Ci wyżej, iż .load służy do wczytywania plików XML - strona nie ma poprawnego formatu xml miałeś skorzystać z funkcji .loadHTMLFile
  1. <?php
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. $objDOM = new DOMDocument();
  6. @$objDOM->loadHTMLFile("http://www.onet.pl");
  7. echo $objDOM->saveHTML();
marcinek37
przepraszam, źle odczytałem Twoje wskazówki, jest jeszcze jeden mały kłopot, a mianowicie brak polskich znaków:
  1. <?
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. $objDOM = new DOMDocument('1.0', 'utf-8');
  6. @$objDOM->loadHTMLFile("http://www.onet.pl");
  7. echo mb_convert_encoding($objDOM->SaveHTML(), 'UTF-8', 'HTML-ENTITIES');
  8. ?>


to chyba ostatni problem, bo całą resztę zaczynam ogarniać, w dużej mierze dzięki Tobie wink.gif
zegarek84
nie jestem z zawodu programistą ale dawniej do parsowania gry www właśnie korzystałem z PHP i DOM ^^ - niestety z kodowaniem trzeba wziąć sprawy w swoje ręce... oprócz kodu przeczytaj komentarze w tej funkcji:
DOMDocument::loadHTML
więc plik musiałby albo zawierać na początku dyrektywę kodowania albo trzeba przekonwertować źródło i znaki UTF-8 do encji html... preferuję to drugie podejście...

kodowanie na stronie możesz rozpoznać dwojako, albo z nagłówków odpowiedzi jeśli byś korzystał z CURL'a albo z tagu meta określającego kodowanie... no ale żeby go odczytać to musisz w pierw wczytać dokument jakim jest (chyba, że wolisz bawić się z wyrażeniami regularnymi ale nie polecam) - i to podejście Ci na początku polecam... inne rozwiązanie by zmniejszyć pamięć (choć w sumie nie musisz zwłaszcza w celach ćwiczebnych) to ten tag meta odszukać przez SAX i przerwać przeglądanie dokumentu po znalezieniu kodowania - ale na tym etapie wiedzy jak Ci wspominałem wcześniej nawet tego nie dotykaj ;p

a teraz jak znasz kodowanie strony to możesz zrobić np. w ten sposób:
  1. <?php
  2. error_reporting(E_ALL|E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. $objDOM = new DOMDocument('1.0', 'UTF-8');
  6. $sHTML = mb_convert_encoding(file_get_contents("http://www.onet.pl"),'HTML-ENTITIES', 'UTF-8');
  7. @$objDOM->loadHTML($sHTML);
  8. // echo $objDOM->saveHTML();
  9. echo mb_convert_encoding($objDOM->saveHTML(),'UTF-8' ,'HTML-ENTITIES');

w ostatniej linijce nie musisz konwertować z powrotem, ale podejrzewam, iż nie chcesz encji ;]

a jeśli nie znasz kodowania to na Twój aktualny poziom wiedzy można by np. w ten sposób:
  1. <?php
  2. error_reporting(E_ALL | E_STRICT);
  3. ini_set('display_errors', '1');
  4.  
  5. $objDOM = new DOMDocument('1.0', 'UTF-8');
  6. $sHTML = file_get_contents("http://www.onet.pl");
  7. //$sHTML = file_get_contents("http://localhost/modul_1/public/urzytkownik/zarejestrowani/");
  8. @$objDOM->loadHTML($sHTML);
  9. $oXPath = new DOMXPath($objDOM);
  10. $oMeta = $oXPath->query('//meta[@charset]|//meta[@http-equiv and @content]')->item(0);
  11. $oXPath = NULL;
  12. unset($oXPath);
  13. $sEcode = 'UTF-8';
  14. if ($oMeta) {
  15. if ($oMeta->hasAttribute('charset')) {
  16. $sEcode = $oMeta->getAttribute('charset');
  17. }
  18. if ($oMeta->hasAttribute('content')) {
  19. $aTemp = explode('=', $sEcode = $oMeta->getAttribute('content'), 2);
  20. $sEcode = trim($aTemp[1]);
  21. }
  22. }
  23. $sHTML = mb_convert_encoding($sHTML, 'HTML-ENTITIES', $sEcode);
  24. @$objDOM->loadHTML($sHTML);
  25. // przy następnej linijce uważaj gdyż może Ci rozwalić plik html...
  26. echo mb_convert_encoding($objDOM->saveHTML(), 'UTF-8', 'HTML-ENTITIES');


ps.
przypomniałem sobie i może Cię to zainteresować, a raczej powinieneś się zainteresować jeśli zamierzasz parsować strony
luknij na tą rozwijaną bibliotekę:
https://github.com/html5lib/html5lib-php
info o niej pośrednio ze strony:
http://ejohn.org/blog/pure-javascript-html-parser/
marcinek37
przepraszam, że odpisuję Ci dopiero teraz, ale cały weekend byłem poza komputerem...
bardzo, ale to bardzo mi pomogłeś, wszystko jak na razie rozumiem, dziękuję i pozdrawiam wink.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.