今年も盛り上がっている Qiita Engineer Festa!
Qiita Engineer Festa 2023、大いに盛り上がっていますね・・・!
特に、Organization対抗戦・・・!
いろんな企業さんが、必死になって沢山の記事を書いています。
現在のランキングはこちら
Organization対抗戦: 総合いいね賞ランキング
※7月21日 12:35時点のランキングです。
なんと、弊社(株式会社ゆめみ)も2位につけています。
ただ、気になったことが・・・
ワイ「なんか、ワイが記事を書くと・・・」
ワイ「いつも、同僚たちが沢山いいねをくれる・・・」
ワイ「そのおかげで、いつもデイリートレンドに載ることができてる・・・」
ワイ「これって、ちょっとズルくないか?」
ワイ「組織票ってやつとちゃうか・・・?」
調べてみた
デイリートレンドの30記事を対象に「同僚からのいいね1が何%を占めているのか」を調べるブックマークレットを作成してみました!
コード
ブックマークレット用JavaScript(長いので折りたたんであります)
javascript: (async () => {
const cache = {};
const addQueryToUrl = (url, params) => {
const urlObj = new URL(url);
const paramsObj = new URLSearchParams(urlObj.search.slice(1));
for (const key in params) {
paramsObj.set(key, params[key]);
}
urlObj.search = paramsObj.toString();
return urlObj.toString();
};
const getPageDoc = async (link, query = {}) => {
const url = addQueryToUrl(link, query);
if (url in cache) return cache[url];
await sleep(500);
const response = await fetch(url);
const data = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html');
cache[url] = doc;
return doc;
};
const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
const articles = Array.from(document.querySelectorAll("main article"));
const getAllPageIds = async (pageUrl, querySelectorString) => {
let page = 1;
let hasPage = true;
let allIds = [];
while (hasPage) {
const doc = await getPageDoc(pageUrl, { page });
const docIds = Array.from(doc.querySelectorAll(querySelectorString)).map(({ href }) => href.replace("https://qiita.com/", ""));
allIds = [...allIds, ...docIds];
hasPage = docIds.length !== 0;
page++;
}
return allIds;
};
const getOrgMemberIds = async (orgMembersPageHref) => {
return await getAllPageIds(orgMembersPageHref, "main ul li > a")
};
const getLikerIds = async (likersPageHref) => {
return await getAllPageIds(likersPageHref, "[id^=PersonalArticleLikersPage] li > a")
};
const getSelfLikeIds = (likerIds, orgMemberIds) => {
return likerIds.filter(likerId => orgMemberIds.includes(likerId));
};
for (const article of articles) {
const orgPageLink = article.querySelector("[href^='/organizations']");
if (!orgPageLink) continue;
const messageFrame = document.createElement("div");
messageFrame.classList.add("messageFrame");
messageFrame.style.position = "relative";
messageFrame.style.border = "1px solid #aaa";
messageFrame.style.backgroundColor = "#aaa";
messageFrame.style.color = "white";
messageFrame.style.marginTop = "10px";
messageFrame.style.padding = "5px 10px";
messageFrame.style.fontWeight = "bold";
messageFrame.innerHTML = "組織票を集計中...(数十秒かかります)";
article.appendChild(messageFrame);
}
for (const article of articles) {
const orgPageLink = article.querySelector("[href^='/organizations']");
if (!orgPageLink) continue;
const orgPageHref = orgPageLink.href;
const orgMembersPageHref = `${orgPageHref}/members`;
const orgMemberIds = await getOrgMemberIds(orgMembersPageHref);
const articleLink = article.querySelector("main article > a");
if (!articleLink) continue;
const articleLikersHref = `${articleLink.href}/likers`;
const likerIds = await getLikerIds(articleLikersHref);
const selfLikeIds = getSelfLikeIds(likerIds, orgMemberIds);
const percent = Math.round(selfLikeIds.length / likerIds.length * 100);
const message = document.createElement("div");
message.style.position = "relative";
message.style.zIndex = "1";
message.innerHTML = `組織票: ${selfLikeIds.length} 票(${percent} %)`;
const meter = document.createElement("div");
meter.style.position = "absolute";
meter.style.transition = "width 0.5s";
meter.style.backgroundColor = "red";
meter.style.opacity = "0.3";
meter.style.height = "100%";
meter.style.left = "0";
meter.style.top = "0";
meter.style.width = "0%";
const messageFrame = article.querySelector(".messageFrame");
if (!messageFrame) continue;
messageFrame.style.border = "1px solid #ffbdbd";
messageFrame.style.backgroundColor = "transparent";
messageFrame.style.color = "black";
messageFrame.innerHTML = "";
messageFrame.appendChild(meter);
messageFrame.appendChild(message);
setTimeout(() => meter.style.width = `${percent}%`, 100);
};
})();
ブックマークレットの登録方法
- ブラウザで適当なページをブックマークする
- そのブックマークの編集画面を開く
- URLの入力欄に、上記のJavaScriptコードを貼り付ける
- ブックマークを保存する
ブックマークレットの実行方法
- Qiitaのデイリートレンドページを開く
- 上で作ったブックマークをクリックする
- 数十秒待つ(サイトに負荷がかからないように、ゆっくりの挙動にしてあります)
検証してみる
早速ブックマークレットを実行してみました。
以下が実行結果のスクショ(抜粋)です。
検証結果
やっていた。2
今日のデイリートレンドには、ワイの記事が2つ載っていましたが、それぞれ───
- 14票(23%)
- 12票(34%)
↑このような組織投票がされていました。
デイリートレンドの他の記事たちも、かなり組織票が多かったです。
同僚からのいいねが 75% という記事もありました。
(日によっては、100%組織票でトレンド入りしている記事も…)
何が問題なのか
- 組織票でいいねを稼いだ記事たちが、デイリートレンドを埋め尽くしてしまう
- 本当に人気のある記事が、デイリートレンドに載らなくなってしまう
- 普段から熱心に記事を書いている人が、トレンドから弾き出されてしまう
- デイリートレンドが「同僚からいいねしてもらった人ランキング」になっている
現在はOrganization対抗戦が開催中ということもあり、特に組織票が多いようです。
Qiitaへの提言
同Organization同士でのいいねは、ノーカウントにしませんか?
Qiitaさんのミッションは「エンジニアを最高に幸せにする」のはずです。
こんな八百長ランキングを見せられても、誰も幸せになりません。
また、Qiitaのガイドラインには、こう書いてあります。
エンジニアにとって再利用性・汎用性の高い情報が集まる場をつくろう
「八百長いいねが集まる場をつくろう」ではないはずです。
最後に
仲間が頑張って書いた記事に「いいね」をすることは、何も悪いと思いません。
僕も、同僚の記事には積極的に「いいね」をします。
あと「絶対にOrganization対抗戦で優勝してやる!」という気持ちも、悪いと思いません。
そういった気持ち自体は、人生の中で大事なものだと思います。
ですが、その結果として取るべき行動は───
「みんながいいねを押したくなるような記事を書いてやる!」
↑こうあるべきだと思います。
「同僚同士でいいね押しまくってやる!」
↑こうあるべきではないと思います。