Создайте генератор RSS за 2 часа с Laravel

задняя часть GitHub RSS Laravel

Wait no longer! Create RSS feeds for all websites you care about and read them from the comfort of your feed reader.

Сейчас все больше и больше веб-сайтов не поддерживают подписку на RSS, и, как преданный поклонник RSS, я все еще надеюсь, что существует инструмент, который может собирать содержимое веб-сайтов, за которыми я слежу, а затем передавать его на мобильный телефон в режиме реального времени. чтобы получать последние новости и новости в динамике во времени.

Итак, сегодня давайте потратим 2 часа на создание генератора RSS.

Главный герой этой статьи по-прежнему Laravel.

1. Создайте скелет Laravel

Поскольку нам нужен фон для добавления нужных нам веб-сайтов, мы по-прежнему используем плагин laravel-admin.

// 1. 创建 Laravel 5.5版本项目

composer create-project --prefer-dist laravel/laravel:5.5 lrss

cd lrss

cp .env.example .env

php artisan key:generate

// 2. 使用 laravel-admin 插件
composer require encore/laravel-admin "1.5.*"

php artisan vendor:publish --provider="Encore\Admin\AdminServiceProvider"

php artisan admin:install

*Примечание: *В случае возникновения проблемы: SQLSTATE[42000]: синтаксическая ошибка или нарушение прав доступа: 1071 Указанный ключ слишком длинный, максимальная длина ключа составляет 767 байт.

Решение: вAppServiceProvider.phpДобавить длину строки по умолчанию

use Illuminate\Support\Facades\Schema;

public function boot()
{
    Schema::defaultStringLength(191);
}

Как оказалось, мы хотим воспользоваться преимуществами Symfonys.DomCrawlerПлагин для анализа информации xpath веб-сайта, найтиlaravel-adminПлагин представил:

2. Разобрать XPath

Раньше я хотел использовать артефакт Huginn для создания нашего RSS-канала, в основном ссылаясь на статью: Превратите все веб-страницы в RSS - Huginngit.Hu Ginn.cai/docs/%E8%AE…

Но при фактическом использовании обнаруживается, что Huginn не работает без причины, или фоновые задания терпят неудачу на каждом шагу. Вот так у меня появилась идея создать свой собственный инструмент.

Но Хьюгинн вдохновил меня на использование синтаксического анализа XPath для создания RSS-каналов.

Создайте контроллер Xpath

Чтобы проверить точность входной информации XPath, мы можем обратиться к Huginn,

Сначала проверьте действие XPath в Huginn.В интерфейсе Create WebsiteAgent введите следующую информацию:

{
  "expected_update_period_in_days": "2",
  "url": "http://www.woshipm.com/",
  "type": "html",
  "mode": "on_change",
  "extract": {
    "title": {
      "xpath": "//div[@class=\"postlist-item u-clearfix\"]/div[2]/h2/a/text()",
      "value": "normalize-space(.)"
    },
    "desc": {
      "xpath": 
"//div[@class=\"postlist-item u-clearfix\"]/div[2]/p/text()",
      "value": "normalize-space(.)"
    },
    "url": {
      "xpath": "//div[@class=\"postlist-item u-clearfix\"]/div[2]/h2/a",
      "value": "@href"
    }
  }
}

Затем нажмите «Пробный прогон», чтобы проверить:

Наконец, в соответствии с информацией, заполненной Huginn, давайте создадим контроллер Xpath.

// bash
php artisan make:model Xpath -m

// migration
public function up()
{
    Schema::create('xpaths', function (Blueprint $table) {
        $table->increments('id');
        
        // url
        $table->string('url', 250);
        $table->string("urldesc", 250);
        
        // title
        $table->string('titlexpath', 250);
        $table->string('titlevalue', 100)
              ->nullable();

        // desc
        $table->string('descxpath', 250);
        $table->string('descvalue', 100)
               ->nullable();

        // url
        $table->string("preurl", 50)->nullable();
        
        $table->string('urlxpath', 250);
        $table->string('urlvalue', 100)
              ->nullable();
              
        $table->timestamps();
    });
}

// migrate
php artisan migrate

// 创建 admin/Controller
php artisan admin:make XpathController --model=App\\Xpath

// 建立 route
$router->resource('xpaths', XpathController::class);

// 加入到 admin 的 menu 中
// 略

Примечание:Вы можете обратиться к предыдущим статьям:Порекомендуйте плагин управления фоном администратора Laravel

CURD XPath

С плагином laravel-admin легко манипулировать информацией XPath, просто взгляните на код:

<?php

namespace App\Admin\Controllers;

use App\Xpath;

use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Content;
use App\Http\Controllers\Controller;
use Encore\Admin\Controllers\ModelForm;

class XpathController extends Controller
{
    use ModelForm;

    /**
     * Index interface.
     *
     * @return Content
     */
    public function index()
    {
        return Admin::content(function (Content $content) {

            $content->header('header');
            $content->description('description');

            $content->body($this->grid());
        });
    }

    /**
     * Edit interface.
     *
     * @param $id
     * @return Content
     */
    public function edit($id)
    {
        return Admin::content(function (Content $content) use ($id) {

            $content->header('header');
            $content->description('description');

            $content->body($this->form()->edit($id));
        });
    }

    /**
     * Create interface.
     *
     * @return Content
     */
    public function create()
    {
        return Admin::content(function (Content $content) {

            $content->header('header');
            $content->description('description');

            $content->body($this->form());
        });
    }

    /**
     * Make a grid builder.
     *
     * @return Grid
     */
    protected function grid()
    {
        return Admin::grid(Xpath::class, function (Grid $grid) {

            $grid->id('ID')->sortable();

            $grid->column('url');
            $grid->column('urldesc', "描述");

            $grid->column('titlexpath');
            $grid->column('titlevalue');

            $grid->column('descxpath');
            $grid->column('descvalue');

            $grid->column('preurl');
            $grid->column('urlxpath');
            $grid->column('urlvalue');

            $grid->created_at();
            $grid->updated_at();
        });
    }

    /**
     * Make a form builder.
     *
     * @return Form
     */
    protected function form()
    {
        return Admin::form(Xpath::class, function (Form $form) {

            $form->display('id', 'ID');

            // url
            $form->text('url', '链接')
                ->placeholder('请输入解析的网址')
                ->rules('required|min:5|max:250');

            $form->text('urldesc', '一句话描述')
                ->placeholder('一句话描述')
                ->rules('required|min:5|max:250');

            // title
            $form->divide();
            $form->text('titlexpath', 'title xpath')
                ->placeholder('请输入标题 xpath')
                ->rules('required|min:5|max:250');

            $form->text('titlevalue', 'title value 默认可以不填')
                ->default('')
                ->rules('max:100');

            // desc
            $form->divide();
            $form->text('descxpath', 'desc xpath')
                ->placeholder('请输入详情 xpath')
                ->rules('required|min:5|max:250');

            $form->text('descvalue', 'desc value,默认可以不填')
                ->default('')
                ->rules('max:100');

            // url
            $form->divide();
            $form->text('preurl', 'url 前缀')
                ->placeholder('请输入文章的url 前缀')
                ->rules('max:50');

            $form->text('urlxpath', 'url xpath')
                ->placeholder('请输入文章的url xpath')
                ->rules('required|min:5|max:250');

            $form->text('urlvalue', 'url value 默认可以不填')
                ->default('')
                ->rules('max:100');

            $form->divide();
            $form->display('created_at', 'Created At');
            $form->display('updated_at', 'Updated At');
        });
    }
}

Попробуйте добавить две информации о веб-сайте:

XPath к RSS-каналу

1.В соответствии с заполненной информацией Xpath проанализируйте содержимое:

public static function analysis(XpathModel $model) {
    $html = file_get_contents($model->url);

    $crawler = new Crawler($html);

    $titlenodes = $crawler->filterXPath($model->titlexpath);
    $titles = self::getValueByNodes($titlenodes, $model->titlevalue);

    $descnodes = $crawler->filterXPath($model->descxpath);
    $desces = self::getValueByNodes($descnodes, $model->descvalue);

    $urlnodes = $crawler->filterXPath($model->urlxpath);
    $urls = self::getValueByNodes($urlnodes, $model->urlvalue);

    return RssFeeds::feeds($model, $titles, $desces, $urls);
}

// 通过规则获取 nodes 的值
public static function getValueByNodes(Crawler $crawler, $key = null) {
    return $crawler->each(function (Crawler $node) use ($key) {
        if (empty($key)) {
            return trim($node->text());
        } else {
            return $node->attr($key);
        }
    });
}

2.Загрузите полученные массивы title, desc и url в элемент фида, чтобы построить RSS.

public static function feeds(Xpath $xpath, $titles = [], $desces = [], $urls = []) {
    if (!empty($xpath->preurl)) {
        $preurl = $xpath->preurl;
        $urlss = collect($urls)->map(function ($url, $key) use ($preurl) {
            return $preurl.trim($url);
        });
    } else {
        $urlss = collect($urls);
    }
    return response()
        ->view('rss',
        [
            'xpath' => $xpath,
            'titles' => $titles,
            'desces' => $desces,
            'urls' => $urlss->toArray(),
            'pubDate' => Carbon::now()
        ])
        ->header('Content-Type', 'text/xml');
}

3.Напишите шаблон блейда

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
   <channel>
       <title>{{ $xpath->url or ' title' }}</title>
       <description>{{ $xpath->urldesc or '描述' }}</description>
       <link>{{ $xpath->url }}</link>
       <atom:link href="{{ url("/feed/$xpath->id") }}" rel="self" type="application/rss+xml"/>
       <pubDate>{{ $pubDate }}</pubDate>
       <lastBuildDate>{{ $pubDate }}</lastBuildDate>
       <generator>coding01</generator>
       @foreach ($titles as $key => $title)
           <item>
               <title>{{ $title }}</title>
               <link>{{ $urls[$key] }}</link>
               <description>{{ $desces[$key] }}</description>
               <pubDate>{{ $pubDate }}</pubDate>
               <author>coding01</author>
               <guid>{{ $urls[$key] }}</guid>
               <category>{{ $title }}</category>
           </item>
       @endforeach
   </channel>
</rss>

4.Наконец, давайте посмотрим на эффект, создадим RSS для каждого сайта:

RSS-подписка в прямом эфире

На данный момент текущий код Laravel подошел к концу, но для достижения цели своевременной доставки контента на мобильный телефон я использовал два инструмента:

  1. Tiny Tiny RSS
  2. IFTTT + DingTalk

Добавьте подготовленную RSS-ссылку в Tiny Tiny RSS и обновляйте ее каждые полчаса, чтобы получать самый свежий контент:

Затем используйте IFTTT для привязки группового робота DingTalk Webhook:

iftttout

Наконец, вы можете своевременно получать последние новости и информацию на мобильный телефон DingTalk или на ПК:

Суммировать

Сегодня я потратил 2 часа, в основном используя плагины laravel-amin и symfony/dom-crawler для создания демо инструмента генерации RSS-каналов.

Остается оптимизировать в будущем, например,feed43.com/Таким образом, вы можете создавать RSS-каналы, вводя URL-адрес в Интернете, а также устанавливать время обновления в соответствии с фактическими потребностями.

Наконец, код можно разместить на github для ознакомления:github.com/fanly/lrss

"Продолжение следует"