【统计向】2008年至今Pixiv上关于初音未来或VOCALOID的插画的投稿量逐月变化趋势
一、摘要
本篇专栏通过Python编写的爬虫自动完成了2008年1月1日至目前(2022年7月16日)初音未来、VOCALOID两个Tag的逐月(30天)的Pixiv插画投稿量统计。统计结果体现了十数年来VOCALOID文化经过的“扩大——兴盛——衰退——复兴”四阶段变化。统计代码可供将来其它人物 / 企划的插画 / 小说统计使用。

二、统计方法
统计通过Python代码进行,代码如下:
# coding:utf-8
import requests
import urllib.parse
import urllib3
import openpyxl
import time
import html
class Date:
# 建立一个日期类,以供后续代码使用
__dayPerMonth = ("Foo", 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
def __init__(self, y: int, m: int, d: int):
# y, m, d为年、月、日
self.y = y
self.m = m
self.d = d
def isInLeap(self):
# 该日期处于闰年中则返回True,否则返回False
if self.y % 400:
return not self.y % 4 and self.y % 100
else:
return True
def printSelf(self):
# 打印自身
return "{}-{:0>2d}-{:0>2d}".format(self.y, self.m, self.d)
def __add__(self, other: int):
# 返回自身过other天后的日期
newDate = Date(self.y, self.m, self.d)
newDate.d += other
while newDate.d > self.__dayPerMonth[newDate.m] + (1 if newDate.isInLeap() and newDate.m == 2 else 0):
newDate.d -= self.__dayPerMonth[newDate.m] + (1 if newDate.isInLeap() and newDate.m == 2 else 0)
newDate.m += 1
if newDate.m == 13:
newDate.m -= 12
newDate.y += 1
return newDate
def __sub__(self, other):
# 返回自身的other天前的日期
newDate = Date(self.y, self.m, self.d)
newDate.d -= other
while newDate.d <= 0:
if newDate.m == 1:
newDate.m = 13
newDate.y -= 1
newDate.d += self.__dayPerMonth[newDate.m - 1] + (1 if newDate.isInLeap() and newDate.m - 1 == 2 else 0)
newDate.m -= 1
return newDate
def loadFile(filename: str, asStr: bool):
# 返回同目录下一文件内的内容,asStr为True时以字符串的形式返回,否则返回eval(自身)
infp = open(filename, "r", encoding="UTF-8")
cont = infp.read()
infp.close()
if not asStr:
cont = eval(cont)
return cont
def tagInfoGet(form: str, Tag: str, scd="", ecd="", mode="all") -> dict:
# 通过requests访问Pixiv,返回Tag自scd日期开始到ecd日期为止form形式的创作的统计信息,mode可以指定
# form can be: 'novels', 'illustrations'
# mode can be: 'all', 'safe', 'r18'
# scd and ecd goes like: 2022-01-27
maxTryTimes = 5
if form not in ("novels", "illustrations"):
raise Exception('parameter "form" cannot be "{}"'.format(form))
if mode not in ("all", 'safe', 'r18'):
raise Exception('parameter "mode" cannot be "{}"'.format(mode))
originalTag = Tag
Tag = urllib.parse.quote(Tag, 'utf-8')
false, true, null = False, True, None
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/41.0.2272.118 Safari/537.36',
'Referer': "https://www.pixiv.net/",
# 如果在headers中不提供Cookie,得到的数据将不准确。
'Cookie': loadFile("cookie.txt", True)
}
if scd and ecd:
url = "https://www.pixiv.net/ajax/search/{}/{}?word={}&order=date_d&mode={}&scd={}&ecd={}" \
"&p=1&s_mode=s_tag&gs=0".format(form, Tag, Tag, mode, scd, ecd)
else:
url = "https://www.pixiv.net/ajax/search/{}/{}?word={}&order=date_d&mode={}" \
"&p=1&s_mode=s_tag&gs=0".format(form, Tag, Tag, mode)
print(url)
for i in range(maxTryTimes):
try:
h = requests.get(url, headers=headers, verify=False)
break
except:
print("Get {} failed! This is the try-{}".format(originalTag,i+1))
h.encoding = "UTF-8"
s = h.text
s = html.unescape(s)
print("Get", originalTag, "successfully!")
return eval(s)
def tagTotalGet(tagInfo: dict, form: str):
# 在从Pixiv获取统计信息后,将作品总数数据从中提取出来
# form can be: 'novel', 'illust'
return tagInfo["body"][form]["total"]
def PixivTime(theme: str, startDate: Date, dayInterval: int, samplePointN: int):
# theme:决定输出.xlsx文件的标题。startDate:统计开始时间,为之前建立的Date类格式。
# dayInterval:统计粒度,即每个数据的时间间隔,单位为天。samplePointN:每个Tag的统计数据数量。
# dayInterval 必须 < 365,因为Pixiv的按时间查找作品功能支持的时长为一年之内。
ZhNames = loadFile("ZhNames.txt", False)
JpNames = loadFile("JpNames.txt", False)
days = [startDate + dayInterval * i for i in range(samplePointN+1)]
if len(ZhNames) != len(JpNames):
raise Exception("len(JpNames) is {} while len(ZhNames) is {}!".format(len(JpNames), len(ZhNames)))
f = openpyxl.Workbook()
dws = f.active
illuSheet = f.create_sheet("插图统计")
novelSheet = f.create_sheet("小说统计")
f.remove_sheet(dws)
illuSheet.cell(1, 1).value = "姓名\\日期"
novelSheet.cell(1, 1).value = "姓名\\日期"
for i in range(len(days)):
illuSheet.cell(1, i + 2).value = days[i].printSelf()
novelSheet.cell(1, i + 2).value = days[i].printSelf()
for i in range(len(JpNames)):
illuSheet.cell(i + 2, 1).value = ZhNames[i]
novelSheet.cell(i + 2, 1).value = ZhNames[i]
for j in range(len(days)-1):
illuSheet.cell(i + 2, j + 2).value = tagTotalGet(tagInfoGet('illustrations', JpNames[i], scd=days[j].printSelf(),
ecd=(days[j+1]-1).printSelf()), 'illust')
print(str(i * 2 + 1) + ' / ' + str(len(JpNames) * 2))
for j in range(len(days)-1):
novelSheet.cell(i + 2, j + 2).value = tagTotalGet(tagInfoGet('novels', JpNames[i], scd=days[j].printSelf(),
ecd=(days[j+1]-1).printSelf()), 'novel')
print(str(i * 2 + 2) + ' / ' + str(len(JpNames) * 2))
timeString = time.asctime(time.localtime(time.time()))
timeString = timeString.replace(":", "")
f.save("PixivTime_" + timeString + theme + ".xlsx")
print("Pixiv Part Finished")
PixivTime("mikuAndVocalo",Date(2008,1,1),30,177)
其中,JpNames.txt用于存放要统计的Tag,内容为:
('初音ミク','VOCALOID')
ZhNames.txt中可以存放JpNames.txt中Tag的中文翻译,便于阅读统计结果。本次统计中ZhNames.txt的内容与JpNames.txt的内容相同。
cookie.txt的内容为访问Pixiv时要用到的Cookie信息,出于信息安全因素,该文件内容不予公开。Cookie的获取方法可参考https://blog.csdn.net/c406495762/article/details/78123502。获取前需确保你已登录一显示满18周岁的账号,且设置 - 浏览限制中的R-18、R-18G开关均为打开状态,以确保“搜索条件”功能可以正常使用,且可以统计到R-18、R-18G相关插图 / 小说的数据。另外,在“屏蔽设置”中不应有Tag和用户被屏蔽,以避免不必要的统计误差。
该代码的运行结果为在同目录下生成带相应数据的.xlsx文件,文件名格式为PixivTime_[运行完成时刻]mikuAndVocalo.xlsx。

三、统计结果
通过简单的数据处理,得折线图以及详细统计结果如下:



四、数据分析
初音未来折线在每年出现两个峰值,分别为MIKU之日(3月9日)和MIKU的纪念日(8月31日)的创作高峰所致。
在折线图中,初音未来折线与VOCALOID折线在峰值、走势上都具有较强的相关性,可以认为VOCALOID折线在很大程度上受初音未来折线的控制。
从折线的大体走势以及峰值的高低来看,VOCALOID相关插图统计量在统计时间段内经历了增加——稳定——减少——增加四个阶段的变化。如果试以插图投稿量作为VOCALOID文化热度的量化指标,则可归纳近十数年来VOCALOID文化经历的“扩大——兴盛——衰退——复兴”阶段如下:
①扩大期: - 约2011年初,VOCALOID亚文化圈快速地扩大声势。实际扩张速度可能慢于折线数据所体现的,因为还要从这一速度中减去Pixiv网站本身的热度增长所带来的影响。
②兴盛期:约2011年初 - 2013年中,VOCALOID文化热度在这一时期内缓慢上升,但上升速度逐渐减缓。
③衰退期:2013年中 - 2017年中,VOCALOID热度在该时间段内逐渐减退。以2017年夏末发生的一次爆发式投稿事件为界,该衰退期迎来结束。
④复兴期:2017年中 - 至今。VOCALOID热度逐渐回暖,并在目前基本上回到了兴盛期初水平。

五、结论与展望
VOCALOID的插图投稿量在很大程度上取决于初音未来的插图投稿量。从该投稿量的月度变化曲线中,我们注意到初音未来以及VOCALOID文化热度经历了“扩大——兴盛——衰退——复兴”的四阶段变化。
Pixiv网站本身的热度增长对插图投稿量曲线的影响程度如何,以及导致曲线呈现出四阶段变化的原因为何、标志性事件为何,有待将来进一步展开探讨。■