わたモテ13巻「遠足編」発売記念 もこっちとくっつくのは誰だ!?選手権 | 電子書籍ストア-BOOK☆WALKER
が目に入ってしましい、一時間ごとのリアルタイム更新では目が離せない!
という言い訳を得まして。
今回はRubyでスクレイピングしてSlackで監視するとともに、
なんとなくAPIを使いQiitaの記事の更新を試してみることにしました。
7月29日までの期間限定デス。
動作環境
- docker ruby:latest
- ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
- nokogiri (1.8.4)
- slack-incoming-webhooks (0.2.0)
- qiita (1.3.5)
成果物
Slack通知
普通にPOSTするとURLが多すぎて展開されませんでした。
attachmentのtextでもURL画像が展開されず、authorとimageで無理やり2つの画像を詰め込むように。
たくさん画像を表示させたいときが課題ですね…
Qiita
RubyのNokogiriでスクレイピングしてSlackに通知&Qiita記事更新 - Qiita
コピーしたprivate記事
コード
require 'nokogiri'
require 'open-uri'
require 'qiita'
require 'slack/incoming/webhooks'
# Qiita投稿前に書き換えるものはグローバル変数にしておこう(悪手)
$incoming_url = 'secret'
$qiita_token = 'secret'
$qiita_item_id = '9f34f79aee34f5fc56ed'
def scraping()
charset = nil
url = 'https://bookwalker.jp/ex/feature/watamote-vote/'
html = open(url) do |f|
charset = f.charset
f.read
end
members = []
doc = Nokogiri::HTML.parse(html, nil, charset)
doc.css('.vote-box').each do |vote|
members.push({
rank: url + vote.css('.rank-box>img').first.attributes['src'].value,
name: vote.css('.border').first.attributes['alt'].value,
icon: icon = url + vote.css('.border').first.attributes['src'].value,
})
end
members
end
def makeAttachments(members)
attachments = []
members.each do |member|
attachments.push({
author_name: member[:name],
image_url: member[:icon],
author_icon: member[:rank],
})
end
attachments
end
def postSlack(attachments)
incoming_url = $incoming_url
channel = '#hitorigoto'
username = 'bot'
slack = Slack::Incoming::Webhooks.new incoming_url, channel: channel, username: username, attachments: attachments
slack.attachments = attachments
slack.post "もこっちとくっつくのは誰だ!?選手権\nhttps://bookwalker.jp/ex/feature/watamote-vote/"
end
def postQiita(body)
title = 'RubyのNokogiriでスクレイピングしてSlackに通知&Qiita記事更新'
client = Qiita::Client.new(access_token: $qiita_token)
client.patch('/api/v2/items/' + $qiita_item_id, title: title, body: body)
end
def makeBody(members)
ranking = <<~EOT
| ランク | エントリー |
|:-:|:-:|
EOT
members.each do |member|
ranking = ranking + "|<img src=\"#{member[:rank]}\">|<img src=\"#{member[:icon]}\">|\n"
end
ranking
client = Qiita::Client.new(access_token: $qiita_token)
body = client.get_item($qiita_item_id).body['body']
body.gsub(/(?<=\*{10}\n).*/m, ranking)
end
# rubyはメソッドを先に宣言しておかないといけない
members = scraping()
attachments = makeAttachments(members)
postSlack(attachments)
body = makeBody(members)
#p body
res = postQiita(body)
#p res
rubyは初心者ですが、コピペでなんとかなりました。
ハッシュの文字列とシンボルはショートハンド含めて混乱のもとですね。
当初はこの記事まるごとコードに持たせるつもりでしたが、記事取得の置換で書き換えるようにしました。
参考記事
更にややこしいことに、HashのKeyにSymbolを使う場合、KeyとValueの紐付けに
=>
(ハッシュロケット)を使わず以下のようなシンプルな表記が可能になります。
記載はシンプルになって見やすいのですが、この表記法を知らない人には混乱のタネになります。また、あくまでも紐付けの表記が変わるだけで、値を取り出す際のリテラルは変わらないことにご注意ください。
ツラミ!
m
がマルチラインモードじゃないのですねえ。
じゃあ^$
がどうなるかというと、普段から各行を表し、ファイル先頭・末尾は\A\z(\Z)
を使うようです。
わざわざm
つかわないほうが混乱しないのかも。
こういうのもあるらしい。Rubyはなかなか深そうですね…
- increments/qiita-rb: Qiita API v2 client library and CLI tool, written in Ruby
- shoyan/slack-incoming-webhooks: A simple wrapper of slack incoming webhooks in Ruby.
- Ruby + Nokogiriでスクレイピング - Qiita
-
Nokogiriでスクレイピング - Qiita
xpathは苦手だったのでCSSで書けるのは助かります。
cronがrequire cannot load such file
エラーを吐いていので、crontab
にGEM_HOME="/usr/local/bundle"
を追加しました。
知見
QiitaのPatchは差分更新
APIでの更新は、毎回タグや限定公開状況も含め、完璧な記事パラメーターにしないとダメだと思っていました。
しかし、渡したパラメーターだけが更新され、渡されなかったものは最初の投稿時に決めればプログラム側で持つ必要がないことがわかりました。
(タグパラメーターの渡し方がわからずずっとbad requestでてたので助かりました)
ただし、タイトルと本文は必須
ここでかなり詰まったのですが、JSONにはtitle
とbody
を含めなければ、Bad request
になるようです。
更新したいものは本文だけだったので、かなり困りました。
APIを見ても、必須指定の情報を見いだせなかったです。(雑に見ていたので見落としかもしれません(必須|必要で検索してみたけど…))
無変更なら非更新
毎回bodyとtitleを渡す必要がありますが、もともとの内容と差異がなければ更新自体が行われませんでした。
ちゃんと動いているか不安になる場合もあるかもしれませんが、
履歴が無駄に汚れないので良いです。
これも差分更新という言い方であってるかな?
Qiitaは直リン不可
img
タグで画像を表示していますが、投稿時するとQiita側に自動でアップロードされるようです。
もともとのsrc
属性値はdata-canonical-src
として保存されるようです。
多分アップロードがスマート
画像を含む更新を頻繁にしていれば、月のアップロード上限に引っかかるのではないか?
と不安になりましたが、画像以外の部分を多少変えた更新では
実際の画像src
= https://camo.qiitausercontent.com/~
が不変でしたので、再度アップロードせずに前回の画像を使いまわしてくれている気がします。
ただし、それだけで枠消費を回避しているのかはわかりませんが…要経過観察。
関係ない疑問
限定共有投稿内のQiitaリンクやメンションは通知されるのかしらん。
private記事なら↓↓にランキングが挿入・更新される予定(これランキングほぼ不動のやつや)