위키/목록에서 위키의 문서 수/총 페이지 수 통계를 갱신할 때 이용되는 파이썬 스크립트입니다. Pywikibot을 통해 돌아갈 수 있게 설게했습니다.
사용방법
- 우선 파이썬을 설치한 후, 시스템의 환경변수를 추가해서 커맨드 창에서 파이썬이 작동하게 설정합니다.
- 그 다음 PIP 설치방법 안내 페이지를 이용해서 PIP 패키지를 설치합니다.
- PIP를 설치했으면 커맨드 창에 PIP install selenium을 입력해 selenium 패키기를 설치합니다.
- 다음 링크를 이용해서 크롬 브라우저용 Chromedriver를 다운로드 받습니다. 반드시 pwb.py가 있는 곳에 chromedriver 실행 프로그램이 있어야 합니다.
- 다음 아래의 소스코드를 복사/붙여넣기 후에 pywikibot이 설치된 폴더에서 scripts/userscripts에 libre_wikistat.py로 저장합니다.(커스텀 스크립트를 저장할 수 있는 곳)
- 다 준비가 되었다면 cd C:/(Pywikibot이 설치된 카테고리) 명령어를 이용해 Pywikibot이 있는 폴더로 이동한 뒤에 커맨드 창에
> python pwb.py libre_wikistat
이라고 입력하면 작동합니다. 상당히 지저분한 코드인 검을 유감스럽게 생각합니다.
스크립트 소스
from selenium import webdriver
import re
import datetime
import time
import pywikibot
# import pickle
# import os
# 숫자 반점으로 끊어주는 함수
def digit_div(msg):
# 우선 arg에서 온점, 띄어쓰기 등 없애기
msg = str(msg).replace('.', '').replace(' ', '').replace(',', '').replace('٬', '')
str_new = str(format(int(msg), ','))
return str_new
# 문자열 한 자리면 0 강제삽입
def zeroinput(_str):
if len(_str) == 0:
return '00'
elif len(_str) == 1:
return '0' + _str
else:
return _str
# 미디어위키 데이터 통계
# "dc": [article_num, page_num, whole_edit, whole_user, active_user]
MW_WIKI_DATA = {}
# 다른 위키 데이터 통계
# "namu": [article_num, page_num], 없으면 None으로 처리
OTHER_WIKI_DATA = {}
# 미디어위키 통계 - 위키 기호- url 입력
MW_WIKI = {
# 한국어 사이트
'nuri': 'https://nuriwiki.net/wiki/', # 누리위키
'dc': 'https://wiki.dcinside.com/wiki/', # 디시위키
"ligame": 'https://libertygame.miraheze.org/wiki/', # 리버티게임
'unam': 'https://www.unamwiki.org/w/', # 우남위키
'wikis': 'https://wikis.krosocsi.org/index.php?title=', # 위키스
'licentium': 'https://www.licentium.net/wiki/', # 자유인사전
'jwiki': 'http://jwiki.kr/wiki/index.php/', # 제이위키
'zeta': 'https://zetawiki.com/wiki/', # 제타위키
'jinbo': 'https://jinbowiki.org/wiki/index.php/', # 진보위키
'truth': 'https://truth.miraheze.org/wiki/', # 진실위키
'coin': 'http://wiki.hash.kr/index.php/', # 코인위키
'bigfo': 'https://bigforest.miraheze.org/wiki/', # 큰숲백과
'footk': 'http://footballk.net/mediawiki/', # 풋케위키
'femi': 'https://femiwiki.com/w/', # 페미위키
'kowiks': 'https://ko.wikisource.org/wiki/', # 위키문헌
'kowikq': 'https://ko.wikiquote.org/wiki/', # 위키인용집
'kowikb': 'https://ko.wikibooks.org/wiki/', # 위키책
'air': 'https://airtravelinfo.kr/wiki/index.php?title=', # 항공위키
'bujok': 'https://bujok.fandom.com/ko/wiki/', # 부족전쟁위키
'poke': 'https://pokemon.fandom.com/ko/wiki/', # 포켓몬 위키
'kofandom': 'https://community.fandom.com/ko/wiki/', # 팬덤 중앙 커뮤니티
'kouncyclo': 'https://uncyclopedia.kr/wiki/', # 백괴사전
# 영어 사이트
'enwikb': 'https://en.wikibooks.org/wiki/', # wikibooks
'enwikn': 'https://en.wikinews.org/wiki/', # wikinews
'enwikq': 'https://en.wikiquote.org/wiki/', # wikiquote,
'enwiks': 'https://en.wikisource.org/wiki/', # wikisource
'mw': 'https://mediawiki.org/wiki/', # medeawiki
'wimeta': 'https://meta.wikimedia.org/wiki/', # wikimedia meta
'common': 'https://commons.wikimedia.org/wiki/', # wikimedia commons
'widata': 'https://www.wikidata.org/wiki/', # wikidata
'fandom': 'https://community.fandom.com/wiki/', # fandom central community
'att': 'https://allthetropes.org/wiki/', # All The Tropes
'bulba': 'http://bulbapedia.bulbagarden.net/wiki/', # Bulbapedia
'conserv': 'https://conservapedia.com/', # Conservapedia
'dcdata': 'https://dc.fandom.com/wiki/', # DC Database
'family': 'https://familypedia.wikia.org/wiki/', # Familypedia
'mariowiki': 'https://mariowiki.com/', # Mariowiki
'minecraft': 'https://minecraft.fandom.com/wiki/', # Minecraft Wiki
'rational': 'https://rationalwiki.org/wiki/', # Rational Wiki
'uncyclo': 'http://en.uncyclopedia.co/wiki/', # Uncyclopedia
'starwars': 'https://starwars.fandom.com/wiki/', # Wookiepedia
'wowpedia': 'https://wowpedia.fandom.com/wiki/', # WowPedia
}
# 각 언어별 위키백과 목록
WIKIPEDIA_LANG = [
'arwiki',
'dewiki',
'enwiki',
'eswiki',
'frwiki',
'idwiki',
'itwiki',
'jawiki',
'kowiki',
'nlwiki',
'plwiki',
'ptwiki',
'ruwiki',
'srwiki',
'svwiki',
'ukwiki',
'viwiki',
'zhwiki'
]
# 각 언어별 위키낱말사전 목록
WIKTIONARY_LANG = [
'dewikt',
'enwikt',
'frwikt',
'kowikt',
'ruwikt'
]
# 미디어위키 계열 위키 반복 프로세스
def mw_process(_str):
global driver, MW_WIKI
# 홈페이지 불러오기.
if 'wiki' in _str:
url = 'https://{}.wikipedia.org/wiki/special:statistics/'.format(_str[0:2])
elif 'wikt' in _str:
url = 'https://{}.wiktionary.org/wiki/special:statistics/'.format(_str[0:2])
else:
url = MW_WIKI[_str]+"special:statistics/"
driver.get(url)
time.sleep(1)
MW_WIKI_DATA[_str] = []
d1 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[2]/td[2]').text
d2 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[3]/td[2]').text
# 미디어 파일 통계가 잡히지 않는 스페인어 위키백과/스웨덴어 위키백과/bulbapedia는 데이터 수집 보정을 해야 한다.
if _str in ['eswiki', 'svwiki', 'bulba', 'widata', 'enwikq']:
d3 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[5]/td[2]').text
d4 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[8]/td[2]').text
d5 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[9]/td[2]').text
# 나머지 위키들
else:
d3 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[6]/td[2]').text
d4 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[9]/td[2]').text
d5 = driver.find_element_by_xpath('//*[@id="mw-content-text"]/table/tbody/tr[10]/td[2]').text
MW_WIKI_DATA[_str] = [digit_div(d1), digit_div(d2), digit_div(d3), digit_div(d4), digit_div(d5)]
print(MW_WIKI_DATA[_str])
# 나머지 위키 패턴
# '위키명': ['stat_url', 'article_stat_value_xpath', 'page_stat_value_xpath']
WIKI_ARTICLE = {
'nosm': ['http://no-smok.net/nsmk/SystemInfo', None, '//*[@id="macro-1"]'], # 노스모크
# 'scpko' - 단순 매크로로 긁어오기 어려운 구조
'namu': [
'https://namu.wiki/w/%EB%82%98%EB%AC%B4%EC%9C%84%ED%82%A4:%ED%86%B5%EA%B3%84',
'//*[@id="app"]/div/div[2]/article/div[3]/div[2]/div/div/div[5]/table/tbody/tr[2]/td[2]/div',
'//*[@id="app"]/div/div[2]/article/div[3]/div[2]/div/div/div[5]/table/tbody/tr[1]/td[2]/div'
], # 나무위키
'alpha': [
'https://awiki.theseed.io/w/%EC%95%8C%ED%8C%8C%EC%9C%84%ED%82%A4:%ED%86%B5%EA%B3%84/i',
'//*[@id="app"]/div/section[2]/div/div/div/div[2]/div/div/div[2]/table/tbody/tr[3]/td[2]/div',
'//*[@id="app"]/div/section[2]/div/div/div/div[2]/div/div/div[2]/table/tbody/tr[2]/td[3]/div'
], # 알파위키
'veda': [
'http://rigvedawiki.net/w/%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%A0%95%EB%B3%B4',
'//*[@id="macro-3"]',
'//*[@id="macro-1"]'
], # 리그베다 위키
# open- 조작이 필요- 별도처리
# 더위키 - 조작이 필요
# 바다위키 - 조작이 필요 - 별도처리
# 시드위키 - 조작 필요
'zero': [
'https://wiki.zeropage.org/wiki.php/TitleIndex',
'//*[@id="macro-3"]',
'//*[@id= "macro-1"]'
] # 제로위키
}
def patterned_process(_str):
global driver, WIKI_ARTICLE, OTHER_WIKI_DATA
url = WIKI_ARTICLE[_str][0]
driver.get(url)
time.sleep(1)
if WIKI_ARTICLE[_str][1] is not None:
v1 = driver.find_element_by_xpath(WIKI_ARTICLE[_str][1]).text
v1 = digit_div(v1)
else:
v1 = None
if WIKI_ARTICLE[_str][2] != None:
v2 = driver.find_element_by_xpath(WIKI_ARTICLE[_str][2]).text
v2 = digit_div(v2)
else:
v2 = None
OTHER_WIKI_DATA[_str] = [v1, v2]
print(OTHER_WIKI_DATA[_str])
driver = webdriver.Chrome()
# 위키백과+위키낱말사전 통합 통계
for pattern in WIKIPEDIA_LANG + WIKTIONARY_LANG:
try:
mw_process(pattern)
except:
pass
# 나머지 미디어위키 통합 통계
for pattern in MW_WIKI.keys():
try:
mw_process(pattern)
except:
pass
# 나머지 위키 중 패턴 있는 위키들 통합 통계
for pattern in WIKI_ARTICLE.keys():
try:
patterned_process(pattern)
except:
pass
# scp 한국어위 통계 - 단독통계
try:
driver.get('http://ko.scp-wiki.net/system:list-all-pages')
time.sleep(1)
txt = driver.find_element_by_xpath('//*[@id="page-content"]/div[1]/p').text
txt = txt.replace('전체 페이지 수: ', '')
OTHER_WIKI_DATA['scpko'] = [None, digit_div(txt)]
print(OTHER_WIKI_DATA['scpko'])
except:
pass
# 오픈위키 통계
try:
driver.get('http://openwiki.kr/about/')
time.sleep(1)
open_1 = driver.find_element_by_xpath('//*[@id="dokuwiki__content"]/div/div[2]/div[19]/ul/li[2]/ul/li[1]/div/em').text
open_2 = driver.find_element_by_xpath('//*[@id="dokuwiki__content"]/div/div[2]/div[19]/ul/li[2]/ul/li[2]/div/em').text
OTHER_WIKI_DATA['open'] = [format(int(open_1), ','), format(int(open_1) + int(open_2), ',')]
print(OTHER_WIKI_DATA['open'])
except:
pass
# 더위키 통계
try:
driver.get('https://thewiki.kr/w/TheWiki:%ED%86%B5%EA%B3%84')
time.sleep(1)
thewiki_data = driver.find_element_by_xpath('/html/body/div[2]/article/div[3]/div/ul[1]/li').text
thewiki_stat = thewiki_data[24:29]
OTHER_WIKI_DATA['thewiki'] = [thewiki_stat, None]
print(OTHER_WIKI_DATA['thewiki'])
except:
pass
# 바다위키 통계
try:
driver.get('https://bada.wiki/w/%EB%B0%94%EB%8B%A4%EC%9C%84%ED%82%A4%3A%ED%86%B5%EA%B3%84')
time.sleep(1)
bada_data = driver.find_element_by_xpath('//*[@id="in_data_0"]')
bada_stat = digit_div(bada_data.text[-8:-2])
OTHER_WIKI_DATA['bada'] = [None, bada_stat]
print(OTHER_WIKI_DATA['bada'])
except:
pass
# 시드위키 통계
try:
driver.get('https://seedwiki.xyz/title_index')
time.sleep(1)
seed_pages = driver.find_element_by_xpath('/html/body/div[3]/div[2]/div[2]/ul[2]/li').text.replace('전체 : ', '')
seed_articles = driver.find_element_by_xpath('/html/body/div[3]/div[2]/div[2]/ul[3]/li[4]').text.replace('기타 : ','')
OTHER_WIKI_DATA['seed'] = [format(int(seed_articles), ','), format(int(seed_pages), ',')]
print(OTHER_WIKI_DATA['seed'])
except:
pass
# TV Tropes 통계
try:
driver.get('https://tvtropes.org/pmwiki/articlecount.php')
time.sleep(1)
trope_1 = driver.find_elements_by_xpath('//*[@id="wikimiddle"]/p[4]')
trope_1 = trope_1[0].text.split('\n')
for stats in trope_1:
if 'Main' in stats:
article_trope = stats.replace(': Main', '')
break
trope_2 = driver.find_element_by_xpath('//*[@id="wikimiddle"]/p[2]')
page_trope = trope_2.text.replace('Total: ', '')
OTHER_WIKI_DATA['trope'] = [format(int(article_trope), ','), format(int(page_trope), ',')]
print(OTHER_WIKI_DATA['trope'])
except:
pass
# 영어 SCP 통계
try:
driver.get('http://www.scpwiki.com/system:list-all-pages/p/500')
time.sleep(1)
cnt1 = len(driver.find_elements_by_xpath('//*[@id="page-content"]/div/div')) - 1
cnt2_item = driver.find_elements_by_class_name('current')[0]
cnt2 = int(cnt2_item.text)
OTHER_WIKI_DATA['scp'] = [None, format(50 * (cnt2 - 1) + cnt1, ',')]
print(OTHER_WIKI_DATA['scp'])
except:
pass
# WIKITREE 통계
try:
driver.get('https://www.wikitree.com/')
time.sleep(1)
cnt_txt = driver.find_element_by_xpath('//*[@id="content"]/div/div[2]/div').text
cnt = cnt_txt[18:28].strip()
print(OTHER_WIKI_DATA['wikitree'])
except:
pass
# 니코니코 대백과 통계 - 단독통계
try:
driver.get('https://dic.nicovideo.jp/')
time.sleep(1)
text = driver.find_element_by_id("count-all-articles").text
OTHER_WIKI_DATA['nico'] = [None, digit_div(text)]
except:
pass
# 바이두 백과 통계
try:
driver.get('https://baike.baidu.com')
time.sleep(5)
baidu_1 = driver.find_element_by_xpath('//*[@id="lemmaNum"]')
OTHER_WIKI_DATA['baidu'] = [None, baidu_1.text]
except:
pass
# pkl = open('wikistat.pkl', 'wb')
# pickle.dump(article_num, pkl)
# pickle.dump(page_num, pkl)
# pkl.close()
# 통계 작성 시점
x = datetime.datetime.now()
msg_date = '<!--date-->' + str(x.year) + '-' + str(x.month) + '-' + str(x.day)
msg_time = '<!--time-->' + zeroinput(str(x.hour)) + ':' + zeroinput(str(x.minute)) + '(KST)'
print('날짜:', msg_date)
print('시간:', msg_time)
driver.close()
# 위키/목록 텍스트 추출
site = pywikibot.Site('ko', 'libre')
page = pywikibot.Page(site, "위키/통계")
txt = page.text
# 통계 수정
# 미디어위키 기반 위키
for pat, dat in MW_WIKI_DATA.items():
pat_1 = "<!--{}-->".format(pat)
pat_2 = "<!--{}_2-->".format(pat)
pat_3 = "<!--{}_3-->".format(pat)
pat_4 = "<!--{}_4-->".format(pat)
pat_5 = "<!--{}_5-->".format(pat)
if len(dat) == 5:
txt = re.sub(pat_1 + r"[0-9,]*", pat_1 + dat[0], txt)
txt = re.sub(pat_2 + r"[0-9,]*", pat_2 + dat[1], txt)
txt = re.sub(pat_3 + r"[0-9,]*", pat_3 + dat[2], txt)
txt = re.sub(pat_4 + r"[0-9,]*", pat_4 + dat[3], txt)
txt = re.sub(pat_5 + r"[0-9,]*", pat_5 + dat[4], txt)
# 나머지 위키들
for pat, dat in OTHER_WIKI_DATA.items():
pat_1 = "<!--{}-->".format(pat)
pat_2 = "<!--{}_2-->".format(pat)
if len(dat) == 2 and dat[0] != None:
txt = re.sub(pat_1 + r"[0-9,]*", pat_1 + dat[0], txt)
if len(dat) == 2 and dat[1] != None:
txt = re.sub(pat_2 + r"[0-9,]*", pat_2 + dat[1], txt)
# 시간 수정
msg_date_old = '<!--date-->' + r'[0-9]{4,}\-[0-9]{1,2}\-[0-9]{1,2}'
msg_time_old = '<!--time-->' + r'[0-9]{1,2}:[0-9]{1,2}\(KST\)'
txt = re.sub(msg_date_old, msg_date, txt)
txt = re.sub(msg_time_old, msg_time, txt)
# 위키/목록 페이지 수정
page.text = txt
page.save('봇:통계 갱신')