2203840423邓佳湧python期末作业
【python爬虫课程设计】博物馆数据爬取+可视化
选题背景介绍
博物馆网络爬虫的选题背景源于对文化遗产数字化管理的需求。随着互联网的普及,越来越多的博物馆将其藏品信息、展览信息等发布在官网上,这些信息对于研究者、策展人、教育者等都具有很高的价值。为了更高效地收集、整理这些信息,同时避免手工整理的繁琐与错误,博物馆网络爬虫应运而生。它能够自动抓取博物馆网站上的信息,并按照一定的格式整理成数据,为博物馆的数字化建设提供有力支持。因此,博物馆网络爬虫的选题背景与文化遗产数字化管理的趋势紧密相关,是信息技术与文化遗产保护相结合的产物。
选题意义
数据整合与共享:通过博物馆网络爬虫,可以将分散在各个博物馆网站上的信息进行整合,形成一个庞大的数据库,方便研究者、策展人、教育者等用户进行数据查询、分析和利用。这有助于促进文化遗产的共享与传播,提高文化资源的利用效率。
学术研究支持:博物馆网络爬虫能够抓取博物馆网站上的文物信息、展览信息等,为学术研究提供丰富的数据支持。通过分析这些数据,可以深入挖掘文化遗产的历史价值、艺术价值、科学价值等,推动学术研究的深入发展。
数字化管理助力:博物馆网络爬虫的应用有助于推动博物馆的数字化管理进程。通过自动化的数据采集和整理,可以减轻博物馆工作人员的工作负担,提高工作效率。同时,数字化管理有助于保护文化遗产,减少文物损坏和遗失的风险。
促进跨学科合作:博物馆网络爬虫的研发和应用涉及多个学科领域,如计算机科学、历史学、艺术学等。这种跨学科的合作有助于推动不同领域之间的交流与融合,促进学科创新与发展。
HTML页面解析
爬虫流程介绍
目标网站 | https://www.kfsbwg.com/#/collectionSelection |
目标信息 | 博物馆藏品数据 |
工具库 | requests库,json库,BeautifulSoup库,pandas库,matplotlib库 |
通过模拟浏览器发送请求,并解析返回的数据。代码分为以下几个步骤:
- 设置伪装的请求头信息,包括 User-Agent(浏览器名称)、Referer(来源页面地址)和 Cookie。
|
2. 使用伪装的请求头发送 GET 请求,获取网页内容并解析。
# 发送 GET 请求,使用伪装的请求头信息 res = requests.get('https://www.kfsbwg.com/#/collectionSelection', headers=fake_headers) # 添加headers参数 # 使用 XPath 解析返回的网页内容 xpath_data = etree.HTML(res.text) |
3. 提取网页中的类别信息。
categories_div = xpath_data.xpath('//div[contains(@class,"tab")]')
# 使用 XPath 根据特定规则提取包含类别信息的 `<div>` 元素。
4. 遍历多个页面,获取每个页面的数据,并将数据存储到 DataFrame 中。
(1) 该代码片段遍历了页码 1 到 13, 爬取藏品的基本信息。创建一个空的 DataFrame,用于存储数据。
- 该代码片段对进入二级页面,爬取每一个藏品的类别信息
- 保存数据到本地文件。
爬虫程序设计
Part1: 爬取博物馆里的数据并保存为“collect_data”文件
Part2: 各个年代的藏品数量的折线图
Part3: 各个年代的藏品数量的折线图
Part4:文物级别的数量 绘制条形图
Part5: 一级文物中 按类别 画环形图
Part6: 对藏品介绍绘制词云图
爬虫程序设计全部代码如下:
- import requests # 用来爬虫
- import json
- import matplotlib.pyplot as plt
- from lxml import etree
- import re
- import pandas as pd
- import jieba #分词库
- from wordcloud import WordCloud #词云库
- # 读取停用词列表
- def get_stopword_list(file):
- with open(file, 'r', encoding='utf-8') as f: # 打开停用词列表文件,使用utf-8编码
- stopword_list = [word.strip('\n') for word in f.readlines()] # 逐行读取文件内容,去除换行符并添加到停用词列表中
- return stopword_list
- def crawling():
- # 设置伪装的请求头信息,包括 User-Agent(浏览器名称)、Referer(来源页面地址)和 Cookie。伪装成浏览器
- cookie = 'gr_user_id=d60a8db5-dbfc-44f3-a589-6917209bca37; douban-fav-remind=1; ' \
- '_vwo_uuid_v2=D5FD64F0F6B348BAC2D886383CA278542|628badf33068a364065510f6d407970d; ' \
- 'bid=mAIlAppZr5U; ll="118284"; __utmz=30149280.1685770417.9.3.utmcsr=cn.bing.com|utmccn=(referral)|utmcmd=referral|utmcct=/; ' \
- '__utma=30149280.678090237.1611223142.1685770417.1685867065.10; __utma=223695111.1624638446.1617981352.1658030776.1685867065.3; ' \
- '__utmz=223695111.1685867065.3.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __yadk_uid=NnfgVqOPYabVP9s7AkUc0PpTGwjICPYJ; ' \
- '_pk_id.100001.4cf6=90c321bacf3d595a.1685867070.; ' \
- '__gads=ID=8d4773a0f5c6d0bb-2268ff2418dd009b:T=1680945664:RT=1685867186:S=ALNI_MYgy1qlxmUrwLO53cc5XfkdoSLdcw; ' \
- '__gpi=UID=0000069a7ea9078e:T=1655104213:RT=1685867186:S=ALNI_MakTxIc3yRNFn3nppio26xG6RXfEA; ' \
- '_pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1686124106%2C%22https%3A%2F%2Fcn.bing.com%2F%22%5D; ' \
- '_pk_ses.100001.4cf6=1; ap_v=0,6.0'
- fake_headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', #浏览器名称
- 'Referer': 'https://www.kfsbwg.com/#/home', #来源页面的地址
- 'Cookie': cookie #cookie
- }
- # 发送 GET 请求,使用伪装的请求头信息
- res = requests.get('https://www.kfsbwg.com/#/collectionSelection', headers=fake_headers) # 添加headers参数
- # 使用 XPath 解析返回的网页内容
- xpath_data = etree.HTML(res.text)
- #代理ip全局参数
- ip = "" # 请求时使用的ip值
- # 代理ip列表,来自免费代理ip网站
- # 通过代理ip爬虫防止被封禁
- # 去除停用词
- stopwords = STOPWORDS
- # stopwords.add('')
- #代理ip全局参数
- ip = "" # 请求时使用的ip值
- # 代理ip列表,来自免费代理ip网站
- # 通过代理ip爬虫防止被封禁
- agency_ip = [
- 'http://49.86.58.36:9999',
- 'http://182.84.144.91:3256',
- 'http://171.35.213.44:9999',
- 'http://114.67.108.243:8081',
- 'http://211.24.95.49:47615',
- 'http://192.168.110.254:8088'
- ]
- proxies = {'http': 'http://192.168.110.254:8088'}
- # 提取类别: 使用 XPath 根据特定规则提取包含类别信息的 `<div>` 元素。
- categories_div = xpath_data.xpath('//div[contains(@class,"tab")]')
- pages = range(1,14) #遍历页码范围,该代码片段遍历了页码 1 到 13。
- data = pd.DataFrame(columns=['藏品名称' ,'年代' ,'类别','文物级别' ,'尺寸描述' ,'长' ,'宽' ,'高' ,'介绍' ]) # 创建一个空的 DataFrame,用于存储数据
- for page in pages: #循环遍历每一页。
- # 循环每一页。一页12个。url是找到的数据接口api。 该网站数据是从接口动态获取的,不能靠爬取网页获得信息。
- url = "https://www.kfsbwg.com/api/exhibit/list?p=w&qu_id=0&is_home=2&page="+str(page)+"&limit=12" # 构建请求 URL,该 URL 包含页码等参数
- # 请求数据:# 构建请求 URL,并发送 GET 请求
- response = requests.get(url, headers=fake_headers) # 发送 GET 请求,使用伪装的请求头信息
- content = response.content # 将获取到的二进制数据转换为 UTF-8 字符串。
- text = content.decode('utf-8')
- # 将获取的数据解析成json对象,解析返回的 JSON 数据,并遍历每个数据项
- json_object = json.loads(text)
- #解析返回的 JSON 数据,将其转换为 Python 对象。
- collect_list = json_object["data"]["list"] #从 JSON 对象中获取数据列表。
- # 当前页藏品数据数量
- len_ = len(collect_list)
- print("数据长度:"+str(len_))
- # 遍历每个数据项,并提取所需的字段信息
- for collect in collect_list:
- exhibit_id = collect['exhibit_id'] # id
- print("爬取:exhibit_id:" + str(exhibit_id))
- name = collect['exhibit_name'] # 藏品名称
- print("爬取:exhibit_name:" + str(name))
- year = collect['year'] # 年份
- print("爬取:year:" + str(year))
- leve = collect['leve'] # 等级
- print("爬取:leve:" + str(leve))
- size_desc = collect['size_desc'] # 尺寸
- print("爬取:size_desc:" + str(size_desc))
- length = collect['length'] # 长
- print("爬取:length:" + str(length))
- width = collect['width'] # 宽
- print("爬取:width:" + str(width))
- height = collect['height'] # 高
- print("爬取:height:" + str(height))
- intro = collect['intro'] # 介绍
- print("爬取:intro:" + str(intro))
- # 前往二级接口(文物详情)爬取类别信息,并解析返回的数据
- sub_url = "https://www.kfsbwg.com/api/exhibit/detail?p=w&exhibit_id="
- # 构建二级接口的请求 URL,根据展品 ID 获取展品详情
- sub_url = sub_url + str(exhibit_id)
- # 请求数据
- sub_response = requests.get(sub_url, headers=fake_headers) # 发送 GET 请求,使用伪装的请求头信息
- content = sub_response.content
- text = content.decode('utf-8')
- # 将获取的数据解析成json对象
- sub_json_object = json.loads(text)
- # 从 JSON 对象中获取类型信息
- cate_name = sub_json_object["data"]["cate_name"]
- # '藏品名称' ,'年代' ,'类别','文物级别' ,'尺寸描述' ,'长' ,'宽' ,'高' ,'介绍'
- data.loc[len(data)] = [name,year,cate_name,leve,size_desc,length,width, height,intro] # 将提取的数据存储到 DataFrame 中
- print('藏品名称: %s ,年代: %s ,类别: %s ,文物级别: %s ,尺寸描述: %s ,长: %s ,宽: %s ,高: %s ,介绍: %s '
- %(name,year,cate_name,leve,size_desc,length,width, height,(intro[:10]+'...')))
- print("一共爬取:" + str(len(data)) + "行藏品数据信息")
- # 保存数据
- data.to_csv('./collect_data.csv',encoding='utf-8')
- '''
- 1. 按年代分组 数量:折线图
- 2. 按类别分组,扇形图
- 3. 按文物级别,条形图
- 4. 一级文物中朝代 柱状图
- 5. 一级文物中类别 多柱状图
- 6. 文物描述 词云图
- '''
- # 1. 各个年代的藏品数量的折线图
- def plot1():
- print("准备绘制图1:各个年代的藏品数量的折线图")
- plt.rcParams['font.sans-serif'] = ['Simhei'] # 显示中文
- '''读取数据'''
- collectData = pd.read_csv('./collect_data.csv')
- # 年代按顺序排序:
- yearSet = ['新石器时代','商','西周', '春秋','战国','秦', '汉', '三国','东晋', '南北朝', '北魏', '东魏',
- '隋','唐','五代十国','宋','北宋','南宋','辽', '金','元', '明', '清', '民国','其他']
- group = collectData.groupby(by='年代') # 数据按年代分组
- yearNum = []
- for i in range(0,len(yearSet)):
- yearCollect = group.get_group(yearSet[i])
- yearNum.append(len(yearCollect))
- # 横坐标数据
- X = range(0,len(yearSet))
- fig = plt.figure(figsize=(12, 4)) # 绘图大小
- subfig = plt.subplot(1, 1, 1) # 子图对象
- subfig.plot(X, yearNum, '-*', color='lightgreen', linewidth=2, markersize='8')
- plt.xticks(X, yearSet,rotation=40)
- for a, b in zip(X, yearNum):
- '''在数据点上进行标注'''
- plt.text(a, b+2, b, ha='center', va='bottom')
- # 纵竖线
- subfig.set_ylim((0, 50))
- subfig.set_xlabel(r'年代')
- subfig.set_ylabel(r'数量')
- subfig.set_title('不同年代的藏品数量统计')
- plt.tight_layout()
- plt.savefig('./不同年代的藏品数量统计.png')
- plt.show()
- “针对藏品进行分类”
- # 2. 各个类型的藏品数量的扇形图
- def plot2():
- print("准备绘制图2:各个类型的藏品数量的扇形图")
- plt.rcParams['font.sans-serif'] = ['Simhei'] # 显示中文
- '''读取数据'''
- collectData = pd.read_csv('./collect_data.csv')
- group = collectData.groupby(by='类别') # 数据按类别分组
- i = 0
- cate_list = set(list(collectData['类别'])) # 获取一共有哪些类别
- cate_count = list(range(0,len(cate_list)))
- for a_cate in cate_list:
- cate_df = group.get_group(a_cate)
- # 类别数量
- cate_count[i] = len(cate_df)
- i += 1
- fig = plt.figure(figsize=(8, 6))
- subfig = plt.subplot(1, 1, 1)
- subfig.pie(cate_count, labels=cate_list, autopct='%.1f%%')
- subfig.set_title('各类别的藏品数量扇形图')
- subfig.set_ylabel('')
- plt.savefig('./各类别的藏品数量扇形图.png')
- plt.show()
- “针对文物进行分类”
- # 3. 文物级别的数量 绘制条形图
- def plot3():
- print("准备绘制图3:文物级别的数量条形图")
- plt.rcParams['font.sans-serif'] = ['Simhei'] # 显示中文
- '''读取数据'''
- collectData = pd.read_csv('./collect_data.csv')
- group = collectData.groupby(by='文物级别') # 数据按文物级别分组
- i = 0
- level_list = set(list(collectData['文物级别'])) # 获取一共有几级文物级别
- level_count = list(range(0, len(level_list)))
- for a_level in level_list: # 循环每个等级
- level_df = group.get_group(a_level)
- # 等级数量
- level_count[i] = len(level_df)
- i += 1
- fig = plt.figure(figsize=(8, 4))
- subfig = plt.subplot(1, 1, 1)
- y = list(range(len(level_list)))
- subfig.barh(y, level_count, height=0.5)
- subfig.set_xlabel('个')
- subfig.set_title('不同文物级别的藏品数量')
- plt.legend()
- plt.yticks(y, level_list)
- for a, b in zip(level_count,y):
- '''标注数据'''
- “并且对数据进行分类”
- plt.text(a+2, b, a, ha='center', va='bottom')
- plt.savefig('./不同文物级别的藏品数量.png')
- plt.show()
- “针对一级文物进行分类”
- # 4. 筛选出一级文物,统计一级文物里各个朝代的文物数量
- def plot4():
- print("准备绘制图4:统计一级文物里各个朝代的文物数量")
- plt.rcParams['font.sans-serif'] = ['Simhei'] # 显示中文
- '''读取数据'''
- collectData = pd.read_csv('./collect_data.csv')
- group = collectData.groupby(by='文物级别') # 数据按文物级别分组
- level_1 = group.get_group('一级文物') # 获取一级文物的数据(DataFrame)
- group = level_1.groupby(by='年代') # 将一级文物的数据按年代分组
- i = 0
- year_list = set(list(level_1['年代'])) # 获取一共有哪些朝代
- year_count = list(range(0, len(year_list))) # 列表放每个朝代的藏品数量
- for a_year in year_list:
- year_df = group.get_group(a_year)
- # 类别数量
- year_count[i] = len(year_df)
- i += 1
- x = list(range(0, len(year_list)))
- fig = plt.figure(figsize=(10, 8))
- subfig = plt.subplot(1, 1, 1)
- subfig.bar(x, year_count,color='lightgreen')
- for a, b in zip(x,year_count):
- '''标注数据'''
- plt.text(a, b+0.1, b, ha='center', va='bottom')
- subfig.set_ylim((0, 5.5))
- plt.xticks(x, year_list) # rotation=-50
- subfig.set_ylabel(r'藏品数量')
- subfig.set_title('一级文物中来自各个朝代的藏品的数量')
- plt.savefig('./一级文物中来自各个朝代的藏品的数量.png')
- plt.show()
- “针对一级文物进行分类”
- # 5.一级文物中 按类别 画环形图
- def plot5():
- print("准备绘制图5:统计一级文物里各个类别的文物数量环形图")
- plt.rcParams['font.sans-serif'] = ['Simhei'] # 显示中文
- '''读取数据'''
- collectData = pd.read_csv('./collect_data.csv')
- group = collectData.groupby(by='文物级别') # 数据按文物级别分组
- level_1 = group.get_group('一级文物') # 获取一级文物的数据(DataFrame)
- group = level_1.groupby(by='类别') # 将一级文物的数据按年代分组
- i = 0
- cate_list = set(list(level_1['类别'])) # 获取一共有哪些朝代
- cate_count = list(range(0, len(cate_list))) # 列表放每个朝代的藏品数量
- for a_cate in cate_list:
- cate_df = group.get_group(a_cate)
- # 类别数量
- cate_count[i] = len(cate_df)
- i += 1
- x = list(range(0, len(cate_list)))
- fig = plt.figure(figsize=(7, 7))
- subfig = plt.subplot(1, 1, 1)
- # 要把离心率设置远一点,怕第二个饼图挡住这个百分率
- subfig.pie(cate_count, pctdistance=0.8, autopct='%.1f%%')
- # 所谓的环形图,就是再画一个比上个图小的饼图,并且为白色,所以半径要小
- subfig.pie([1], radius=0.6, colors='w')
- subfig.legend(cate_list, loc='upper left')
- subfig.set_title('一级文物中各个类别的藏品的占比')
- plt.savefig('./一级文物中各个类别的藏品的占比.png')
- plt.show()
- ## 6. 对藏品介绍绘制词云图
- def plot6():
- print("准备绘制图6:对藏品介绍绘制词云图")
- plt.rcParams['font.sans-serif'] = ['Simhei'] # 显示中文
- '''读取数据'''
- collectData = pd.read_csv('./collect_data.csv')
- intro = list(collectData['介绍'])
- new_text = []
- all_text = ''
- for str in intro:
- # 去除无用的标点符号,分词和预处理
- remove_chars = '[brp·’!"\#$%&\'()#!()*+,-./:;<=>?\@,:。?¥★、….>【】[]《》?“”‘’\[\\]^_`{|}~]+'
- str = re.sub(remove_chars, "", str)
- str.replace('br', '').replace('p', '')
- result = jieba.cut(str)
- a = ','.join(result).split(',')
- new_text.append(a)
- # 去除停用词
- stopwords = get_stopword_list('stopwords.txt') # 获取停用词列表,加载停用词的路径为'stopwords.txt'
- stopwords.append('厘米') # 添加额外的停用词
- stopwords.append('这件')
- stopwords.append('')
- for s in new_text:
- for word in s:
- if word not in stopwords: # 判断分词后的词语是否在停用词表内
- if word != '\t' and len(word) > 1: # 去除空字符和长度小于2的词语
- all_text += word + ' ' # 拼接所有分词后的词语
- # 词云图
- font = "C:\\Windows\\Fonts\\simsun.ttc" # 词云的中文字体所在路径
- wc = WordCloud(
- scale=4, # 调整图片大小---(如果设置太小图会很模糊)
- font_path=font, # 使用的字体库
- max_words=300, # 词云显示的最大词数
- margin=1, # 字体之间的间距
- background_color='white', # 背景颜色
- max_font_size=60,
- # min_font_size=1,
- # stopwords=STOPWORDS, #屏蔽的内容
- collocations=True, # 避免重复单词
- # width=1600, height=1200 # 图像宽高,字间距
- )
- wc.generate(all_text)
- wc.to_file('词云图.jpg') # 保存到当地文件
- if __name__ == '__main__':
- crawling()
- plot1()
- plot2()
- plot3()
- plot4()
- plot5()
- plot6()
总结:
博物馆网络爬虫与数据可视化相结合,为文化遗产的数字化管理提供了有力支持。通过博物馆网络爬虫,实现对博物馆网站数据的自动抓取和整理,形成结构化的数据集。数据可视化则将这些数据以直观、生动的方式呈现出来,便于用户理解和分析。这种结合不仅提高了数据处理的效率和准确性,还为用户提供了更加丰富和深入的洞察力。通过数据可视化,用户可以快速识别数据中的模式、趋势和关联,进一步挖掘文化遗产的价值。这种技术有助于推动学术研究的进步,促进跨学科的合作,同时也为公众提供了更加便捷的文化资源访问途径。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix