一、作业要求
1、选一个自己感兴趣的主题。
2、网络上爬取相关的数据。
3、进行文本分析,生成词云。
4、对文本分析结果解释说明。
5、写一篇完整的博客,附上源代码、数据爬取及分析结果,形成一个可展示的成果。
二、游戏王卡片简介
《游戏王OCG》是由KONAMI研发、吉尼斯世界纪录认证的全世界销量最高的集换式卡牌游戏。改编自漫画《游戏王》中登场的卡片游戏“魔法与巫师卡”(动画版称为“决斗怪兽”),使用游戏王卡片进行对战的卡牌类益智游戏。这些卡片比普通的扑克稍大,卡上的图案取材于各种神话传说及未来幻想,包括日本、古印度、古埃及、古巴比伦等,还标明了该怪兽的属性、攻击、防守值与怪兽的效果(或介绍)。
游戏王卡片于1999年在日本发售,至今包括英文专属版本卡片在内,已经发行了8568张不同的卡片。
游戏王卡片基本种类有三种,分别是怪兽卡、陷阱卡和魔法卡,各类卡片还有子类卡片。怪兽卡都基本拥有名称、属性、种族、效果、攻击力与防守力的基本属性,此外还有星级属性,但由于后来发行的怪兽卡片类型出现了星级、阶级、连接级等针对不同怪兽卡片的星级属性,不再统一化,所以在本次爬虫中不会获取“星级”这个属性。而陷阱卡和魔法卡只有名称、属性、种类和效果这四个基本属性。
图1 游戏王卡片的基本种类
图2 游戏王卡片的基本属性
图3 游戏王卡片的属性类型的详细子类型
国内游戏王爱好玩家聚在一起开发了在线卡查的游戏王查询网站,方便游戏王爱好者查询和及时了解卡片的最新信息。本次数据搜索与分析便基于这个在线卡查网站来进行爬虫。网站主页面为http://www.ourocg.cn/Cards/Lists-5-1。
三、爬虫步骤
1、爬取总页数
1 #函数功能:获取卡片的总页数 2 def getpage(): 3 flag1 = '搜索结果 / 约 ' #该变量内容为总页数所在字段文本的前部分文字 4 flag2 = '条' 5 card_page = soup.select('.result')[0].text.lstrip(flag1) #获取到总页数所在的字段文本 6 page_num = card_page.find(flag2) #查找“条”字符所在的位置 7 allpage = int(card_page[0:page_num]) #输出“条”字前面的数字,即总卡片数 8 page = allpage//18 #输出总页数,每页有18条卡片记录 9 return page
2、爬取卡片信息
由于上面所说的星数问题,因此怪兽卡只获取名称、链接、类型、属性、种族和效果这6个元素,而由于魔法陷阱卡与怪兽卡有别,又为了能使它们的信息都放在同一张表上,因此魔法陷阱卡的信息中属性与种族的信息为空,以便保留格式一致。
1 #函数功能:获取魔法陷阱卡的信息 2 def getdetail_MagicTrap(url_MT): 3 resd = requests.get(url_MT) 4 resd.encoding = 'utf-8' 5 soup_MT = BeautifulSoup(resd.text,'html.parser') 6 card_news_MagicTrap = {} 7 for news in soup_MT.select('.card'): 8 card_news_MagicTrap['名称'] = news.select('div')[3].text 9 card_news_MagicTrap['链接'] = url_MT 10 card_type = news.select('div')[12].text 11 card_news_MagicTrap['类型'] = card_type[0:4] 12 card_news_MagicTrap['属性'] = ' ' 13 card_news_MagicTrap['种族'] = ' ' 14 card_effect_text = news.select(".effect")[0].text 15 card_news_MagicTrap['效果'] = card_effect_text[2:len(card_effect_text)-1] 16 return card_news_MagicTrap 17 18 #函数功能:获取怪兽卡的信息 19 def getdetail_Monster(url_M): 20 resd = requests.get(url_M) 21 resd.encoding = 'utf-8' 22 soup_M = BeautifulSoup(resd.text,'html.parser') 23 card_news_Monster = {} 24 for news in soup_M.select('.card'): 25 card_news_Monster['名称'] = news.select('div')[3].text 26 card_news_Monster['链接'] = url_M 27 card_type = news.select('div')[12].text 28 card_news_Monster['类型'] = card_type[0:4] 29 card_news_Monster['属性'] = news.select('div')[24].text 30 card_news_Monster['种族'] = news.select('div')[22].text 31 card_effect_text = news.select(".effect")[0].text 32 card_news_Monster['效果'] = card_effect_text[0:len(card_effect_text)-1] 33 return card_news_Monster
3、爬取每一页的卡片信息
每个卡片信息都有个共同的h2标签作为开头,利用这个特性可以获取到每个卡片的链接,之后通过创建一个字符串来判断该卡片是何种类型的卡片,从而调用上面的不同种类的函数来获取卡片的信息。
1 #函数功能:获取一个页面的卡片信息 2 def onepage(url_page): 3 res = requests.get(url_page) 4 res.encoding = 'utf-8' 5 soup_page = BeautifulSoup(res.text,'html.parser') 6 card_newsls = [] #创建一个列表 7 for allnews in soup_page.select('.card'): 8 if len(allnews.select('h2'))>0: 9 card_maintype = allnews.select('span')[0].text 10 url = allnews.select('a')[0]['href'] 11 card_url = ('http://www.ourocg.cn{}'.format(url)) 12 if card_maintype in card_judge: #这里用来判断卡片是怪兽卡还是魔法陷阱卡 13 card_newsls.append(getdetail_MagicTrap(card_url)) 14 else: 15 card_newsls.append(getdetail_Monster(card_url)) 16 #append():接受一个参数,这个参数可以是任何数据类型,并且简单地追加到list的尾部 17 return card_newsls 18 19 card_judge = '魔法陷阱' #该字符串变量用于判断卡片是何类卡片
4、生成所有卡片信息的列表
1 news_cardtotal = [] #存放所有卡片信息 2 url_cards = 'http://www.ourocg.cn/Cards/Lists-5-1' #网站首页 3 news_cardtotal.extend(onepage(url_cards)) #把新list中的每个元素添加到原list中 4 5 res_card = requests.get(url_cards) 6 res_card.encoding = 'utf-8' 7 soup = BeautifulSoup(res_card.text,"html.parser") 8 9 for i in range(2,getpage()+1): #通过循环获取首页之后页数的卡片信息列表 10 url_list = 'http://www.ourocg.cn/Cards/Lists-5-{}'.format(i) 11 news_cardtotal.extend(onepage(url_list)) 12 13 df = pandas.DataFrame(news_cardtotal) #转换成表格型数据(DataFrame型) 14 df.to_excel('card_info.xlsx') #以excel表的形式保存结果
生成的结果可以用Excel表来查看具体的内容:
5、分词与云图的生成
1 for j in range(len(news_cardtotal)): 2 if j>0: 3 race = race+news_cardtotal[j].get('种族') 4 race = race+' ' 5 else: 6 race = news_cardtotal[j].get('种族') 7 race = race+' ' 8 words = jieba.lcut(race) #进行文本分词 9 10 ls = [] #存放词组文本 11 counts = {} #存放词组的计数 12 for word in words: 13 ls.append(word) 14 if len(word) == 0: 15 continue 16 else: 17 counts[word] = counts.get(word,0)+1 #计数字典 18 items = list(counts.items()) #计数列表 19 items.sort(key = lambda x:x[1], reverse = True) #排序 20 for j in range(10): 21 word , count = items[j] 22 print ("{:<10}{:>5}".format(word,count)) 23 24 cy = WordCloud().generate(race) #词云的制作与显示 25 plt.imshow(cy) 26 plt.axis("off") #不显示标尺 27 plt.show()
由于数量庞大,所以在电脑后台可以看到大量的数据流量交换。
四、结果分析
这次大作业会分析以下三点:
1、各种族的怪兽卡有多少张?(例如战士族的怪兽卡有多少张)哪个种族的卡片最多?
我们把分词的关键词选为“种族”后进行分词,生成的文本用来生成云图。为了使分词更加准确,在每个分词后会添加一个空格以避免重复计算和重义计算,所以结果中空格永远会占最高排名位。代码与上面第五点一样。
从结果最终可以看出,战士族是卡片数量最多的,主要是因为战士族是第一批发行的怪兽种族,而且主体系列卡片也比较多,自然比例就很高。
2、怪兽卡、魔法卡、陷阱卡的卡片数量以及它们子类卡片的数量比例是多少?
我们把分词的关键词选为“类型”后进行分词,生成的文本用来生成云图即可。由于每个词都属于关键词,因此不需要做排除词的操作。
从结果可以看出,怪兽卡的数量最多,而魔法陷阱卡的数量相差不大,而其中,效果怪兽的数量最多。这是因为游戏王每次发行卡包时都是40张怪兽卡、10张魔法卡和10张陷阱卡的,另外也会发行数量不规则的系列卡包,但是这占了少数。
3、卡片名称中哪些词出现的最多?
我们把分词的关键词选为“名称”后进行分词,生成的文本用来生成云图即可。
由结果可以看出,“元素英雄”和“娱乐伙伴”的卡片最多。其实这些词都代表了一个系列的卡片,而“元素英雄”和“娱乐伙伴”系列是游戏王动画中第二代主角和第四部主角使用的系列卡片,加上衍生出的发行卡片,数量自然相对比较多。
五、完整代码
1 #——游戏王卡片信息爬虫—— 2 #——最后修改时间:2017.11.02—— 3 4 import requests 5 import re 6 import jieba 7 import pandas 8 import matplotlib.pyplot as plt 9 from bs4 import BeautifulSoup 10 from wordcloud import WordCloud 11 12 #函数功能:获取卡片的总页数 13 def getpage(): 14 flag1 = '搜索结果 / 约 ' #该变量内容为总页数所在字段文本的前部分文字 15 flag2 = '条' 16 card_page = soup.select('.result')[0].text.lstrip(flag1) #获取到总页数所在的字段文本 17 page_num = card_page.find(flag2) #查找“条”字符所在的位置 18 allpage = int(card_page[0:page_num]) #输出“条”字前面的数字,即总卡片数 19 page = allpage//18 #输出总页数,每页有18条卡片记录 20 return page 21 22 #函数功能:获取魔法陷阱卡的信息 23 def getdetail_MagicTrap(url_MT): 24 resd = requests.get(url_MT) 25 resd.encoding = 'utf-8' 26 soup_MT = BeautifulSoup(resd.text,'html.parser') 27 card_news_MagicTrap = {} 28 for news in soup_MT.select('.card'): 29 card_news_MagicTrap['名称'] = news.select('div')[3].text 30 card_news_MagicTrap['链接'] = url_MT 31 card_type = news.select('div')[12].text 32 card_news_MagicTrap['类型'] = card_type[0:4] 33 card_news_MagicTrap['属性'] = ' ' 34 card_news_MagicTrap['种族'] = ' ' 35 card_effect_text = news.select(".effect")[0].text 36 card_news_MagicTrap['效果'] = card_effect_text[2:len(card_effect_text)-1] 37 return card_news_MagicTrap 38 39 #函数功能:获取怪兽卡的信息 40 def getdetail_Monster(url_M): 41 resd = requests.get(url_M) 42 resd.encoding = 'utf-8' 43 soup_M = BeautifulSoup(resd.text,'html.parser') 44 card_news_Monster = {} 45 for news in soup_M.select('.card'): 46 card_news_Monster['名称'] = news.select('div')[3].text 47 card_news_Monster['链接'] = url_M 48 card_type = news.select('div')[12].text 49 card_news_Monster['类型'] = card_type[0:4] 50 card_news_Monster['属性'] = news.select('div')[24].text 51 card_news_Monster['种族'] = news.select('div')[22].text 52 card_effect_text = news.select(".effect")[0].text 53 card_news_Monster['效果'] = card_effect_text[0:len(card_effect_text)-1] 54 return card_news_Monster 55 56 #函数功能:获取一个页面的卡片信息 57 def onepage(url_page): 58 res = requests.get(url_page) 59 res.encoding = 'utf-8' 60 soup_page = BeautifulSoup(res.text,'html.parser') 61 card_newsls = [] #创建一个列表 62 for allnews in soup_page.select('.card'): 63 if len(allnews.select('h2'))>0: 64 card_maintype = allnews.select('span')[0].text 65 url = allnews.select('a')[0]['href'] 66 card_url = ('http://www.ourocg.cn{}'.format(url)) 67 if card_maintype in card_judge: #这里用来判断卡片是怪兽卡还是魔法陷阱卡 68 card_newsls.append(getdetail_MagicTrap(card_url)) 69 else: 70 card_newsls.append(getdetail_Monster(card_url)) 71 #append():接受一个参数,这个参数可以是任何数据类型,并且简单地追加到list的尾部 72 return card_newsls 73 74 card_judge = '魔法陷阱' #该字符串变量用于判断卡片是何类卡片 75 76 news_cardtotal = [] #存放所有卡片信息 77 url_cards = 'http://www.ourocg.cn/Cards/Lists-5-1' #网站首页 78 news_cardtotal.extend(onepage(url_cards)) #把新list中的每个元素添加到原list中 79 80 res_card = requests.get(url_cards) 81 res_card.encoding = 'utf-8' 82 soup = BeautifulSoup(res_card.text,"html.parser") 83 84 for i in range(2,getpage()+1): #通过循环获取首页之后页数的卡片信息列表 85 url_list = 'http://www.ourocg.cn/Cards/Lists-5-{}'.format(i) 86 news_cardtotal.extend(onepage(url_list)) 87 88 df = pandas.DataFrame(news_cardtotal) #转换成表格型数据(DataFrame型) 89 df.to_excel('card_info.xlsx') #以excel表的形式保存结果 90 91 for j in range(len(news_cardtotal)): 92 if j>0: 93 race = race+news_cardtotal[j].get('种族') 94 race = race+' ' 95 else: 96 race = news_cardtotal[j].get('种族') 97 race = race+' ' 98 words = jieba.lcut(race) #进行文本分词 99 100 ls = [] #存放词组文本 101 counts = {} #存放词组的计数 102 for word in words: 103 ls.append(word) 104 if len(word) == 0: 105 continue 106 else: 107 counts[word] = counts.get(word,0)+1 #计数字典 108 items = list(counts.items()) #计数列表 109 items.sort(key = lambda x:x[1], reverse = True) #排序 110 for j in range(10): 111 word , count = items[j] 112 print ("{:<10}{:>5}".format(word,count)) 113 114 cy = WordCloud().generate(race) #词云的制作与显示 115 plt.imshow(cy) 116 plt.axis("off") #不显示标尺 117 plt.show()