【问题标题】:Organising an array of URLs by hierarchy按层次结构组织 URL 数组
【发布时间】:2021-10-09 20:39:57
【问题描述】:

我已经构建了一个网络爬虫,它递归地从特定网站获取所有 URL 并将它们存储在一个数组中

下面的例子:

$array = [
    'http://site.test',
    'http://site.test/blog',
    'http://site.test/blog/blog1',
    'http://site.test/blog/blog2',
    'http://site.test/services',
    'http://site.test/services/service1',
    'http://site.test/services/service2',
    'http://site.test/services/service2/sub-service',
    'http://site.test/product',
    'http://site.test/product/product1',
    'http://site.test/product/product1',
];

我正在寻找某种方法将这个数组组织成一个多维数组,以便我可以看到哪些页面是子页面以及哪个部分类似于下面的结构

即:

Home
----blog
--------article1
--------article2
----services
--------service1
--------service2
------------sub-service1
-----product
--------product1
--------product2

我尝试循环并提取每个字符串的某些部分,但似乎无法获得所需的结果。

理想情况下,我希望将结果放在一个数组中,甚至显示在一个多级列表中以供显示。

任何指导将不胜感激!

【问题讨论】:

  • 我会首先从每个 URL 中删除根域 (site.test),然后在 "/" 分隔符上explode() 其余部分。然后循环遍历结果,构建你的多维数组。
  • 根据爆炸长度排序。这样,父母现在总是先于孩子。接下来,是提取父母并为每个值分配版本号,例如11.11.1.2 等。现在,根据这些版本号进行排序,您就完成了。明天试试,让你知道。

标签: php arrays laravel sorting multidimensional-array


【解决方案1】:

让我们试试)我们有一个链接数组

$array = [
    'http://site.test',
    'http://site.test/blog',
    'http://site.test/blog/blog1',
    'http://site.test/blog/blog2',
    'http://site.test/services',
    'http://site.test/services/service1',
    'http://site.test/services/service2',
    'http://site.test/services/service2/sub-service',
    'http://site.test/product',
    'http://site.test/product/product1',
    'http://site.test/product/product2',
];

为了创建树,我们应该创建 Node 类

class Node
{
    private array $childNodes;

    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
        $this->childNodes = [];
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function addChildNode(Node $node): void
    {
        $this->childNodes[$node->getName()] = $node;
    }

    public function hasChildNode(string $name): bool
    {
        return array_key_exists($name, $this->childNodes);
    }

    public function getChildNode(string $name): Node
    {
        return $this->childNodes[$name];
    }

    public function getChildNodes(): array
    {
        return $this->childNodes;
    }
}

还有 Tree 类,使用 Node 类。 方法 appendUrl 解析 URL 并构建节点链。

class Tree
{
    private Node $head;

    public function __construct()
    {
        $this->head = new Node('Head');
    }

    public function getHead(): Node
    {
        return $this->head;
    }

    public function appendUrl(string $url): void
    {
        $parsedUrl = parse_url($url);
        $uri = sprintf('%s//%s', $parsedUrl['scheme'], $parsedUrl['host']);
        $keys = array_filter(explode('/', $parsedUrl['path'] ?? ''));
        $keys = [$uri, ...$keys];

        $node = $this->head;
        foreach ($keys as $key) {
            if (!$node->hasChildNode($key)) {
                $prevNode = $node;
                $node = new Node($key);
                $prevNode->addChildNode($node);
            } else {
                $node = $node->getChildNode($key);
            }
        }
    }
}

现在我们创建将树绘制到控制台的 ConsoleTreeDrawer 类

class ConsoleTreeDrawer
{
    public function draw(Tree $tree): void
    {
        $node = $tree->getHead();
        $this->drawNode($node);
    }

    private function drawNode(Node $node, int $level = 1): void
    {
        $prefix = implode('', array_fill(0, 2 * $level, '-'));

        print("{$prefix}{$node->getName()}\n");

        foreach ($node->getChildNodes() as $childNode) {
            $this->drawNode($childNode, $level + 1);
        }
    }
}

让我们使用我们的类

$tree = new Tree();

foreach ($array as $url) {
    $tree->appendUrl($url);
}

$drawer = new ConsoleTreeDrawer();
$drawer->draw($tree);

我们画了树

--Head
----http//site.test
------blog
--------blog1
--------blog2
------services
--------service1
--------service2
----------sub-service
------product
--------product1

【讨论】:

    【解决方案2】:

    算法:

    • 暂时删除前缀http://,因为它对我们的要求没有用。您可以稍后再添加。

    • 接下来是使用usort 对所有元素进行排序。这里,基于/的爆炸得到的长度。

    • 现在,我们可以确定数组中所有的父母都在孩子之前

    • 接下来是为每个链接分配版本号/等级。命名如下:

      'http://site.test' => 1

      'http://site.test/blog' => 1.1

      'http://site.test/services' => 1.2

      'http://site.test/blog/blog1' => 1.1.1

    • 以上是分配版本号的策略。

    • 现在,我们只需要使用 uasort 根据这个版本号对数组进行排序 你就完成了。

    片段:

    <?php
    
    $array = [
        'http://site.test',
        'http://site.test/blog',
        'http://site.test/blog/blog1',
        'http://site.test/blog/blog2',
        'http://site.test/services',
        'http://site.test/services/service1',
        'http://site.test/services/service2',
        'http://site.test/services/service2/sub-service',
        'http://site.test/product',
        'http://site.test/product/product1',
    ];
    
    // remove http:// 
    foreach($array as &$val){
        $val = substr($val,7);
    }
    
    // sort based on length on explode done on delimiter '/' 
    usort($array, function($a,$b){
        return count(explode("/",$a)) <=> count(explode("/",$b));
    });
    
    $ranks = [];
    $child_count = [];
    
    // assign ranks/version numbers
    
    foreach($array as $link){
        $parent = getParent($link);
        if(!isset($ranks[$parent])){
            $ranks[$link] = 1;
        }else{
            $child_count[$parent]++;
            $ranks[$link] = $ranks[$parent] . "." . $child_count[$parent];
        }
        $child_count[$link] = 0;
    }
    
    function getParent($link){
        $link = explode("/",$link);
        array_pop($link);
        return implode("/",$link);
    }
    
    // sort based on version numbers
    uasort($ranks,function($a,$b){
        $version1 = explode(".", $a);
        $version2 = explode(".", $b);
        foreach($version1 as $index => $v_num){
            if(!isset($version2[$index])) return 1;
            $aa = intval($v_num);
            $bb = intval($version2[$index]);
            if($aa < $bb) return -1;
            if($bb < $aa) return 1;
        }
        
        return count($version1) <=> count($version2);
    });
    
    // get the actual product links that were made as keys 
    $array = array_keys($ranks);
    print_r($array);// now you can attach back http:// prefix if you like
    

    注意:当前算法会删除重复项,保留它们没有意义。


    #更新:

    由于您需要一个多维层次数组,我们可以跟踪父子数组链接引用并将子元素插入到它们各自的父元素中。

    <?php
    
    $array = [
    'http://site.test',
    'http://site.test/blog',
    'http://site.test/blog/blog1',
    'http://site.test/blog/blog2',
    'http://site.test/services',
    'http://site.test/services/service1',
    'http://site.test/services/service2',
    'http://site.test/services/service2/sub-service',
    'http://site.test/product',
    'http://site.test/product/product1',
    ];
    
    foreach($array as &$val){
        $val = substr($val,7);
    }
    
    usort($array, function($a,$b){
        return count(explode("/",$a)) <=> count(explode("/",$b));
    });
    
    $hier = [];
    $set = [];
    
    foreach($array as $link){
        $parent = getParent($link);
        if(!isset($set[$parent])){
            $hier[$link] = [];
            $set[$link] = &$hier[$link];
        }else{
            $parent_array = &$set[$parent];
            $parent_array[$link] = [];
            $set[$link] = &$parent_array[$link];
        }    
    }
    
    function getParent($link){
        $link = explode("/",$link);
        array_pop($link);
        return implode("/",$link);
    }
    
    
    print_r($hier);
         
    

    【讨论】:

    • 谢谢你,逻辑对我来说很有意义,但并没有给我最终的结果,我正在寻找我正在寻找的不幸
    • @JonnyFoley 你能分享一个示例链接来显示它对于哪个输入不起作用吗?
    • @JonnyFoley 它工作正常。见这里sandbox.onlinephpfunctions.com/code/…所有链接都正确分组
    • 代码执行完美,排序正确,但是我希望最后能把它作为一个多维数组,这样我就可以把它用作站点地图,可能不完全像下面,而是像,$ a = [ ['site.test'] => [ ['blog'] => ['blog1', 'blog2'], ['services'] => ['service1', 'service2' => ['sub -service1', 'sub-service2' ] ], ['product'] => ['product1', 'product2'], ] ];
    • @JonnyFoley 现在也更新了。
    猜你喜欢
    • 1970-01-01
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    相关资源
    最近更新 更多