よみもの:Pythonを使ったエスケープ転載ツールの感想など

出典: 謎の百科事典もどき『エンペディア(Enpedia)』
ナビゲーションに移動 検索に移動
エンペたん看板 Welcome.png よみものページ
この「よみもの」は、WxYukiが作成したものです。
他ユーザーの編集も歓迎いたします。

こんにちは、毎度おなじみWxYuki(雪見だいふく)です。今回は利用者ページでも散々自慢していた「Wikipediaからのエスケープ転載ツール」を簡単にpythonで作ってみての感想や具体的なあれこれなどを、忘れないようにここに書き留めておきたいと思います。まあノンプログラマの稚拙なコードなのでクオリティはお察し。暖かい目でご覧いただければ。python勉強しててMediaWiki好きだよって方も是非。

本記事のプログラムを使用して発生した損害については、誰も何も一切の責任を負いませんし負えません。自己責任でどうぞ。

下記の説明はある程度 Python を知っている前提で書かれています。が、Python とかプログラミングとかよく知らない人でも、このツールを使うことは可能です。「Python?何それ美味しいの?」って人は、こちらの補足説明をお読みください。

お知らせ[編集]

  • 2024/02/18追記:

悲報:版指定+テンプレートへの分岐を実装するため、全ての版の情報を取得するようプログラムを書き換えたところ、取得版数の上限が500であるということが判明してしまいました...。なので、メインページなどのような歴史のありすぎるページや、無駄に編集されまくったページは場合によって取得できない場合がありますっつーかできねーよ(正確には、取得した版が500に達した時点で動作を停止するようにしています)。なので原則、版数が500以上あるページは移入が現実的でないので、使用する際は移入元ページの版数をよく確認の上使用するようにお願いいたします。そういう意味では、最新版しか移入しないという方は前のバージョンを今まで通り使用してもらって大丈夫です。

履歴[編集]

  • 2023/06/10:api使用版にアップデートしました。
  • 2024/02/18:アップデートしました。版を指定して移入できるようになりました。また、要約欄に収まりきらないほど著作者が多い場合、{{移入元情報}}へ分岐するようにしました。悲報も併せてご覧ください。

作ろうと思った理由[編集]

まあ、単純に履歴継承とか記事ソース拾いが面倒だったから。ですね。自分は比較的単純作業には慣れているしむしろ好きなのですが、こればっかりはやってらんねーなと思っちゃいました。編集に携わったすべての利用者の名前を書けとか、時間が有限な学生にとっては苦痛でしかない。正直なところ、いつもみたいに途中で座礁しておじゃんになるのかなと思っていたんですが、割とうまく行ったんで安心しました。

開発環境[編集]

開発環境は以下の通りです。

  • python 3.10.6

作り方[編集]

今回はコンソール版の制作手順を紹介します。GUI版は要望があれば。

全体像[編集]

import requests
# coding: UTF-8
from urllib.request import Request, urlopen
from urllib.parse import urlencode
from urllib.error import URLError, HTTPError
import json

session = requests.Session()
url = "https://ja.wikipedia.org/w/api.php"
input_title = input("記事名を入力>>")
if input_title == "":
    input_title = "メインページ"
    
version_settings = input("必要な場合、何版前を取得するか指定(通常無視)>>")
if version_settings == "":
    version_settings = 0
else:
    version_settings = int(version_settings)

#記事ゾーン
article_params = {
    "action":"query",
    "prop":"revisions",
    "titles":input_title,
    "rvprop":"content",
    "rvlimit": version_settings + 1,
    "format":"json"
}
article_response = session.get(url=url,params=article_params)
article_data = article_response.json()

#IDぞーん
af_id = list(article_data["query"]["pages"].keys())[0]
content = str(article_data["query"]["pages"][af_id]["revisions"][0+version_settings]["*"])#記事

#履歴ゾーン
history_params = {
    "action":"query",
    "prop":"revisions",
    "titles":input_title,
    "rvprop":"timestamp",
    "rvlimit": version_settings+1,
    "format":"json"
}

article_history_response = session.get(url=url,params=history_params)
article_history_data = article_history_response.json()

#タイムスタンプ
time_stamp = article_history_data["query"]["pages"][af_id]["revisions"][0+version_settings]["timestamp"]

#版の総数
total_params = {
    "action": "query",
    "prop": "revisions",
    "titles": input_title,
    "rvprop": "ids",
    "rvlimit": "max",
    "format": "json"
}

total_response = requests.get(url, params=total_params)
total_data = total_response.json()
total_revisions = len(total_data["query"]["pages"][af_id]["revisions"])
if total_revisions == 500:
    input("版が多すぎるため、正確に移入できません。動作を中止します。>>")
    exit()
else:
    print(total_revisions - version_settings)
    print("-"*30)
#利用者ゾーン
users_params ={
    "action":"query",
    "prop":"revisions",
    "rvprop":"user",
    "format":"json",
    "titles":input_title,
    "rvdir":"newer",
    "rvlimit":total_revisions - version_settings
}

users_response = requests.get(url=url,params=users_params)
users_data = json.loads(users_response.text)
user_list = []
for page in users_data["query"]["pages"].values():
   if "revisions" in page:
       for revision in page["revisions"]:
           if "user" in revision:
               user_list.append(revision["user"])
                

#重複した利用者名を削除する
user_list = list(set(user_list))

user_result = ",".join(map(str, user_list))#利用者

sentence = "[[jawp:{}]]の{}(UTC)の版より全文をエスケープ転載。投稿者:{}。".format(input_title,time_stamp,user_result)

print(content)
print("-"*30)
if len(sentence) >= 500:
    print("以下、移入元情報を記載する。\nこの記事は [[jawp:{}]] の {} (UTC) 時点の版から転記された。\n*著作者一覧:{}\n*改変点:{}\n以上。".format(input_title,time_stamp,user_result,"修正内容を入力."))
else:
    print(sentence)

全体はこんな感じ。コンソールでの動作のみなのでその分コード量は抑えられています。

インポート[編集]

#-------------------------------------------------------------------------------
# Author:      WxYuki
# Created:     24/09/2022
# Copyright:   (c) WxYuki 2022
# Licence:     CC by-sa 3.0
#-------------------------------------------------------------------------------
import requests
# coding: UTF-8
from urllib.request import Request, urlopen
from urllib.parse import urlencode
from urllib.error import URLError, HTTPError
import json

とりあえずインポートを済ませましょう。requestsについてはあらかじめpipでインスコしておいてください。urllib,jsonは標準付属なので特段操作は必要ないです。一応アップデートなどの確認はしておいたほうがいいかも。そこら辺は割愛。

記事ソースを持ってくる[編集]

import requests
# coding: UTF-8
from urllib.request import Request, urlopen
from urllib.parse import urlencode
from urllib.error import URLError, HTTPError
import json

session = requests.Session()
url = "https://ja.wikipedia.org/w/api.php"
input_title = input("記事名を入力>>")
if input_title == "":
    input_title = "メインページ"
    
version_settings = input("必要な場合、何版前を取得するか指定(通常無視)>>")
if version_settings == "":
    version_settings = 0
else:
    version_settings = int(version_settings)

#記事ゾーン
article_params = {
    "action":"query",
    "prop":"revisions",
    "titles":input_title,
    "rvprop":"content",
    "rvlimit": version_settings + 1,
    "format":"json"
}
article_response = session.get(url=url,params=article_params)
article_data = article_response.json()

#IDぞーん
af_id = list(article_data["query"]["pages"].keys())[0]
content = str(article_data["query"]["pages"][af_id]["revisions"][0+version_settings]["*"])#記事

まずapiを使用するためにjawpの/api.phpへのurlを定義。article_paramsには要求したいデータの詳細をjson形式でapiに送りつけられるように設定します。次のパラグラフでは記事情報を取得...したかと思いきや、実はここで手間が発生します。 やってみるとわかりますが、基本的にapiを使用する場合はjsonでデータが帰ってきます。なので適切な形にスライスして調理しなければいけないのですが、個人的には面倒くさかったです。af_idという変数はスライスの作業のために定義していますが、スライスになれていなかった当時の僕はかなり頭を抱えていました。あゝ。

履歴を拾って出力[編集]

履歴は手作業でやるのがかなり苦痛だったので、一番自動化したい行程の一つでした。api様々。

#版の総数
total_params = {
    "action": "query",
    "prop": "revisions",
    "titles": input_title,
    "rvprop": "ids",
    "rvlimit": "max",
    "format": "json"
}

total_response = requests.get(url, params=total_params)
total_data = total_response.json()
total_revisions = len(total_data["query"]["pages"][af_id]["revisions"])
if total_revisions == 500:
    input("版が多すぎるため、正確に移入できません。動作を中止します。>>")
    exit()
else:
    print(total_revisions - version_settings)
    print("-"*30)
#利用者ゾーン
users_params ={
    "action":"query",
    "prop":"revisions",
    "rvprop":"user",
    "format":"json",
    "titles":input_title,
    "rvdir":"newer",
    "rvlimit":total_revisions - version_settings
}

users_response = requests.get(url=url,params=users_params)
users_data = json.loads(users_response.text)
user_list = []
for page in users_data["query"]["pages"].values():
   if "revisions" in page:
       for revision in page["revisions"]:
           if "user" in revision:
               user_list.append(revision["user"])
                

#重複した利用者名を削除する
user_list = list(set(user_list))

user_result = ",".join(map(str, user_list))#利用者

sentence = "[[jawp:{}]]の{}(UTC)の版より全文をエスケープ転載。投稿者:{}。".format(input_title,time_stamp,user_result)

print(content)
print("-"*30)
if len(sentence) >= 500:
    print("以下、移入元情報を記載する。\nこの記事は [[jawp:{}]] の {} (UTC) 時点の版から転記された。\n*著作者一覧:{}\n*改変点:{}\n以上。".format(input_title,time_stamp,user_result,"修正内容を入力."))
else:
    print(sentence)

history_params、users_paramsという変数では、記事ソースと同じく「履歴を取得する用」のデータの塊を定義しています。設定箇所を少し変えると取得するものや条件、数量などを簡単に変更することができますので、是非いろいろ試してみてください。あ、冒険の際はQiitaを忘れずに。

後の流れはさっきと同じ。説明省略。user_listから後ろの動きが何やってるかわからないという質問はなしで。僕もよく覚えていません。後はprintして終了。

ぐだぐだポイント[編集]

jsonの扱いとスライスが下手っぴすぎるせいでかなりぐだぐだな作業工程となりました。精進します。

お世話になった猛者たちの英知[編集]