野獣大百科の編集方針及びテンプレート集

@korosu_gmnt / 更新: 2026/01/04 06:43

私が記事を編集する際の方針とよく使うテンプレートを交えて備忘録として残します
内容は少しずつ増やしていきます

HTML 8.53KB
4

基本的な執筆方針


  • 他のウィキに倣った記事形式

    本文は基本的に常体を、閲覧者への誘導やお知らせは敬語を用います。ただし淫夢語録はアクセントとしてたまに入れます。

  • 魚拓は必ず残す

    投稿を引用する場合は魚拓を保存し、元URLとともに記載します。

  • 画像は多用しない

    TorやVPNなど通信環境が高くないユーザーの比率が大きくなりやすい場所のため基本的に文字のみ、画像を載せる場合はaltだけでなく文章による説明を含めます。

  • 装飾は控えめ

    読みやすさと理解しやすさを重視しシンプルな構成を目指します。

投稿の引用


SNSや掲示板の投稿を引用するときはBootstrapのテンプレートを使っています。投稿内容をユーザー名や投稿日時等を含めてそのままコピペし、最小限の整形で記載します。ただしTorやVPN経由でアクセスすると出口国の標準時になってしまうので日本標準時に戻すこと。魚拓も取得しそのリンクも付記します。

本文と区別をつけやすいように両幅に余白を設け、h6にして詰めています。

<div class="card h6 m-4">
    <ul class="list-group list-group-flush">
        <li class="list-group-item bg-light"><a href="ページURL" target="_blank" rel="noopener noreferrer">ページのタイトル</a>(<a href="魚拓URL" target="_blank" rel="noopener noreferrer">魚拓</a>)</li>
        <li class="list-group-item">
            <p>投稿1</p>
        </li>
        <li class="list-group-item">
            <p>投稿2</p>
        </li>
    </ul>
</div>

魚拓によるページ保存


上述の通り、リンク切れや改変対策のため外部サイトを参照する場合は必ず魚拓(ウェブアーカイブ)を取得します。私が基本的に用いている魚拓保存サイトはarchive.todayです。詳細な解説は省きますが、比較的簡単に保存できるため多用しています。

有名なサービスのため多くのブラウザ拡張機能が存在し、有名なものだと現在閲覧しているページの魚拓の有無を、ほかの魚拓サービスとまとめて確認できるWeb Archivesや、ワンクリックでarchive.todayへ保存可能なArchive Pageがあります。

引用スクリプト


Xからの引用を気持ち楽にするスクリプト。対象のURLを入力することで野獣大百科に直ぐに貼り付けられる形に出力される。Twikitを用いているのでログイン済みのクッキー☆が必要です。

ソースコード
from twikit import Client
import json
import re
from datetime import datetime, timezone, timedelta
import requests
import asyncio

def extract_tweet_id(url: str) -> str:
    match = re.search(r"status/(\d+)", url)
    if not match:
        raise ValueError()
    return match.group(1)

def format_datetime(created_at):
    dt_utc = datetime.strptime(
        created_at,
        "%a %b %d %H:%M:%S %z %Y"
    )
    dt_jst = dt_utc.astimezone(timezone(timedelta(hours=9)))
    hour = dt_jst.hour
    minute = dt_jst.minute

    if hour < 12:
        ampm = "午前"
        display_hour = hour if hour != 0 else 12
    else:
        ampm = "午後"
        display_hour = hour - 12 if hour > 12 else 12
        
    return f"{ampm}{display_hour}:{minute:02d} · {dt_jst.year}年{dt_jst.month}月{dt_jst.day}日"

def extractData(entry, root=False):
    if root:
        tweet_result = entry["content"]["itemContent"]["tweet_results"]["result"]
    else:
        tweet_result = entry["item"]["itemContent"]["tweet_results"]["result"]
    tweet_legacy = tweet_result["legacy"]
    user_legacy = tweet_result["core"]["user_results"]["result"]["legacy"]
    
    return {
        "name": user_legacy["name"],
        "screen_name": user_legacy["screen_name"],
        "created_at": format_datetime(tweet_legacy["created_at"]),
        "full_text": tweet_legacy["full_text"],
        "favorite_count": tweet_legacy["favorite_count"],
        "retweet_count": tweet_legacy["retweet_count"],
        "quote_count": tweet_legacy["quote_count"],
        "reply_count": tweet_legacy["reply_count"],
    }

def get_archive_today_short_url(target_url, timeout=10):
    archive_check_url = f"https://archive.ph/timemap/{target_url}"

    try:
        resp = requests.get(
            archive_check_url,
            allow_redirects=False,
            timeout=timeout,
            headers={
                "User-Agent": "Mozilla/5.0"
            }
        )
    except requests.RequestException:
        return None

    if resp.status_code == 302:
        return f"https://archive.ph/timegate/{target_url}"

    return None

async def main():
    client = Client('ja-JP')
    client.load_cookies("./cookies_twikit.json")

    tweet_url = "TARGET_URL"
    tweet_id = extract_tweet_id(tweet_url)

    response, _ = await client.gql.tweet_detail(str(tweet_id), None)
    entries = response["data"]["threaded_conversation_with_injections_v2"]["instructions"][1]["entries"]
    tweets = []

    print(json.dumps(entries))

    for entry in entries:
        if entry["content"].get("items"):
            for entry_reply in entry["content"].get("items"):
                if entry_reply["item"]["itemContent"].get("cursorType"):
                    break
                tweets.append(extractData(entry_reply))
        else:
            if entry["content"].get("cursorType"):
                break
            tweets.append(extractData(entry, root=True))

    archived = get_archive_today_short_url(tweet_url)
    if archived:
        em_archive = f"""(<a href="{archived}">魚拓</a>)"""
    else:
        em_archive = "(<del>魚拓</del>)"

    li_items = []

    for tweet in tweets:
        li_items.append(f"""        <li class="list-group-item">
            <p>{tweet["name"]}<br><small>@{tweet["screen_name"]}</small></p>
            <p>{tweet["full_text"].replace("\n", "<br>")}</p>
            <p><small>{tweet["created_at"]}</small></p>
        </li>""")
    li_html = "\n".join(li_items)

    html = f"""<div class="card h6 m-4">
    <ul class="list-group list-group-flush">
        <li class="list-group-item bg-light">
            <a href="{tweet_url}" target="_blank" rel="noopener noreferrer">
                {tweet_url}
            </a>
            {em_archive}
        </li>
        {li_html}
    </ul>
</div>"""

    with open(f"./{tweet_id}.txt", "w", encoding="utf-8") as f:
        f.write(html)

if __name__ == "__main__":
    asyncio.run(main())

脚注挿入


参照元[1]


  • 1. 参照先
<p>参照元<a href="#ref-1" name="fn-1"><sup>[1]</sup></a></p>
<hr>
<ul class="list-unstyled">
    <li>1. <a href="#fn-1" name="ref-1">↑</a>参照先</li>
</ul>