XPath查询& HTML - 在Anchor标签内查找特定的HREF

时间:2012-03-27 17:57:46

标签: php xpath

我已获得DOMDocumentDOMXPath所需的HTML数据。

但我需要访问并检索某些href代码中的<a>值。以下是标准:

  1. href包含:some-site.vendor.com/jobs/[#idnumber]/job(即some-site.vendor.com/jobs/23094/job

  2. href不包含:some-site.vendor.com/jobs/search?search=pr2

  3. href不包含:some-site.vendor.com/jobs/intro

  4. href不包含:www.someothersite.com/

  5. href不包含:media.someothersite.com/

  6. href不包含:javascript:void(0)

  7. 这些(类似的)查询中的任何一个都会获取除4-6之外的所有内容 - 这是一件好事:

    $joblinks = $xpath->query('//a[@href[contains(., "https://some-site.vendor.com/jobs/")]]');    
    $joblinks = $xpath->query('//a[@href[contains(., "job")]]');
    

    但最终我需要访问包含href的所有锚标签,如#1,并将实际的href值分配给变量/数组。这就是我正在做的事情:

    $payload = fetchRemoteData(SPEC_SOURCE_URL);
    
    // suppress warning(s) due to malformed markup
    libxml_use_internal_errors(true);
    
    // load the fetched contents
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    $dom->loadHTML($payload);
    
    // parse and cache the required data elements
    $xpath = new DOMXPath($dom);
    
    //$joblinks = $xpath->query('//a[@href[contains(., "some-site.vendor.com/jobs/")]]');
    $joblinks = $xpath->query('//a[@href[contains(., "job")]]');
    foreach($joblinks as $joblink) {
        var_dump(trim($joblink->nodeValue)); // dump hrefs here!
    }
    echo "\n";
    

    这真的打败了我 - 我很接近,但我似乎无法正确调整查询和/或访问实际的href值。如果我没有遵循任何类型的协议来解决这个问题,我会表示最卑鄙的道歉......

    任何/所有帮助将不胜感激! Thanx SO MUCH提前!

1 个答案:

答案 0 :(得分:1)

单独使用xpath执行此操作我不建议。首先,你有一个白名单和一个黑名单。它不是很清楚你想要什么,所以我认为这会随着时间的推移而改变。

所以你可以做的是首先选择有问题的所有href属性并返回节点。这就是Xpath非常适合的,所以让我们使用xpath:

if (!$links = $xpath->query('//a/@href')) {
    throw new Exception('XPath query failed.');
}

您现在在DOMNodeList中拥有共同的$links,并且它包含零个或多个DOMAttr元素,因为我们选择了这些元素。这些现在需要您正在寻找的过滤。

所以你有一些想要匹配的标准。你有详细但不是非常具体的应该如何工作。你有积极的比赛,但也有负面的比赛。但在这两种情况下,如果没有,你就不知道会发生什么。所以我在这里做了一个快捷方式:如果true字符串符合条件,你自己写一个返回false"href"的函数:

function is_valid_href($href) {

    // do whatever you see fit ...

    return true or false;
}

因此,解决href现在是否有效的问题已经解决了。最好的事情:你可以稍后改变它。

因此,将所有链接与链接集成所需要的只是以规范化和绝对形式获取所有链接。这意味着需要更多数据处理,请参阅:

有关不同类型的网址规范化的详细信息。

因此我们创建了另一个封装href规范化,基本分辨率和验证的函数。如果href错误,它只返回null,否则返回标准化的href:

function normalize_href($href, $base) {

    // do whatever is needed ...

    return null or "href string";
}

让我们把它放在一起,在我的情况下,我甚至将href设为Net_URL2实例,以便验证器可以从中受益。

当然,如果你把它包装成闭包或某些类,它会得到一个更好的界面。您也可以考虑将xpath表达式作为参数:

// get all href
if (!$links = $xpath->query('//a/@href')) {
    throw new Exception('XPath query failed.');
}

// set a base URL
$base = 'https://stackoverflow.com/questions/9894956/xpath-query-html-find-specific-hrefs-within-anchor-tags';

/**
 * @return bool
 */
function is_valid_href($href) {    
    ...
}

/**
 * @return href
 */
function normalize_href($href, $base) {
    ...
}

$joblinks = array();
foreach ($links as $attr) {
    $href = normalize_href($attr->nodeValue, $base);
    if (is_valid_href($href)) {
        $joblinks[] = $href;
    }
}

// your result is in:
var_dump($joblinks);

我在这个网站上运行了一个例子,结果是:

array(122) {
  [0]=>
  object(Net_URL2)#129 (8) {
    ["_options":"Net_URL2":private]=>
    array(5) {
      ["strict"]=>
      bool(true)
      ["use_brackets"]=>
      bool(true)
      ["encode_keys"]=>
      bool(true)
      ["input_separator"]=>
      string(1) "&"
      ["output_separator"]=>
      string(1) "&"
    }
    ["_scheme":"Net_URL2":private]=>
    string(4) "http"
    ["_userinfo":"Net_URL2":private]=>
    bool(false)
    ["_host":"Net_URL2":private]=>
    string(17) "stackexchange.com"
    ["_port":"Net_URL2":private]=>
    bool(false)
    ["_path":"Net_URL2":private]=>
    string(1) "/"
    ["_query":"Net_URL2":private]=>
    bool(false)
    ["_fragment":"Net_URL2":private]=>
    bool(false)
  }
  [1]=> 

  ...

  [121]=>
  object(Net_URL2)#250 (8) {
    ["_options":"Net_URL2":private]=>
    array(5) {
      ["strict"]=>
      bool(true)
      ["use_brackets"]=>
      bool(true)
      ["encode_keys"]=>
      bool(true)
      ["input_separator"]=>
      string(1) "&"
      ["output_separator"]=>
      string(1) "&"
    }
    ["_scheme":"Net_URL2":private]=>
    string(4) "http"
    ["_userinfo":"Net_URL2":private]=>
    bool(false)
    ["_host":"Net_URL2":private]=>
    string(22) "blog.stackoverflow.com"
    ["_port":"Net_URL2":private]=>
    bool(false)
    ["_path":"Net_URL2":private]=>
    string(30) "/2009/06/attribution-required/"
    ["_query":"Net_URL2":private]=>
    bool(false)
    ["_fragment":"Net_URL2":private]=>
    bool(false)
  }
}