2012-06-11 26 views
7

Estoy escribiendo un script que toma una página web y detecta cuántas veces se usan cosas como un botón de Facebook. Como esto se haría mejor con un DOM, decidí usar DOMDocument de PHP.PHP DOMDocument Namespaces

El único problema que he encontrado, sin embargo, es para elementos como el facebook del botón como:

<fb:like send="true" width="450" show_faces="true"></fb:like> 

Desde este elemento técnicamente tiene un espacio de nombres de "FB", DOMDocument lanza una advertencia diciendo que este prefijo de espacio de nombres es no definida. A continuación, procede a quitar el prefijo, por lo que cuando llego a dicho elemento, su etiqueta ya no es fb: como, sino como.

¿Hay alguna manera de "preinscribir" un espacio de nombres? ¿Alguna sugerencia?

Respuesta

0

que estaba teniendo el mismo problema y se me ocurrió siguientes soluciones/soluciones:

No hay manera limpia para analizar HTML con espacios de nombres utilizando DOMDocument sin perder los espacios de nombres, pero hay algunas soluciones:

  • Use otro analizador que acepte espacios de nombres en código HMTL. Busque aquí una lista agradable y detallada de analizadores de HTML. Esta es probablemente la forma más eficiente de hacerlo.
  • Si quiere seguir con DOMDocument, básicamente tiene que preprocesar y posprocesar el código.

    • Antes de enviar el código para DomDocument-> loadHTML, el uso de expresiones regulares, bucles o lo que usted quiere encontrar todas las etiquetas de espacios de nombres y añadir un atributo personalizado a las etiquetas de apertura que contienen el espacio de nombres.

      <fb:like send="true" width="450" show_faces="true"></fb:like> 
      

      entonces dar lugar a

      <fb:like xmlNamespace="fb" send="true" width="450" show_faces="true"></fb:like> 
      
    • Ahora dar el código editado a DomDocument-> loadHTML. Se tira a los espacios de nombres pero mantendrá los atributos que resulta en

      <like xmlNamespace="fb" send="true" width="450" show_faces="true"></like> 
      
    • ahora (de nuevo usando expresiones regulares, bucles o como se quiera) encontrar todas las etiquetas con el atributo XMLNamespace y sustituir el atributo con el espacio de nombres reales. ¡No olvides agregar también el espacio de nombres a las etiquetas de cierre!

No creo OP sigue buscando una respuesta, sólo estoy publicar esto para cualquiera que encuentra este blog en su investigación.

+0

esto sonaba como un muy solución directa así que decidí correr con él. Aquí está el código que terminé con cualquiera que odie la expresión regular. '// almacena los elementos espaciados por nombre para que podamos volver a agregarlos más adelante' ' $ postContent = preg_replace ('/ <(\ w +): (\ w +) /', '<\ 1 namespace = "\ 2" ', $ postContent); ' ' // reconstruye las etiquetas con espacios entre nombres' '$ postContent = preg_replace ('/<(\ w +) namespace =" (\ w +) "/ ',' <\ 1 : \ 2 ', $ postContent); ' – lupos

0

¿Esto es lo que estás buscando?

Puede probar SimpleHTMLDOM. A continuación, puede ejecutar algo como ...

$html = new simple_html_dom(); 
$html->load_file('fileToParse.html'); 
$count=0; 
foreach($html->find('fb:like') as $element){ 
    $count+=1 
} 
echo $count; 

Eso debería funcionar.

Miré un poco más y encontré esto. Tomé esto desde el DOMDocument en PHP.net.

$dom = new DOMDocument; 
$dom->loadHTML('fileToParse.html'); // or $dom->loadXML('fileToParse.html'); 
$likes = $dom->getElementsByTagName('fb:like'); 
$count=0; 
foreach ($likes as $like) { 
    $count+=1; 
} 

Después de esto que yo estoy atascado

$file=file_get_contents("other.html"); 
$search = '/<fb:like[^>]*>/'; 
$count = preg_match_all($search , $file, $matches); 
echo $count; 
//Below is not needed 
print_r($matches); 

que no obstante está RegEx y es bastante lento. Intenté:

$dom = new DOMDocument; 
$xpath = new DOMXPath($dom); 
$dom->load("other.html"); 
$xpath = new DOMXPath($dom); 
$rootNamespace = $dom->lookupNamespaceUri($dom->namespaceURI); 
$xpath->registerNamespace('fb', $rootNamespace); 
$elementList = $xpath->query('//fb:like'); 

Pero obtuve el mismo error que usted.

+0

que estaba usando esto antes, pero quería usar una solución nativa en aras de la velocidad. Puede que tenga que volver a esto por defecto :( – Obto

+0

@Obto Lo uso en mis sitios pequeños, así que no tengo problemas con la velocidad. – Bonzo

+0

He actualizado esto para otra solución que debería ser más rápida. – Bonzo

4

Puede usar tidy para arreglar cosas antes de usar un analizador xml.

$tidy = new tidy(); 
$config = array(
    'output-xml' => true, 
    'input-xml' => true, 
    'add-xml-decl' => true, 
); 
$tidy->ParseString($htmlSoup, $config); 
$tidy->cleanRepair(); 
echo $tidy; 
0

no han sido capaces de encontrar una manera de hacerlo con DOM. Me sorprende que la expresión regular sea más lenta que DOMDocument ya que generalmente no es el caso para mí. strpos debe ser el más rápido, sin embargo:

strpos($dom, '<fb:like'); 

Esto sólo se busca la primera ocurrencia, pero se puede escribir una sencilla función recursiva que cambia la compensación apropiada.

1

Dado que esto nunca fue "resuelto", decidí seguir adelante e implementar la solución syndance para cualquier otra persona a la que no le guste descifrar expresiones regulares.

// do this before you use loadHTML()  
// store any name spaced elements so we can re-add them later 
$postContent = preg_replace('/<(\w+):(\w+)/', '<\1 data-namespace="\2"' , $postContent); 

// once you are done using domdocument fix things up 
// re-construct any name-spaced tags 
$postContent = preg_replace('/<(\w+) data-namespace="(\w+)"/', '<\1:\2 ' , $postContent); 
-1

trató la solución de expresiones regulares ... hay un problema con las etiquetas de cierre, ya que no aceptan los atributos!

<ns namespace="node">text</ns> 

(sobre todo, la expresión regular no buscaba etiquetas de cierre ...) por lo que finalmente lo hice un poco de materia feo como

$output = preg_replace('/<(\/?)(\w+):(\w+)/', '<\1\2thistaghasanamespace\3' , $output); 

y

$output = preg_replace('/<(\/?)(\w+)thistaghasanamespace(\w+)/', '<\1\2:\3' , $output);