编程

Laravel Sitemap 生成器 - 生成站点地图

569 2023-09-28 10:20:00

spatie/laravel-sitemap 是一个 Laravel 站点的 Sitemap 生成器。可以在无需手动添加 URL 的情况下,爬取整个站点生成站点地图。

use Spatie\Sitemap\SitemapGenerator;

SitemapGenerator::create('https://example.com')->writeToFile($path);

你也可以手动生成站点地图(sitemap):

use Carbon\Carbon;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;

Sitemap::create()

    ->add(Url::create('/home')
        ->setLastModificationDate(Carbon::yesterday())
        ->setChangeFrequency(Url::CHANGE_FREQUENCY_YEARLY)
        ->setPriority(0.1))

   ->add(...)

   ->writeToFile($path);

或者你也可以生成 Sitemap 然后再添加链接:


SitemapGenerator::create('https://example.com')
   ->getSitemap()
   ->add(Url::create('/extra-page')
        ->setLastModificationDate(Carbon::yesterday())
        ->setChangeFrequency(Url::CHANGE_FREQUENCY_YEARLY)
        ->setPriority(0.1))


    ->add(...)


    ->writeToFile($path);

你也可以控制 Sitemap 的最大深度:

SitemapGenerator::create('https://example.com')
    ->configureCrawler(function (Crawler $crawler) {
        $crawler->setMaximumDepth(3);
    })
    ->writeToFile($path);

该生成器在每个页面中拥有[执行 JavaScript 的能力],因此由 JavaScript 注入到 DOM 的链接也可以爬取。
你也可以将 sitemap 写入到可用的文件系统磁盘。

SitemapGenerator::create('https://example.com')->getSitemap()->writeToDisk('public', 'sitemap.xml');

你也可用通过实现 \Spatie\Sitemap\Contracts\Sitemapable 接口,添加模型。 

use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;

class Post extends Model implements Sitemapable
{
    public function toSitemapTag(): Url | string | array
    {
        return route('blog.post.show', $this);
    }
}

现在,你就可以添加单个 post 模型到 sitemap 或者甚至整个集合。

use Spatie\Sitemap\Sitemap;

Sitemap::create()
    ->add($post)
    ->add(Post::all());

这样可以很快就添加页面,而不必爬取所有页面。

安装

首先,通过 Composer 安装:

composer require spatie/laravel-sitemap

这个包会自己自动注册。

配置

你可以重写爬虫的默认选项。首先发布配置:

php artisan vendor:publish --provider="Spatie\Sitemap\SitemapServiceProvider" --tag=sitemap-config

这会复制默认的配置文件 config/sitemap.php

use GuzzleHttp\RequestOptions;
use Spatie\Sitemap\Crawler\Profile;

return [

    /*
     * These options will be passed to GuzzleHttp\Client when it is created.
     * For in-depth information on all options see the Guzzle docs:
     *
     * http://docs.guzzlephp.org/en/stable/request-options.html
     */
    'guzzle_options' => [


        /*
         * Whether or not cookies are used in a request.
         */
        RequestOptions::COOKIES => true,


        /*
         * The number of seconds to wait while trying to connect to a server.
         * Use 0 to wait indefinitely.
         */
        RequestOptions::CONNECT_TIMEOUT => 10,


        /*
         * The timeout of the request in seconds. Use 0 to wait indefinitely.
         */
        RequestOptions::TIMEOUT => 10,


        /*
         * Describes the redirect behavior of a request.
         */
        RequestOptions::ALLOW_REDIRECTS => false,
    ],
   
    /*
     * The sitemap generator can execute JavaScript on each page so it will
     * discover links that are generated by your JS scripts. This feature
     * is powered by headless Chrome.
     */
    'execute_javascript' => false,
   
    /*
     * The package will make an educated guess as to where Google Chrome is installed.
     * You can also manually pass it's location here.
     */
    'chrome_binary_path' => '',


    /*
     * The sitemap generator uses a CrawlProfile implementation to determine
     * which urls should be crawled for the sitemap.
     */
    'crawl_profile' => Profile::class,
   
];

用途

生成 sitemap

最简单的方法是爬取给定的域名,并用所有找到的链接生成 sitemap。

sitemap 的目标路径由 $path 指定:

SitemapGenerator::create('https://example.com')->writeToFile($path);

生成的 sitemap 类似于如此:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc>https://example.com</loc>
        <lastmod>2016-01-01T00:00:00+00:00</lastmod>
        <changefreq>daily</changefreq>
        <priority>0.8</priority>
    </url>
    <url>
        <loc>https://example.com/page</loc>
        <lastmod>2016-01-01T00:00:00+00:00</lastmod>
        <changefreq>daily</changefreq>
        <priority>0.8</priority>
    </url>


    ...
</urlset>

自定义 sitemap generator

定义自定义 Crawl Profile


你可以通过实现 Spatie\Crawler\CrawlProfiles\CrawlProfile 接口,并实现 shouldCrawl() 方法完全控制哪些 url/domain/subdomain 应该爬取,创建自定义爬虫:

use Spatie\Crawler\CrawlProfiles\CrawlProfile;
use Psr\Http\Message\UriInterface;


class CustomCrawlProfile extends CrawlProfile
{
    public function shouldCrawl(UriInterface $url): bool
    {
        if ($url->getHost() !== 'localhost') {
            return false;
        }
       
        return $url->getPath() === '/';
    }
}

并在 config/sitemap.php 中注册 CustomCrawlProfile::class:

return [
    ...
    /*
     * The sitemap generator uses a CrawlProfile implementation to determine
     * which urls should be crawled for the sitemap.
     */
    'crawl_profile' => CustomCrawlProfile::class,
   
];

修改属性

要修改页面的 lastmodchangefreqpriority :

use Carbon\Carbon;
use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;


SitemapGenerator::create('https://example.com')
   ->hasCrawled(function (Url $url) {
       if ($url->segment(1) === 'contact') {
           $url->setPriority(0.9)
               ->setLastModificationDate(Carbon::create('2016', '1', '1'));
       }


       return $url;
   })
   ->writeToFile($sitemapPath);

省略部分链接

如果您不希望已爬取的链接出现在网站地图中,请不要将其返回到您传递 hasCrawled 的可调用项中。

use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;

SitemapGenerator::create('https://example.com')
   ->hasCrawled(function (Url $url) {
       if ($url->segment(1) === 'contact') {
           return;
       }


       return $url;
   })
   ->writeToFile($sitemapPath);

防止爬虫爬取某些页面

您还可以通过将“ callable传递给 shouldCrawl来指示底层爬网程序不要对某些页面进行爬取。

:`shouldCrawl`  只能与默认爬虫 Profile 或者实现 shouldCrawlCallaback 方法的自定义 crawl profile 一起工作。

use Spatie\Sitemap\SitemapGenerator;
use Psr\Http\Message\UriInterface;


SitemapGenerator::create('https://example.com')
   ->shouldCrawl(function (UriInterface $url) {
       // All pages will be crawled, except the contact page.
       // Links present on the contact page won't be added to the
       // sitemap unless they are present on a crawlable page.
       
       return strpos($url->getPath(), '/contact') === false;
   })
   ->writeToFile($sitemapPath);

配置爬虫

爬虫本身可以配置做许多不同的事情。

你可以使用 sitemap 生成器配置爬虫,比如:忽略 robot 检测,像这样:

SitemapGenerator::create('http://localhost:4020')
    ->configureCrawler(function (Crawler $crawler) {
        $crawler->ignoreRobots();
    })
    ->writeToFile($file);

限制页面爬取数量

调用 setMaximumCrawlCount 可以限制页面爬取数量

use Spatie\Sitemap\SitemapGenerator;
 
SitemapGenerator::create('https://example.com')
    ->setMaximumCrawlCount(500) // only the 500 first pages will be crawled
    ...

执行 Javascript

这个站点生成器可以在每个页面中执行 JavaScript,因此它将发现由JS脚本生成的链接。您可以通过将配置文件中的 execute_javascript设置为true来启用此功能。

在底层,它使用[无头 Chrome](https://github.com/spatie/browsershot) 执行 JavaScript。这里有一些如何在你的系统中安装的指示。

 该软件包将对Chrome在您的系统上的安装位置进行有根据的猜测。您还可以手动将Chrome二进制文件的位置传递给 executeJavaScript()

手动添加链接

你也可以手动添加链接到 sitemap:

use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;

SitemapGenerator::create('https://example.com')
    ->getSitemap()
    // here we add one extra link, but you can add as many as you'd like
    ->add(Url::create('/extra-page')->setPriority(0.5))
    ->writeToFile($sitemapPath);

添加备选方案到链接Adding alternates to links

多语言网站可能有多个相同页面的备用版本(每种语言一个)。根据前面的示例,添加备选方案可以按如下方式进行:

use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;

 
SitemapGenerator::create('https://example.com')
    ->getSitemap()
    // here we add one extra link, but you can add as many as you'd like
    ->add(Url::create('/extra-page')->setPriority(0.5)->addAlternate('/extra-pagina', 'nl'))
    ->writeToFile($sitemapPath);

注意 addAlternate 函数需要一个 alternate URL,以及它所属语言。

将图片添加到链接

URL 也可以有图片。

use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
 
Sitemap::create()
    // here we add an image to a URL
    ->add(Url::create('https://example.com')->addImage('https://example.com/images/home.jpg', 'Home page image'))
    ->writeToFile($sitemapPath);

手动创建 sitemap

你也可以完全手动创建 sitemap:

use Carbon\Carbon;

 
Sitemap::create()
   ->add('/page1')
   ->add('/page2')
   ->add(Url::create('/page3')->setLastModificationDate(Carbon::create('2016', '1', '1')))
   ->writeToFile($sitemapPath);

创建 sitemap index

你可以创建 sitemap 索引:

use Spatie\Sitemap\SitemapIndex;

 
SitemapIndex::create()
    ->add('/pages_sitemap.xml')
    ->add('/posts_sitemap.xml')
    ->writeToFile($sitemapIndexPath);

 你可以传入一个 Spatie\Sitemap\Tags\Sitemap 对象,手动设置 lastModificationDate 属性。 

use Spatie\Sitemap\SitemapIndex;
use Spatie\Sitemap\Tags\Sitemap;

 
SitemapIndex::create()
    ->add('/pages_sitemap.xml')
    ->add(Sitemap::create('/posts_sitemap.xml')
        ->setLastModificationDate(Carbon::yesterday()))
    ->writeToFile($sitemapIndexPath);

生成的站点地图索引像这样:

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
   <sitemap>
      <loc>http://www.example.com/pages_sitemap.xml</loc>
      <lastmod>2016-01-01T00:00:00+00:00</lastmod>
   </sitemap>
   <sitemap>
      <loc>http://www.example.com/posts_sitemap.xml</loc>
      <lastmod>2015-12-31T00:00:00+00:00</lastmod>
   </sitemap>
</sitemapindex>

使用后续站点地图创建站点地图索引

你可以调用 maxTagsPerSitemap 方法,取生成只包含给定数量 tag 的 sitemap

use Spatie\Sitemap\SitemapGenerator;

 
SitemapGenerator::create('https://example.com')
    ->maxTagsPerSitemap(20000)
    ->writeToFile(public_path('sitemap.xml'));

频繁生成 sitemap 

你的网站可能会时不时更新。为了让 sitemap 映射这些更新,你可以定期运行这个生成器。最简单的方式是使用 Laravel 默认的任务调度工具。

你可以像这样创建一个 artisan 命令:

namespace App\Console\Commands;

 
use Illuminate\Console\Command;
use Spatie\Sitemap\SitemapGenerator;

 
class GenerateSitemap extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $signature = 'sitemap:generate';

 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate the sitemap.';

 
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // modify this to your own needs
        SitemapGenerator::create(config('app.url'))
            ->writeToFile(public_path('sitemap.xml'));
    }
}
```

 
That command should then be scheduled in the console kernel.

 
```php
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    ...
    $schedule->command('sitemap:generate')->daily();
    ...
}