PHP抓取一个网站内容

很多时候,抓取一个网站并从特定的标签中提取信息是非常有价值的。这种基本的机制可以用来在网络上搜索有价值的信息。在其他时候,你可能需要得到一个 <IMG> 标签和 SRC 属性的列表,或者 <A> 标签和相应的 HREF 属性。这种可能性是无穷无尽的。

1、首先新建一个抓取类: capture.php

class Capture {

    public $content;

2、抓取目标网站的内容。乍一看,似乎我们应该发出 cURL 请求,或者干脆使用 file_get_contents() 。这些方法的问题是,我们最终将不得不进行大量的字符串操作,很可能不得不过度使用可怕的正则表达式。为了避免这一切,我们将简单地利用已经存在的 PHP 7 类 DOMDocument 。所以我们创建一个 DOMDocument 实例,并将其设置为UTF-8。我们不关心空格,使用方便的 loadHTMLFile() 方法将网站的内容加载到对象中:

public function getContent($url)
{
    if (!$this->content) {
        if (stripos($url, 'http') !== 0) {
            $url = 'http://' . $url;
        }
        $this->content = new DOMDocument('1.0', 'utf-8');
        $this->content->preserveWhiteSpace = FALSE;
        // @ used to suppress warnings generated from // improperly configured web pages
        @$this->content->loadHTMLFile($url);
    }
    return $this->content;
}

请注意,我们在调用 loadHTMLFile() 方法之前加上了 @。这并不是为了掩盖错误的编码(!),就像在PHP
5中经常发生的那样!而是当解析器遇到写得不好的HTML时,@ 会抑制产生的通知。相反,当解析器遇到写得不好的HTML时,@
会抑制产生的通知。大概我们可以捕获这些通知并记录下来,可能会给我们的 Capture 类提供一个诊断功能。

3、接下来,提取感兴趣的标签。我们使用 getElementsByTagName() 方法来实现这一目的。如果我们希望提取所有标记,我们可以提供 * 作为参数:

public function getTags($url, $tag)
{
    $count    = 0;
    $result   = array();
    $elements = $this->getContent($url)
                     ->getElementsByTagName($tag);
    foreach ($elements as $node) {
        $result[$count]['value'] = trim(preg_replace('/\s+/', ' ', $node->nodeValue));
        if ($node->hasAttributes()) {
            foreach ($node->attributes as $name => $attr) 
            {
                $result[$count]['attributes'][$name] = 
                    $attr->value;
            }
        }
        $count++;
    }
    return $result;
}

4、提取某些属性而不是标签也可能是有意义的。因此,我们为此定义了另一个方法。在这种情况下,我们需要解析所有的标签并使用 getAttribute() 。你会注意到,有一个DNS域的参数。我们添加这个参数是为了使扫描保持在同一个域内(例如,如果你正在构建一个网络树):

public function getAttribute($url, $attr, $domain = NULL)
{
    $result   = array();
    $elements = $this->getContent($url)
                     ->getElementsByTagName('*');
    foreach ($elements as $node) {
        if ($node->hasAttribute($attr)) {
            $value = $node->getAttribute($attr);
            if ($domain) {
                if (stripos($value, $domain) !== FALSE) {
                    $result[] = trim($value);
                }
            } else {
                $result[] = trim($value);
            }
        }
    }
    return $result;
}

5、最后执行。 例如,访问传入 ?url=sanplit.cn&tag=h2 获取文章标题,根据网站结构修改。

$obj= new Capture();

$url = strip_tags($_GET['url']);
$tag = strip_tags($_GET['tag']);

echo 'Dump of Tags: ' . PHP_EOL;
var_dump($obj->getTags($url, $tag));

参考
更多关于 DOM 的信息,请参阅 PHP 参考 DOMDocument


扩展,建立一个深度网络扫描器。
有时您需要扫描一个网站,但是要更深一层。例如,你想建立一个网站的网络树图。这可以通过寻找所有 <A> 标签,并跟随 HREF 属性到下一个网页来实现。一旦你获得了子页面,你就可以继续扫描,以便完成树状图。如抓取图片为例:?url=sanplit.cn&tag=img

<?php
    include 'Capture.php';
    class Deep
    {
        protected $domain;
    
        public function scan($url, $tag)
        {
        $cap    = new Capture();
        $scan   = $cap->getAttribute($url, 'href', $this->getDomain($url));
        $result = array();
        foreach ($scan as $subSite) {
            yield from $cap->getTags($subSite, $tag);
        }
        return count($scan);
        }
    
        public function getDomain($url)
        {
        if (!$this->domain) {
            $this->domain = parse_url($url, PHP_URL_HOST);
        }
        return $this->domain;
        }
    
    }

    $obj = new Deep();

    $url = strip_tags($_GET['url']);
    $tag = strip_tags($_GET['tag']);

    foreach ($obj->scan($url, $tag) as $item) {
        $src = $item['attributes']['src'] ?? NULL;
        if ($src && (stripos($src, 'png') || stripos($src, 'jpg'))) {
            printf('<br><img src="%s"/>', $src);
        }
    }

转载:PHP7

- End -




(0)
爬虫PHP7