如何使用PHP检查URL是外部URL还是内部URL?

11

我正在使用这个循环获取页面的所有ahrefs:

foreach($html->find('a[href!="#"]') as $ahref) {
    $ahrefs++;
}

我想做类似这样的事情:

foreach($html->find('a[href!="#"]') as $ahref) {
    if(isexternal($ahref)) {
        $external++;
    }
    $ahrefs++;
}

where isexternal是一个函数

function isexternal($url) {
    // FOO...

    // Test if link is internal/external
    if(/*condition is true*/) {
        return true;
    }
    else {
        return false;
    }
}

救命啊!


是外部的还是内部的?指的是网页被抓取的网站吗? - Andrew Barber
我认为这就是他的意思。比如,“这是一个跨网站链接吗?” - Apolo
是的。我已经获取了每个带有其href +域名的< a >链接。只是我想知道如何将其与相同域名的URL分开。 - mehulmpt
但是你知道这个HTML代码来自哪里吗?(如果它是从abc.com获得的HTML,则是否有一个变量$site == 'abc.com'?) - Apolo
先生,我已经准备好了。 - mehulmpt
显示剩余2条评论
5个回答

22
使用parse_url并将主机与本地主机进行比较(通常但不总是与$_SERVER['HTTP_HOST']相同)
function isexternal($url) {
  $components = parse_url($url);    
  return !empty($components['host']) && strcasecmp($components['host'], 'example.com'); // empty host will indicate url like '/relative.php'
}

然而,这将把www.example.com和example.com视为不同的主机。如果您希望将所有子域视为本地链接,则该函数将稍微复杂一些:
function isexternal($url) {
  $components = parse_url($url);
  if ( empty($components['host']) ) return false;  // we will treat url like '/relative.php' as relative
  if ( strcasecmp($components['host'], 'example.com') === 0 ) return false; // url host looks exactly like the local host
  return strrpos(strtolower($components['host']), '.example.com') !== strlen($components['host']) - strlen('.example.com'); // check if the url host is a subdomain
}

不确定这是否无障碍,如果某些链接包含www.,有些则没有。如果主机值中存在www.(或其他子域),则应将其删除。 - user1610743
1
子域名的检查不可靠。 例如,theexample.com将与example.com匹配。 - Jeboy

2
这是如何简单检测外部URL的方法:
$url    = 'https://my-domain.com/demo/';
$domain = 'my-domain.com';

$internal = (
    false !== stripos( $url, '//' . $domain ) || // include "//my-domain.com" and "http://my-domain.com"
    stripos( $url, '.' . $domain ) ||            // include subdomains, like "www.my-domain.com". DANGEROUS (see below)!
    (
        0 !== strpos( $url, '//' ) &&            // exclude protocol relative URLs, like "//example.com"
        0 === strpos( $url, '/' )                // include root-relative URLs, like "/demo"
    )
);

上述检查将把www.my-domain.commy-domain.com视为“内部”的。 为什么这个规则是危险的
子域逻辑引入了一个弱点,可能会被利用:当外部URL在路径中包含您的域时,例如,https://external.com/www.my-domain.com被视为内部! 更安全的代码
可以通过删除子域支持来消除此问题(我建议这样做):
$url    = 'https://my-domain.com/demo/';
$domain = 'my-domain.com';

$internal = (
    false !== stripos( $url, '//' . $domain ) || // include "//my-domain.com" and "http://my-domain.com"
    (
        0 !== strpos( $url, '//' ) &&            // exclude protocol relative URLs, like "//example.com"
        0 === strpos( $url, '/' )                // include root-relative URLs, like "/demo"
    )
);

1

我知道这篇文章有点旧了,但是这是我刚编写的函数。也许其他人也需要它。

function IsResourceLocal($url){
    if( empty( $url ) ){ return false; }
    $urlParsed = parse_url( $url );
    $host = $urlParsed['host'];
    if( empty( $host ) ){ 
    /* maybe we have a relative link like: /wp-content/uploads/image.jpg */
    /* add absolute path to begin and check if file exists */
    $doc_root = $_SERVER['DOCUMENT_ROOT'];
    $maybefile = $doc_root.$url;
    /* Check if file exists */
    $fileexists = file_exists ( $maybefile );
    if( $fileexists ){
        /* maybe you want to convert to full url? */
        return true;        
        }
     }
    /* strip www. if exists */
    $host = str_replace('www.','',$host);
    $thishost = $_SERVER['HTTP_HOST'];
    /* strip www. if exists */
    $thishost = str_replace('www.','',$thishost);
    if( $host == $thishost ){
        return true;
        }
    return false;
}

如果您使用Web框架,还应检查是否存在有效的本地路由,例如“/2017/10/some-nice-post”。但通常,框架已经拥有了这样的方法。另外,只有在开头时才需要执行str_replace('www。','',$ host); - Ruslan Bes

0
function isexternal($url) {
    // FOO...

    // Test if link is internal/external
    if(strpos($url,'domainname.com') !== false || strpos($url,"/") === '0') 
    {
         return true;
    }
    else 
    {
         return false;
    }
}

1
坏答案,原因如上所述^ - uyuyuy99
如果URL字符串是相对链接怎么办?这个答案不好。 - Daniel Cheung
如果URL包含您的URL作为参数,例如:http://someapidomain.com/callapi/?url=yourdomain.com 这将在您的函数中返回true。 - Mike Aron
1
@RuslanBes,strpos($url,"/") === '0'将永远不会匹配这对于根相对URL(相对于当前域)是正确的。然而,它也会误解协议相对URL,例如//example.com/page - Philipp

-2

您可能想要检查链接是否在同一域中。但是,这只适用于所有href属性都是绝对的并包含该域的情况。像/test/file.html这样的相对路径比较棘手,因为一个文件夹的名称可以与域名相同...所以,如果每个链接都有完整的URL:

function isexternal($url) {

  // Test if link is internal/external
  if(stristr($url, "myDomain.com") || strpos($url,"/") == '0')
    return true;
  else
    return false;
}

如果a[href]以/开头,则将其标记为内部链接。请更新代码。 - mehulmpt
实际上,如果它以'/'开头,则是绝对路径。相对路径是从HTML所在的位置开始计算的。例如,'img/img1.png'可以表示为'/shared/img/img1.png'。 - Apolo
1
是的,已经添加了。主题发起人说它将包含域名。 @Apolo: 他想知道哪些是外部的;在这种情况下,这样做是可以的。路径是否绝对或相对无关紧要,只要它在同一域空间中。 - user1610743
没问题。只要域名包含测试域名的主机名,就将其标记为内部。这意味着如果正在测试abc.com,则将不带http并且不包含abc.com的每个域名和每个具有域名主机名的链接标记为非外部,反之亦然。 - mehulmpt
@MehulMohan //evilhost.example.com/ 开头,在许多情况下被视为有效的 URL... 不要盲目地假设 / 可以保证绝对、同主机路径! - dst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接