利用 Laravel 花 2 小时撸一个 RSS 生成器
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-damin 插件。
// 1. 創建 Laravel 5.5版本項目composer create-project --prefer-dist laravel/laravel:5.5 lrsscd lrsscp .env.example .envphp 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]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
解決方案:在 AppServiceProvider.php 加入默認字符串長度
use Illuminate\Support\Facades\Schema;public function boot() {Schema::defaultStringLength(191); }剛巧,我們想借助 Symfonys 提供的 DomCrawler 插件,來解析網站的 xpath 信息,發現 laravel-admin插件有引入:
2. 解析 XPath
之前有想借助 huginn 這個神器來生成我們的 RSS Feed,主要參看文章:讓所有網頁變成RSS —— Huginn http://git.huginn.cn/docs/%E8%AE%A9%E6%89%80%E6%9C%89%E7%BD%91%E9%A1%B5%E5%8F%98%E6%88%90RSS%E2%80%94%E2%80%94Huginn.html
但在實際使用中,發現一直出現 Huginn 無故宕機,或者后臺 jobs 動不動就失敗。這才有了自己擼個工具的想法。
但 Huginn 給了我靈感,可以利用解析 XPath 來生成 RSS Feed。
創建 Xpath 控制器
為了驗證輸入的 XPath 信息的準確性,我們可以參考 Huginn,
首先在 Huginn 測試 XPath 的效果,在創建 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"}} }然后點 「Dry Run」即可測試:
最后根據 Huginn 填入的信息,我們來創建 Xpath Controller
// 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 admin 后臺管理插件
CURD XPath
有了 laravel-admin 插件,操作 XPath 信息就好簡單了,直接看代碼:
<?phpnamespace 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 Feed
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 數組裝入 Feed Item 中,構建 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. 編寫 blade 模板
<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 代碼告一段落了,但為了達到及時推送內容到手機的目標,我借助了兩個工具:
把制作好的 RSS 鏈接加入 Tiny Tiny RSS 上,每隔半個小時,更新一次,獲取最新的內容:
然后借助 IFTTT 綁定釘釘的群機器人 Webhook:
最后在手機釘釘或者在 PC 上就能及時收到最新資訊和信息了:
總結
今天花了 2 個小時,主要是借助 laravel-amin 和 symfony/dom-crawler 插件來自己動手搭建一個 RSS Feed 生成工具Demo。
接下來還有待于繼續優化,如向 https://feed43.com/ 那樣,輸入 Web URL 就能生成 RSS Feed,又能根據實際需要自己設定更新時間等。
最后,代碼已放在 github 上,可供參考: https://github.com/fanly/lrss
「未完待續」
總結
以上是生活随笔為你收集整理的利用 Laravel 花 2 小时撸一个 RSS 生成器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在浏览器中进行深度学习:TensorFl
- 下一篇: java基础-Eclipse开发工具介绍