title: XPath in PHP: Get all text nodes, except navigation
body: <p>I’m writing a custom parser/data extractor for some pretty shitty HTML. </p>
  
  <p>Changing the HTML is out of the question.</p>
  
  <p>I will spare you the details of the hoops I’ve had to jump through but I’ve now come pretty close to my original goal. I’m using a combination of DOMDocument getElementByName, regular expression replace (I know, I know...), and XPath queries.</p>
  
  <p>I need to get all the text out of the body of the document. I would like for the navigation to remain a separate entity, at least in the abstract. Here’s what I’m doing now:</p>
  
  <pre><code>$contentnodes = $xpath-&gt;query("//body//*[not(self::a)]/text()|//body//ul/li/a");
  
  foreach ($contentnodes as $contentnode) {    
      $type      = $contentnode-&gt;nodeName;
      $content   = $contentnode-&gt;nodeValue;
  
      $output[] = array( $type, $content);
  }
  </code></pre>
  
  <p>This works, except that of course it treats all of the links on the page differently, and I only want it to do that to the navigation.</p>
  
  <p>What XPath syntax can I use so that, in the first part of that query, before the <code>|</code>, I tell it to get all the text nodes of <code>body</code>’s children <strong>except</strong> <code>ul &gt; li &gt; a</code>.</p>
  
  <p>Please note that I cannot rely on the presence of <code>p</code> tags or <code>h1</code> tags or anything sensible like that to make educated guesses about content.</p>
  
  <p>Thanks</p>
  
  <p><strong>Update</strong>: @hr_117’s answer below works. I’ve also found that you can use multiple <code>not</code> statements like so:</p>
  
  <p><code>//body//text()[not(parent::a/parent::li/parent::ul)][not(parent::h1)]</code></p>