抓取一个网站

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

如何做…

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

  1. public function getContent($url)
  2. {
  3. if (!$this->content) {
  4. if (stripos($url, 'http') !== 0) {
  5. $url = 'http://' . $url;
  6. }
  7. $this->content = new DOMDocument('1.0', 'utf-8');
  8. $this->content->preserveWhiteSpace = FALSE;
  9. // @ used to suppress warnings generated from // improperly configured web pages
  10. @$this->content->loadHTMLFile($url);
  11. }
  12. return $this->content;
  13. }

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

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

  1. public function getTags($url, $tag)
  2. {
  3. $count = 0;
  4. $result = array();
  5. $elements = $this->getContent($url)
  6. ->getElementsByTagName($tag);
  7. foreach ($elements as $node) {
  8. $result[$count]['value'] = trim(preg_replace('/\s+/', ' ', $node->nodeValue));
  9. if ($node->hasAttributes()) {
  10. foreach ($node->attributes as $name => $attr)
  11. {
  12. $result[$count]['attributes'][$name] =
  13. $attr->value;
  14. }
  15. }
  16. $count++;
  17. }
  18. return $result;
  19. }

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

  1. public function getAttribute($url, $attr, $domain = NULL)
  2. {
  3. $result = array();
  4. $elements = $this->getContent($url)
  5. ->getElementsByTagName('*');
  6. foreach ($elements as $node) {
  7. if ($node->hasAttribute($attr)) {
  8. $value = $node->getAttribute($attr);
  9. if ($domain) {
  10. if (stripos($value, $domain) !== FALSE) {
  11. $result[] = trim($value);
  12. }
  13. } else {
  14. $result[] = trim($value);
  15. }
  16. }
  17. }
  18. return $result;
  19. }

如何运行…

为了使用新的 Hoover 类,请初始化自动加载器(如前所述)并创建 Hoover 类的实例。 然后,您可以运行 Hoover::getTags() 方法,从指定为参数的URL生成标签数组。

下面是 chap_01_vacuuming_website.php 中的一段代码,它使用 Hoover 类来扫描O’Reilly网站的 <A> 标签:

  1. <?php
  2. // 根据需要修改
  3. define('DEFAULT_URL', 'http://oreilly.com/');
  4. define('DEFAULT_TAG', 'a');
  5. require __DIR__ . '/../Application/Autoload/Loader.php';
  6. Application\Autoload\Loader::init(__DIR__ . '/..');
  7. // 获取 "vacuum" 类
  8. $vac = new Application\Web\Hoover();
  9. // N注意:使用PHP 7 null 合并运算符
  10. $url = strip_tags($_GET['url'] ?? DEFAULT_URL);
  11. $tag = strip_tags($_GET['tag'] ?? DEFAULT_TAG);
  12. echo 'Dump of Tags: ' . PHP_EOL;
  13. var_dump($vac->getTags($url, $tag));

输出结果如下所示:

抓取一个网站 - 图1

参考

更多关于 DOM 的信息,请参阅 PHP 参考 http://php.net/manual/en/class.domdocument.php