【问题标题】:Is there a way to use PHP to crawl links?有没有办法使用 PHP 来抓取链接?
【发布时间】:2009-09-17 08:18:47
【问题描述】:

我想使用 PHP 来抓取我们拥有的包含大约 6 或 7000 个 href 链接的文档。我们需要的是链接另一侧的内容,这意味着 PHP 必须跟踪每个链接并获取链接的内容。这个可以吗?

谢谢

【问题讨论】:

    标签: php


    【解决方案1】:

    当然,只需使用 file_get_contents (http://nl.php.net/file_get_contents) 之类的函数获取起始 url 的内容,使用正则表达式在此页面的内容中查找 URL,获取这些 url 的内容等等。

    正则表达式类似于:

    $regexUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
    

    【讨论】:

    • 谢谢桑德。好的,所以一旦我得到内容,比如所有的 URL,我会遍历每个 URL,但是我如何告诉 PHP 跟随链接?
    • 嘿 Sander,我不能对每个链接也使用 file_get_contents() 吗?
    • 是的,您可以使用 file_get_contents() 来获取页面内链接的内容。基本上,您为找到的每个链接重复“获取 url 内容 + 从中提取链接”过程。
    【解决方案2】:

    获取链接后,您可以使用curl 或 file_get_contents(但在安全环境中,file_get_contents 不应允许通过 http 协议)

    【讨论】:

    • 您好 Eineki,我拥有的是带有 6K 链接的主要 html 文档。我想我会先把它们解析出来,然后再努力获取它们背后的数据。不过,我无权使用 curl。这会是一个问题吗?我有哪些选择?
    • 如果在 php.ini 中启用了 allow_url_fopen,您可以使用任何以文件名作为参数的函数来打开 URL(也许你不能使用 include 和 require,我不关心它们)。 readfile, fopen, get_file_contents 是你的选择,也许还有其他的。如果 allow_url_fopen 也许你应该手动恢复使用套接字,但我不想穿上你的鞋子;)
    【解决方案3】:

    我只有一个我找到的所有链接的 SQL 表,以及它们是否已被解析。

    然后我使用Simple HTML DOM 来解析最旧的添加页面,尽管由于它倾向于用大页面(500kb+ 的 html)耗尽内存,我对其中一些使用正则表达式*。对于我找到的每个链接,我将其添加到 SQL 数据库中作为需要解析,以及我找到它的时间。

    SQL 数据库可防止数据因错误而丢失,并且由于我有 100,000 多个要解析的链接,因此我会在很长一段时间内进行分析。

    我不确定,但你检查过 file_get_contents() 的用户代理吗?如果不是您的页面并且您发出了 1000 次请求,您可能想要更改用户代理,方法是编写自己的 HTTP 下载器或使用库中的一个(我使用 Zend 框架中的那个)但 cURL 等工作正常。如果您使用自定义用户代理,它允许管理员查看日志以查看有关您的机器人的信息。 (我倾向于把我爬的原因和一个联系人放在我的里面)。

    *我使用的正则表达式是:

    '/<a[^>]+href="([^"]+)"[^"]*>/is'
    

    更好的解决方案(来自 Gumbo)可能是:

    '/<a\s+(?:[^"'>]+|"[^"]*"|'[^']*')*href=("[^"]+"|'[^']+'|[^<>\s]+)/i'
    

    【讨论】:

    • 嗨,Yacoby,嗯……这不是我的数据。我从没想过改变用户代理。我对这类东西并不了解,但如果我不更改用户代理,会发生什么?
    • 感谢您的正则表达式。我会试试看。
    • 属性值也可能包含&gt; 字符并用单引号括起来,甚至根本不用引号。所以最好使用这个:/&lt;a\s+(?:[^"'&gt;]+|"[^"]*"|'[^']*')*href=("[^"]+"|'[^']+'|[^&lt;&gt;\s]+)/i
    【解决方案4】:

    PHP Snoopy 库有许多内置函数可以完全满足您的需求。

    http://sourceforge.net/projects/snoopy/

    您可以使用 Snoopy 下载页面本身,然后它还有一个功能可以提取该页面上的所有 URL。它甚至会将链接更正为完整的 URI(即它们不只是相对于页面所在的域/目录)。

    【讨论】:

      【解决方案5】:

      您可以尝试以下方法。详情请见this thread

      <?php
      //set_time_limit (0);
      function crawl_page($url, $depth = 5){
      $seen = array();
      if(($depth == 0) or (in_array($url, $seen))){
          return;
      }   
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_TIMEOUT, 30);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
      $result = curl_exec ($ch);
      curl_close ($ch);
      if( $result ){
          $stripped_file = strip_tags($result, "<a>");
          preg_match_all("/<a[\s]+[^>]*?href[\s]?=[\s\"\']+"."(.*?)[\"\']+.*?>"."([^<]+|.*?)?<\/a>/", $stripped_file, $matches, PREG_SET_ORDER ); 
          foreach($matches as $match){
              $href = $match[1];
                  if (0 !== strpos($href, 'http')) {
                      $path = '/' . ltrim($href, '/');
                      if (extension_loaded('http')) {
                          $href = http_build_url($url, array('path' => $path));
                      } else {
                          $parts = parse_url($url);
                          $href = $parts['scheme'] . '://';
                          if (isset($parts['user']) && isset($parts['pass'])) {
                              $href .= $parts['user'] . ':' . $parts['pass'] . '@';
                          }
                          $href .= $parts['host'];
                          if (isset($parts['port'])) {
                              $href .= ':' . $parts['port'];
                          }
                          $href .= $path;
                      }
                  }
                  crawl_page($href, $depth - 1);
              }
      }   
      echo "Crawled {$href}";
      }   
      crawl_page("http://www.sitename.com/",3);
      ?>
      

      【讨论】:

        【解决方案6】:

        我建议您使用包含 6000 个 URL 的 HTML 文档,将它们解析出来并循环浏览您拥有的列表。在您的循环中,使用 file_get_contents 获取当前 URL 的内容(为此,在您的服务器上启用 file_get_contents 时,您实际上并不需要 cURL),再次解析出包含的 URL,等等。

        看起来像这样:

        <?php
        function getUrls($url) {
            $doc = file_get_contents($url);
            $pattern = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
            preg_match_all($pattern, $doc, $urls);
            return $urls;
        }
        
        $urls = getUrls("your_6k_file.html"); 
        foreach($urls as $url) {
            $moreUrls = getUrls($url); 
            //do something with moreUrls
        }
        ?>
        

        【讨论】:

        • 你的意思可能是 foreach($urls[0] as $url) :)
        猜你喜欢
        • 1970-01-01
        • 2014-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多