これは琉大 Advent Calendar 2020の5日目の記事です。
昨日はid:anatofuzさんのvirshの学科ラッパーie-virshをRustで再実装したでした。
まだまだ琉大アドベントカレンダーは空きがあるので皆さんの参加お待ちしております!!!
学科サイトのリプレイス
以前こちらのエントリで書いたように、今までWordPressで動作していた学科サイトを今年のシステム更新に合わせて静的サイト化しました!
このエントリはその際のログとなっています
いきなりのhugo化 -> 断念
静的サイト化する場合直接HTMLを編集しないといけなくなるのは避けたいところです。 そのためmarkdownなどからHTMLを生成する静的サイトジェネレーターを使って運用を行いたいと考えました。
システム管理チームの1年生にwebの練習としてhugoテーマを作成してもらおうと思いましたが、難易度が高すぎたようで結局学科システムの更新タイミングまでテーマ作成が間に合いませんでした。 しかしシステム更新後はWordPressを動かしてはいけないというレギュレーションになっていたため、とりあえず現状のWordPressコンテンツをHTMLに変換するという方向で作業を行いました。
wordpressの静的サイト化プラグイン
前回のエントリで紹介したhugo形式のmarkdownにWordPressのコンテンツを変換してくれるプラグインですが、学科サイトではプラグインの問題か変換が出来ませんでした。
似たようなツールにWP2Staticが存在しますが、こちらも件数が多すぎて学科サイトは変換できませんでした。
これらのプラグインは小規模なWordPressサイトでは変換が可能なのですが、学科サイトは様々な問題がありこれらのプラグインが使えなかったのです。
学科サイトのつらみ
学科サイトと読んでいるものは https://ie.u-ryukyu.ac.jp
のサイトです。
主に受験生・外部の方向けの https://ie.u-ryukyu.ac.jp/prospective
の様なパスと、学科の学生・スタッフ向けの https://ie.u-ryukyu.ac.jp/students
のパスが大まかなコンテンツパスとなっています。
それだけなら変換は楽そうなのですが、実は学科サイトは https://ie.u-ryukyu.ac.jp/e155730
の様に学籍番号をURLにつけると、マルチサイト化された各学生のWordPressページが使われるように設定されています。
同様にnewsサイトはhttps://ie.u-ryukyu.ac.jp/news-ie
になっていたり、各講義で使っていたページなども存在するため、ざっと2000件ほどのWordPressサイトが存在しています。
しかもこれらWordPressサイトは、歴史的な理由で数年前からマルチサイト化されており、それ以前はすべて同じWordPressサイトとして運用されていました。そのため、学科トップページを静的化しようとすると、これらのほかユーザーなどのURLもプラグインが同時に変換しに行くため、変換プラグインの負荷がかかり、基本的に変換できないという状況になっていました。
気合で静的サイト化する
プラグインが使えないなら気合でどうにかするしかありません。幸いWP2Static
はページを生成するところまではしませんでしたが、ページのURLをクロールして発見してくれました。
発見してくれたパスの一覧はこんな感じです。
Pending /robots.txt Note: initial_crawl_list Pending /sitemap/ Note: initial_crawl_list Pending /sitemap.xml Note: initial_crawl_list Pending /students/ Note: initial_crawl_list Pending /students/get-a-job/ Note: initial_crawl_list Pending /students/graduate/ Note: initial_crawl_list Pending /students/graduate/%e5%a4%a7%e5%ad%a6%e9%99%a2%e4%bf%ae%e5%ad%a6%e3%81%ae%e6%89%8b%e5%bc%95%e3%81%8d/ Note: initial_crawl_list Pending /students/graduate/%e5%a4%a7%e5%ad%a6%e9%99%a2%e5%85%a5%e8%a9%a6%e3%82%a2%e3%83%89%e3%83%9f%e3%83%83%e3%82%b7%e3%83%a7%e3%83%b3%e3%83%9d%e3%83%aa%e3%82%b7%e3%83%bc/ Note: initial_crawl_list Pending /students/graduate/%e5%a4%a7%e5%ad%a6%e9%99%a2%e7%a0%94%e7%a9%b6%e8%a8%88%e7%94%bb%e6%9b%b8/ Note: initial_crawl_list Pending /students/graduate-students/ Note: discovered on: / Pending /students/graduate-students2019/ Note: initial_crawl_list Pending /students/graduation-study/ Note: initial_crawl_list Pending /students/info-ethics-video/ Note: initial_crawl_list Pending /students/install/ Note: initial_crawl_list Pending /students/install/event-calendar/ Note: initial_crawl_list Pending /students/install/forward-email/ Note: initial_crawl_list Pending /students/install/homebrew/ Note: initial_crawl_list Pending /students/install/ip-address/ Note: initial_crawl_list Pending /students/install/mail/ Note: initial_crawl_list Pending /students/install/mercurial/ Note: initial_crawl_list Pending /students/install/newsie/ Note: initial_crawl_list Pending /students/install/printer/ Note: initial_crawl_list Pending /students/install/rss-2/ Note: initial_crawl_list Pending /students/install/server-access/ Note: initial_crawl_list Pending /students/install/web-server/ Note: initial_crawl_list Pending /students/install/wifi/ Note: initial_crawl_list Pending /students/office-hour/ Note: initial_crawl_list Pending /students/rental/ Note: initial_crawl_list Pending /students/request/ Note: initial_crawl_list Pending /students/request/guest-id/ Note: initial_crawl_list Pending /students/request/guest-wifi/ Note: initial_crawl_list Pending /students/request/vm/ Note: initial_crawl_list Pending /students/security-policy/ Note: initial_crawl_list Pending /students/software/ Note: initial_crawl_list Pending /students/study-guide/ Note: initial_crawl_list Pending /students/study-teaching-licence/ Note: initial_crawl_list Pending /students/study-teaching-licence/risyuu/ Note: initial_crawl_list Pending /students/study-teaching-licence/risyuu/%e6%95%99%e5%93%a1%e5%85%8d%e8%a8%b1%e3%82%b3%e3%83%bc%e3%82%b9/ Note: initial_crawl_list Pending /students/study-teaching-licence/risyuu/%e7%9f%a5%e8%83%bd%e6%83%85%e5%a0%b1%e3%82%b3%e3%83%bc%e3%82%b9%e5%b1%a5%e4%bf%ae%e8%a6%8f%e5%ae%9a/ Note: initial_crawl_list Pending /students/study-teaching-licence/risyuu/%e7%9f%a5%e8%83%bd%e6%83%85%e5%a0%b1%e3%82%b3%e3%83%bc%e3%82%b9%e5%b1%a5%e4%bf%ae%e8%a8%88%e7%94%bb%e8%a1%a8/ Note: initial_crawl_list Pending /students/study-teaching-licence/risyuu/2017%e5%b9%b4%e5%ba%a6-%e5%be%8c%e6%9c%9f/ Note: initial_crawl_list Pending /students/study-teaching-licence/shinro/ Note: initial_crawl_list Pending /students/study-teaching-licence/shinro/%e3%82%a4%e3%83%b3%e3%82%bf%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%83%e3%83%97/ Note: initial_crawl_list Pending /students/timetable/ Note: initial_crawl_list Pending /students/timetable/2018-latter/ Note: initial_crawl_list Pending /students/timetable/2018-previous/ Note: initial_crawl_list Pending /students/timetable/2019-latter/ Note: initial_crawl_list Pending /students/timetable/2019-previous/ Note: initial_crawl_list Pending /students/timetable/2020-previous/ Note: initial_crawl_list Pending /survey-ob/ Note: discovered on: /%e5%ad%a6%e5%86%85%e5%90%91%e3%81%91/ Pending /tag/%e3%82%af%e3%83%a9%e3%82%b9%e3%82%bf/ Note: initial_crawl_list Pending /tag/%e3%83%a1%e3%83%bc%e3%83%ab/ Note: initial_crawl_list Pending /tag/%e3%83%aa%e3%83%b3%e3%82%af%e9%9b%86/ Note: initial_crawl_list Pending /tag/%e3%83%ad%e3%82%b0%e3%82%a4%e3%83%b3%e3%82%b7%e3%82%a7%e3%83%ab/ Note: initial_crawl_list Pending /tag/%e4%bb%ae%e6%83%b3os/ Note: initial_crawl_list Pending /tag/%e5%89%b2%e3%82%8a%e5%bd%93%e3%81%a6/ Note: initial_crawl_list Pending /tag/%e6%89%80%e5%b1%9evlan/ Note: initial_crawl_list Pending /tag/bashou/ Note: initial_crawl_list Pending /tag/editor/ Note: initial_crawl_list Pending /tag/emacs/ Note: initial_crawl_list Pending /tag/imac/ Note: initial_crawl_list Pending /tag/mac/ Note: initial_crawl_list Pending /tag/procmailrc/ Note: initial_crawl_list Pending /tag/spam/ Note: initial_crawl_list Pending /tag/spamassassin/ Note: initial_crawl_list Pending /tag/tcshrc/ Note: initial_crawl_list Pending /tag/torque/ Note: initial_crawl_list Pending /tag/vi/ Note: initial_crawl_list Pending /tag/xen/ Note: initial_crawl_list Pending /wp-admin/ Note: discovered on: / Pending /wp-content/plugins/contact-form-7/includes/js/scripts.js Note: discovered on: / Pending /wp-content/plugins/wc-shortcodes/public/assets/css/font-awesome.min.css Note: discovered on: / Pending /wp-content/plugins/wc-shortcodes/public/assets/css/style.css Note: discovered on: / Pending /wp-content/plugins/wc-shortcodes/public/assets/js/rsvp.js Note: discovered on: / Pending /wp-content/plugins/wp-browsing-history-master/jquery.cookie.js Note: discovered on: / Pending /wp-content/plugins/wp-browsing-history-master/wp-browsing-history-display.js Note: discovered on: / Pending /wp-content/plugins/wp-browsing-history-master/wp-browsing-history-logging.js Note: discovered on: / Pending /wp-content/plugins/wp-browsing-history-master/wp-browsing-history-variables.js Note: discovered on: / Pending /wp-content/plugins/wp-browsing-history-master/wp-browsing-history.css Note: discovered on: / Pending /wp-content/plugins/wp-rss-aggregator/css/colorbox.css Note: discovered on: / Pending /wp-content/plugins/wp-rss-aggregator/css/styles.css Note: discovered on: / Pending /wp-content/plugins/wp-rss-aggregator/js/custom.js Note: discovered on: / Pending /wp-content/plugins/wp-rss-aggregator/js/jquery.colorbox-min.js Note: discovered on: / Pending /wp-content/plugins/wp-to-twitter/css/twitter-feed.css Note: discovered on: / Pending /wp-content/themes/education-booster/assets/js/main.js Note: discovered on: / Pending /wp-content/themes/education-booster/assets/vendors/bootstrap/css/bootstrap.min.css Note: discovered on: / Pending /wp-content/themes/education-booster/assets/vendors/bootstrap/js/bootstrap.min.js Note: discovered on: / Pending /wp-content/themes/education-booster/assets/vendors/kf-icons/css/style.css Note: discovered on: / Pending /wp-content/themes/education-booster/assets/vendors/OwlCarousel2-2.2.1/assets/owl.carousel.min.css Note: discovered on: / Pending /wp-content/themes/education-booster/assets/vendors/OwlCarousel2-2.2.1/assets/owl.theme.default.min.css Note: discovered on: / Pending /wp-content/themes/education-booster/assets/vendors/OwlCarousel2-2.2.1/owl.carousel.min.js Note: discovered on: / Pending /wp-content/themes/education-booster/style.css Note: discovered on: / Pending /wp-content/themes/education-booster-child/style.css Note: discovered on: / Pending /wp-includes/js/imagesloaded.min.js Note: discovered on: / Pending /wp-includes/js/jquery/jquery-migrate.min.js Note: discovered on: / Pending /wp-includes/js/jquery/jquery.js Note: discovered on: / Pending /wp-includes/js/jquery/jquery.masonry.min.js Note: discovered on: / Pending /wp-includes/js/masonry.min.js Note: discovered on: / Pending /wp-json/ Note: discovered on: / Pending /wp-json/oembed/1.0/embed Note: discovered on: /
なんかデバッグ情報が付与されているので、perlスクリプトを書いて解決しました。
Perlだと__DATA__
以下に書いたテキストをwhile (my $line = <DATA>)
の用にして取得できるので便利です。
この結果こんな感じのパスが生成されました
students/install/newsie students/install/printer students/install/rss-2 students/install/server-access students/install/web-server students/install/wifi students/office-hour students/rental students/request students/request/guest-id students/request/guest-wifi students/request/vm students/security-policy students/software students/study-guide students/study-teaching-licence students/study-teaching-licence/risyuu students/study-teaching-licence/risyuu/%e6%95%99%e5%93%a1%e5%85%8d%e8%a8%b1%e3%82%b3%e3%83%bc%e3%82%b9 students/study-teaching-licence/risyuu/%e7%9f%a5%e8%83%bd%e6%83%85%e5%a0%b1%e3%82%b3%e3%83%bc%e3%82%b9%e5%b1%a5%e4%bf%ae%e8%a6%8f%e5%ae%9a students/study-teaching-licence/risyuu/%e7%9f%a5%e8%83%bd%e6%83%85%e5%a0%b1%e3%82%b3%e3%83%bc%e3%82%b9%e5%b1%a5%e4%bf%ae%e8%a8%88%e7%94%bb%e8%a1%a8 students/study-teaching-licence/risyuu/2017%e5%b9%b4%e5%ba%a6-%e5%be%8c%e6%9c%9f students/study-teaching-licence/shinro students/study-teaching-licence/shinro/%e3%82%a4%e3%83%b3%e3%82%bf%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%83%e3%83%97 students/timetable students/timetable/2018-latter students/timetable/2018-previous students/timetable/2019-latter students/timetable/2019-previous students/timetable/2020-previous survey-ob tag/%e3%82%af%e3%83%a9%e3%82%b9%e3%82%bf tag/%e3%83%a1%e3%83%bc%e3%83%ab tag/%e3%83%aa%e3%83%b3%e3%82%af%e9%9b%86
このパスをもとにひたすらwget
的なことをして、HTMLをダウンロードしてくれば人まずは静的化できそうです。
このURL一覧に画像やPDFファイルなどは含まれていませんが、それはこのURLから落としてきたHTMLをパースして発見すれば良いでしょう。
ダウンローダーの実装
なんとなくgoroutineを使って解決したかったのでgolangを選択しました。
脳死でgoroutineを書くとファイルディスクリプタを食い尽くして死ぬので、CPUのコア数あたりを見ていい感じにgoroutineの数を抑制しています。
wg := &sync.WaitGroup{} cpus := runtime.NumCPU() semaphore := make(chan bool, cpus) ctx := context.Background()
基本的には先程のパスを与えてあげるとダウンロード出来るように仕込んでいます。
HTMLからcssや画像ファイル, javascriptを回収するのは2段階で行っています。
(なんでこんなにクソ面倒なことをしているかというと、生成するファイルパスをgolangでURIから変更するのが面倒だったためです...)
#!/usr/bin/env perl use strict; use warnings; use JSON::PP qw/encode_json/; use utf8; use File::Find qw/find/; find({wanted => \&wanted, no_chdir => 1}, 'ie.u-ryukyu.ac.jp'); my $urls; use DDP {deparse => 1}; sub wanted { my $filename = $_; if ($filename !~ /\.html/) { return; } open my $fh, '<:utf8', $filename; while (my $line = <$fh>) { if ($line =~ m|text/javascript.*src=(['"])(https.*?)\1|) { unless (exists $urls->{js}->{$2}) { $urls->{js}->{$2} =1; } next; } # if ($line =~ /stylesheet.*href=(['"])(http.*?)\1/ ) { # unless (exists $urls->{css}->{$2}) { # $urls->{css}->{$2} =1; # } # next; # } # if ($line =~ /img.*src=(['"])(http.*?)\1/) { # unless (exists $urls->{img}->{$2}) { # $urls->{img}->{$2} =1; # } # next; # } # #<a href="https://ie.u-ryukyu.ac.jp/files/2015/04/%E5%B7%A5%E5%AD%A6%E9%83%A8%E5%AE%89%E5%85%A8%E8%A1%9B%E7%94%9F%E3%83%9E%E3%83%8B%E3%83%A5%E3%82%A2%E3%83%AB.pdf" target="_blank" rel="noopener">工学部 安全衛生マニュアル(PDF)</a> # while ($line =~ m|href=(['"])(https?://.*/files/.*?)\1|g) { # unless (exists $urls->{files}->{$2}) { # $urls->{files}->{$2} =1; # } # next; # } } } my $outputJsonData = {}; for my $key (qw/js/) { for my $url (sort keys %{$urls->{$key}}) { my $filePATH = $url; $filePATH =~ s|https?://||; my $outputDir = $filePATH; $outputDir =~ s|(.+)/(.+)$|$1|; push(@{$outputJsonData->{$key}}, {url => $url, filePATH => $filePATH, outputDir => $outputDir}); } } #$outputJsonData->{css} = [ sort ]; #$outputJsonData->{img} = [ sort keys %{$urls->{img}}]; # print encode_json($outputJsonData);
パスの相対パス化
取得したこれらのコンテンツですが、WordPressは仕様でリンクをすべて絶対URLで作成するので、ローカルでCSSや画像のテストが出来ません。
今回はPerlスクリプトを用いてs|https://ie.u-ryukyu.ac.jp||g
のようなことを行い、相対パスに変換しています。こうすると、変換したあとはlocalでwebサーバーを立てて確認する事が可能となります。
テスト用のwebサーバーは当初psgiで実装しましたが、他の人が使いそうなのでgolangで書き直しました。
use Plack::Builder; builder { enable "Plack::Middleware::Static", path => sub { s!/([^\.]+)$!/$1/!; s!(.*/$)!$1/index.html! or return qr{^/.+}; }, content_type => sub { my $file = shift; my $content_type = Plack::MIME->mime_type($file) || 'text/plain'; if ($content_type eq 'application/atom+xml') { $content_type .= "; charset=utf-8"; } return $content_type; }, root => './ie.u-ryukyu.ac.jp/'; };
ということで学科サイトはひとまず静的化が完了しました。
静的化したため、学科のGitLabや、GitHubにgitリポジトリ化したサイトをcloneするだけでバックアップになるので、極めて便利です。さらにwebサーバーをapacheからnginxに乗り換えたため、スピードが大分向上しました。
早くなった学科サイトですが、いくつか課題があります
このあたりもシステム管理チームで行っているので、webやってみたい人がいたらぜひシス管に入りましょう!!!!
明日はYoshiaki Sanoさんで「 ニューラルネットワークの仕組みと実装の解説」です。お楽しみに!!