如何获取网页的标题和内容


How to grab title and content of web page

我有一个网页,例如http://example.com/some-page。如果我将这个URL传递给我的PHP函数,它应该会获取页面的标题和内容。我试着这样抢标题:

function page_title($url) {
    $page = @file_get_contents($url);
    if (preg_match('~<h1 class="page-title">(.*)<'/h1>~is', $page, $matches)) {
        return $matches[0];
    }
}
echo page_title('http://example.com/some-page');  

我犯了什么错?

您的函数实际上几乎可以工作。我将提出DOM解析器解决方案(见下文),但在此之前,我将指出正则表达式和代码中的一些弱点:

  • (.*)捕获组是贪婪的,即它将捕获在闭合</h1>之前尽可能长的字符串,即使是换行符(因为s修饰符)。因此,如果你的文档有多个h1标签,它会一直捕获到最后一个!你可以通过使它成为一个懒惰的捕获来解决这个问题:(.*?)

  • 实际页面的标题内可能有其他标签,如span。您可能希望改进正则表达式,以排除标题周围的任何标记,但PHP有一个函数strip_tags用于此目的。

  • 确保实际检索到文件内容;错误可能阻止了正确的检索,或者您的服务器可能不允许这样的检索。当您使用@前缀来抑制错误时,您可能会错过它们。我建议去掉@。您也可以检查false的返回值
  • 你确定要H1标签的内容吗?页面通常具有特定的title标记

上述改进将为您提供以下代码:

function page_title($url) {
    $page = file_get_contents($url);
    if ($page===false) {
        echo "Failed to retrieve $url";
    }
    if (preg_match('~<h1 class="page-title">(.*?)<'/h1>~is', $page, $matches)) {
        return strip_tags($matches[0]);
    }
}

尽管这样做有效,但您迟早会碰到一个文档,该文档在h1标记中有一个额外的空间,或者在class之前有另一个属性,或者有多个css样式,等等…使匹配失败。下面的正则表达式将处理其中的一些问题:

~<h1's+class's*='s*"([^" ]* )?page-title( [^"]*)?"[^>]*>(.*?)<'/h1's*>~is

但是class属性仍然必须位于任何其他属性之前,并且其值必须用双引号括起来。这也是可以解决的,但正则表达式将变成一个怪物。

DOM方式

正则表达式不是从HTML中提取内容的理想方法。这里有一个基于DOM解析的替代函数:

function xpage_title($url) {
    // Create a new DOM Document to hold our webpage structure
    $xml = new DOMDocument();
    // Load the url's contents into the DOM, ignore warnings
    libxml_use_internal_errors(true);
    $success = $xml->loadHTMLFile($url);
    libxml_use_internal_errors(false);
    if (!$success) {
        echo "Failed to open $url.";
        return;
    }
    // Find first h1 with class 'page-title' and return it's text contents
    foreach($xml->getElementsByTagName('h1') as $h1) {
        // Does it have the desired class?
        if (in_array('page-title', explode(" ", $h1->getAttribute('class')))) {
            return $h1->textContent;
        }
    }
}

使用CCD_ 15仍能改善上述性能。

编辑

您在评论中提到,您实际上不想要H1标记的内容,因为它包含的文本比您想要的多。

然后您可以读取title标签和article标签内容:

function page_title_and_content($url) {
    $page = file_get_contents($url);
    if ($page===false) {
        echo "Failed to retrieve $url";
    }
    // PHP 5.4: $result = (object) ["title" => null, "content" => null];
    $result = new stdClass();
    $result->title = null;
    $result->content = null;
    if (preg_match('~'<title'>(.*?)'<'/title'>~is', $page, $matches)) {
        $result->title = $matches[1];
    }
    if (preg_match('~<article>(.*)<'/article>~is', $page, $matches)) {
        $result->content = $matches[1];
    }
    return $result;
}
$result = page_title_and_content('http://www.example.com/example');
echo "title: " . $result->title . "<br>";
echo "content: <br>" . $result->content . "<br>";

上面的代码将返回一个具有两个属性的对象:titlecontent。请注意,content属性将具有HTML标记,其中可能包含图像等。如果不需要标记,请应用strip_tags