Javascriptコンテンツ
Googleのクローラーは巡回時にサイトのJavascriptを実行しないらしい。そうすると保存されるコンテンツキャッシュは中身スカスカの状態だ。てなると検索に引っかかりにくくなるし、探されづらくなる。GoogleさんがJSコンテンツをレンダリングしてくれるのがいいと思うんだけど、現状そうなってないのでこちらでクローラー用のコンテンツを用意してあげるとかがある(プリレンダリング)。ほっつい!もそういう感じにしてみたい。
方針
Googleから提供されている情報として、こちらの
https://developers.google.com/search/docs/advanced/javascript/javascript-seo-basics?hl=ja
一連のドキュメントがある。で、この中のダイナミックレンダリングを実装したいと思っていたのだけど、ここで解説されている方法はRendertronを使って静的HTMLファイルを返す方法だ。
https://codelabs.developers.google.com/codelabs/dynamic-rendering
Rendertronは内部にPuppeteerを使用しており、試してないけど説明を見る限りでは1アクセスごとに1レンダリングしてレスポンスを送るような形で実装しているみたい。すでに自分はSeleniumをインストールしているのに加えて、逐次的な実行はサーバーのリソース的に優しくなさそうだなーと思った。ので、リアルタイムではなくなるものの、クローラーの頻度もそんなに高くねーだろと思ったのでプリレンダリングした静的ファイルを用意しておき、botが来たときだけそっちのファイルを見せるような実装にしようと考えた。そうしましょう。
isbotでアクセスがbotのものか否かを判断する
isbotというライブラリを利用させてもらう。
https://github.com/omrilotan/isbot
有志によって作成されているクローラーbotリストをもとにUserAgentと比較してbotかどうかを判定してくれるナイスなライブラリだった。
$ npm i isbot
でインストールできる。そしたらコード内で
const isbot = require('isbot')
と、例えば
app.get('/hotwee', (req, res) => {
if (isbot(req.header('User-Agent'))) {
console.log('bot has come : ' + req.header('User-Agent'))
res.sendFile('/***/rendered.html')
} else {
res.sendFile('/***/index.html')
}
})
みたいな。これでbotからのアクセスの時には別のファイルを渡すという方法が実現できた。ここのrendered.htmlに保存してあげればよいのだな。
レンダリング後のHTMLファイルを保存する
Seleniumによって取得したレンダリング後のHTMLファイルを保存する。
driver.page_source
なのだが、これは現在のページをそのまま保存するやつなので、レンダリングされるまで待たないといけない。レンダリングされたことを取得するそれ用の方法が、調べた限りではなかったので代替方法として、レンダリングされないと出現しない要素を取得するコードを書き
driver.implicitly_wait(time)
で待つ最大値を設定しておくことで実現しようと思う。ただ、だとしても秒数までにレンダリングが終わらなければ取得はできない。…まぁいっか!というか結局time.sleep()で実装しちゃった。これだとブロッキング処理になるので実行中はサイト止まったりすんのかな。。。
# どれが必要でどれがいらないか忘れた。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import chromedriver_binary
import time
options = webdriver.ChromeOptions()
options.add_argument('--headless') # ヘッドレスモード
options.add_argument('--no-sandbox') # サンドボックス使用しない
profile_path = '/root/.config/google-chrome/default' # chromeの個人設定のpath
options.add_argument('--user-data-dir=' + profile_path) # 個人設定を利用するよう設定する
# これ自体がbotと認識されるのを防ぐため適当にUserAgentを設定する
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36')
driver = webdriver.Chrome(options=options) # オプションを適用
driver.set_window_size(1600, 900)
driver.implicitly_wait(15)
driver.get("https://dalomo.net/hotwee")
time.sleep(10)
print(driver.page_source)
driver.quit()
最終的にprintを使ってるので、実行するとコンソール上にソースが表示される。Pythonからファイルに出力する方法を調べてもよかったが、めんどくさくなってしまったので簡単にシェルで出力することにした。つまり
0 10,22 * * * /usr/local/bin/python3 /home/dalomo/hotwee/getsource.py > /home/dalomo/hotwee/public/rendered.html
こうした。リダイレクションというらしい。大なり>を2つ重ねると>>追記する感じになるみたい。ログとかで使ったな。これで保存ができたわけだ。確実性という点ではちょっと劣るがまぁしょーがない。これをcronに登録しておく。
確認
https://search.google.com/test/mobile-friendly?hl=ja
を使ってサイトをテストする。自分のサイトだと
https://search.google.com/test/mobile-friendly/result?id=mUYg46635timlkcvvz0zCw&hl=ja
「テスト済みのページを表示」から確認できる。ちゃんと取得できてるのでおっけーですね。あとはー広告が邪魔くさいので削除できればいいんだけどそれは後回しかな。