【问题标题】:XPath Query & HTML - Find Specific HREF's Within Anchor TagsXPath 查询和 HTML - 使用锚标记查找特定 HREF
【发布时间】:2012-04-11 07:18:03
【问题描述】:

我在DOMDocumentDOMXPath 中获得了所需的HTML 数据。

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

  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)

这些(类似)查询中的任何一个都可以获取除 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 值。如果我没有遵循这个问题的任何协议,我最谦虚的道歉......

任何/所有帮助将不胜感激!非常感谢!

【问题讨论】:

  • 是否有必要仅使用 XPath 查询过滤 href 值?我确信这是可能的,但是看着它我头疼(典型的 XPath)。也许您可以使用$xpath->query() 来获取与广泛模式匹配的节点列表(例如,href="https://some-site.vendor.com/jobs/"),然后使用几行 PHP 来更深入地检查每个节点的 href 值?
  • XPath 1.0 而不是XPath 1.1 肯定会让这变得不必要地冗长......但这就是我们所拥有的一切。我将创建一个可以与registerphpfunctions 进行正则表达式匹配的函数。

标签: php xpath


【解决方案1】:

我不建议只使用xpath 来做这件事。首先,您有一个白名单和一个黑名单。目前还不清楚你想要什么,所以我认为这会随着时间的推移而改变。

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

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

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

所以你有一些你想要匹配的标准。你有详细但不是很具体应该如何工作。你有一个积极的匹配,但也有消极的匹配。但是在这两种情况下,您都不会告诉如果不应该会发生什么。所以我在这里做一个捷径:你自己写一个函数,如果 "href" 字符串符合条件,则返回 truefalse

function is_valid_href($href) {

    // do whatever you see fit ...

    return true or false;
}

所以现在判断href 是否有效的问题已经解决了。最好的事情:您可以稍后更改。

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

有关不同类型的 URL 规范化的更多详细信息。

所以我们创建了另一个函数来封装 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)
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-23
    • 2023-03-14
    • 1970-01-01
    相关资源
    最近更新 更多