照片由 Olena Bohovyk 在 Unsplash 提供。
《介绍》你有没有想过畅销书单上哪种颜色占主导地位?在我的最新项目中,我着手探讨的就是这个问题:几十年来畅销书封面的主要颜色。我的目标是揭示哪些颜色更突出,以及如何将这些发现有效可视化。结果不仅令人着迷,还为图书封面设计和更广泛的出版行业动态提供了宝贵的洞见。
数据收集:幕后一窥在这篇博客文章的结尾,你会发现一个完整的可视化,捕捉了六十年来畅销书的独特色彩模式。每一本“书”代表一年,每种颜色代表了该年每周最畅销书籍的主要封面设计。这个可视化展示了颜色趋势和封面风格如何随时间演变——所有这些都在一个充满活力的单一书架上。通过此篇文章中的步骤,你可以看到数据是如何被收集、处理并以视觉形式展示的,这将帮助你创造一个关于书籍趋势的视觉之旅。
为了踏上这段多彩的旅程,我收集了关于书籍标题、作者以及这些书籍登顶畅销书榜单的具体时间段的数据。大部分信息来源于维基百科(链接),这是一个文学数据的宝库。此外,我还从各种在线平台获取了书籍封面,以确保数据集的丰富。这个过程包括了网页抓取,一种能从网络自动收集相关信息的强大技术。
下面是一个代码片段,展示如何从维基百科页面爬取畅销书列表:
1. 导入库模块# 导入requests库
import requests
# 导入pandas库,并使用别名pd
import pandas as pd
# 导入BeautifulSoup库
from bs4 import BeautifulSoup
我们现在来导入需要用到的模块。
requests
用来发起 HTTP 请求,获取网页内容。pandas
帮助我们将数据整理成结构化的格式。BeautifulSoup
是来自bs4
模块的用于解析 HTML 和 XML 文档的库,使从网页中提取特定信息变得更加容易。
# 从网页抓取HTML源代码
# 从德语维基百科获取畅销浪漫小说列表的HTML源代码
r = requests.get('https://de.wikipedia.org/wiki/Liste_der_meistverkauften_Belletristikbücher_in_Deutschland')
html = r.text
在这个部分中,我们通过 requests.get()
向指定的 URL 发送一个 GET 请求,该 URL 包含了德国最畅销小说的列表。服务器的响应结果,即页面的 HTML 文本,被存储在变量 r
中,我们用 r.text
提取响应的文本。
# 将 HTML 网站解析为文本
parsed_html = BeautifulSoup(html, 'html.parser')
site = parsed_html.get_text()
在这里,我们通过传递获取的HTML内容给BeautifulSoup
对象来创建一个名为BeautifulSoup
的对象,这让我们能够解析HTML结构。然后,我们使用get_text()
方法将整个解析后的HTML转换为纯文本,这会给我们一个包含网页上所有文本的单一字符串。
# 定义相关年代
decade_list = ['1961', '1971', '1981', '1991', '2001', '2011', '2021']
在这部分,我们定义了一系列感兴趣的年代,以便分析。每个年代都是以字符串形式表示,这将有助于我们提取关于这些时期畅销书的信息。
5. 接下来我们来准备一下数据提取的相关工作 # 初始化循环变量
year = 1960
tuple_list = []
在我们开始提取数据之前,我们将变量 year
设定为 1960,作为我们的开始。首先,我们还会创建一个空列表 tuple_list
来存储提取的书籍数据元组。
for idx, decade in enumerate(decade_list):
# 检查是否到了最后一个条目
if idx == len(decade_list) - 1:
break
# 获取相关十年的索引并提取字符串
first_idx = site.find(decade_list[idx] + ' ff.[Bearbeiten | Quelltext bearbeiten]')
second_idx = site.find(decade_list[idx + 1] + ' ff.[Bearbeiten | Quelltext bearbeiten]')
decade_string = site[first_idx:second_idx]
decade_string_split = decade_string.split('\n')
在这个块里,我们循环遍历 decade_list
中的每个十年。我们首先检查以确保是否到了最后,以防止数组越界。
接下来,我们通过搜索特定的文本模式,在网页上找到当前十年代部分的起始和结束索引。然后,提取与该十年代对应的子字符串,并使用split('\n')
(即拆分换行符)将其拆分成单独的行。
# 初始化十年循环变量
add_next = False
empty_count = 0
# 遍历每个十年条目数据
for row in decade_string_split:
if add_next:
tuple_list.append((first_entry, row, year))
add_next = False
如果 len(row) > 4:
如果 row[0] 不是'0123456789'中的一个:
如果 empty_count 大于等于2:
year += 1
empty_count = 0
first_entry = row
add_next = True
否则:
empty_count += 1
这里,我们初始化两个变量:add_next
,这是一个用来控制何时追加数据的标志,以及empty_count
,它用来记录连续的空行数。
然后我们遍历每行(row
),其中每一行来自分割后的字符串(这里指十年字符串)。如果add_next
为True
,则我们将一个包含first_entry
(书名)、row
(当前时间范围或详情)和year
的元组添加到tuple_list
中。
我们还检查row
的长度是否超过4,以确保内容有意义。如果该行没有以数字开头(表示可能是书名),并且如果有两个或更多的空行(表示该年条目已经结束),我们就把年份加1。如果该行有效,我们就将其设为下一次迭代的first_entry
。
df_books = pd.DataFrame(tuple_list, columns=['title', 'time_range', 'year'])
# 将元组列表转换为数据框并保存为Excel文件
df_books.to_excel('books.xlsx')
最后,我们将 tuple_list
转换为一个 Pandas DataFrame,列标签分别为 'title'、'time_range' 和 'year'。这种格式便于数据处理和分析。这种结构在第一次提到时也可以解释为数据框(DataFrame),以保持术语的一致性。然后将数据框保存到名为 books.xlsx
的 Excel 文件中,使得提取的数据易于使用。
对于图片抓取(图像抓取),我使用了与之前抓取维基百科数据相同的方法。我在线访问了多个图像来源网站,为每本书找合适的封面图,提供了多样化的视觉效果,使每本书都有一个合适的封面图。
数据准备:良好的组织至关重要一旦我收集了数据,就发现数据并不完全一致。有些条目需要修正,还有一些图片没有找到。这个手动数据清洗的过程教会了我一个重要的教训:干净且结构良好的数据是任何有洞察力分析的基础。没有这一步,几乎无法得出有意义的结论。
识别书籍封面的主打颜色为了分析书籍封面的视觉影响,我使用了Color Thief库来提取每本书封面的主要颜色。这涉及从指定目录加载封面图片并处理它们以识别其主要颜色。这里是我为此目的用到的代码片段:
from colorthief import ColorThief
import os
import pandas as pd
cover_list = os.listdir('Cover')
cover_color_list = []
for cover in cover_list:
# 加载封面图片
color_thief = ColorThief('Cover/' + cover)
# 获取主颜色
dominant_color = color_thief.get_color(quality=1)
# 保存颜色信息
cover_color_list.append([int(cover[:-4]), dominant_color])
df = pd.DataFrame(cover_color_list, columns=['ID', 'RGB'])
畅销书架上的书的可视化
为了实现这一可视化,我想要创造一个既美观又有信息量的作品。我选用了创意书架的形式来展示畅销书的主要颜色。每个架子代表一年,每一本书象征着一周。每一本书的颜色代表了该周畅销书的主要颜色。为了增添一丝趣味性,我让书脊的高度和宽度变化无常,模仿了真实书架的不规则性。
代码逐步解析代码首先导入了所需的模块,包括os
、ColorThief
、pandas
、matplotlib
和numpy
。这些模块让代码可以管理文件路径、从图片中提取主要颜色、处理CSV文件,并创建可视化。
import os # 导入操作系统相关的模块
from colorthief import ColorThief # 从colorthief导入ColorThief类,用于提取图片颜色
import pandas as pd # 导入pandas库,用于数据处理
import matplotlib.pyplot as plt # 导入matplotlib.pyplot子库,用于绘图
import numpy as np # 导入numpy库,用于数值计算
from matplotlib.collections import PatchCollection # 导入PatchCollection类,用于管理绘图元素集合
from matplotlib.patches import Rectangle # 导入Rectangle类,用于绘制矩形
import matplotlib.patheffects as path_effects # 导入path_effects模块,用于设置绘图路径效果
接下来,代码从名为final_table.csv
的文件里加载图像文件名,该文件包含书封图片文件名。然后,它创建了一个名为image_list
的列表,其中包含从该CSV文件中提取的所有图片文件名。
# 读取CSV文件
img_df = pd.read_csv('final_table.csv')
# 将图像列转换为列表
image_list = list(img_df['img'])
为了从书封中提取主要颜色,代码创建了一个空列表cover_color_list
。然后,它会遍历列表image_list
中的每个图片。对于每张书封图片,它尝试从名为ScrapedCover/
的目录中加载图片。如果图片找不到,代码会跳过这张书封,然后继续执行,使用pass
语句。成功加载图片后,代码会使用ColorThief
来提取主要颜色,并将这个颜色和书封的ID(从文件名中提取)添加到cover_color_list
中。
cover_color_list = [] # 封面颜色列表
for cover in image_list: # 对于每张封面图片
try:
color_thief = ColorThief('ScrapedCover/' + cover) # 尝试获取封面图片的颜色贼对象
except:
pass
dominant_color = color_thief.get_color(quality=1) # 获取封面图片的主色调
cover_color_list.append([int(cover[:-4]), dominant_color]) # 将封面编号和颜色添加到封面颜色列表
一旦所有主要颜色都被提取出来,代码将 cover_color_list
转换为名为 df
的 pandas 数据框(DataFrame),并保存为名为 RGB.csv
的文件。之后,程序再读取该 CSV 文件,并将内容读入名为 rgb_df
的数据框,它包含了提取的 RGB 颜色值。
df = pd.DataFrame(cover_color_list, columns=['ID', 'RGB'])
df.to_csv('RGB.csv')
rgb_df = pd.read_csv('RGB.csv', index_col='ID')
接下来的步骤是创建主导颜色的可视化。代码初始化了一个30乘180大小的图。它准备了两个空列表:fc
,用于存储矩形的面颜色;以及boxes
,用于存放矩形。变量year
设为1962,标志着可视化的开始年份。
fig, ax = plt.subplots(1, figsize=(30, 180)) # 创建一个图形对象和轴对象,设置图形尺寸为30x180
fc = [] # 初始化一个空列表用于存储后续数据
boxes = [] # 初始化一个空列表用于存储后续数据
year = 1962 # 设定起始年份为1962
代码接下来进入一个循环,从索引 11 开始处理 rgb_df
中的每本封面。在循环中,它检查当前索引是否为 52 的倍数,表示新一年的开始。当条件满足时,代码生成一个随机整数数组,并归一化这些值,同时计算年标签和用于分隔年份的黑色矩形的尺寸。然后年份在下一次迭代中增加。
# 遍历rgb_df中从第11个元素开始的'RGB'列
for idx, book in enumerate(rgb_df['RGB'][11:]):
# 当索引为52的倍数时
if idx % 52 == 0:
# 生成随机数组
r_arr = np.random.randint(1, 3, 52)
# 数组归一化
r_arr = r_arr / r_arr.sum()
x = 0.0185
# 创建矩形对象
box = Rectangle((x - 0.005, 0.9835 - (idx // 52) * 0.0165), 0.972, 0.0015)
# 将'fc'列表中添加'k'
fc.append('k')
# 将矩形对象添加到'boxes'列表中
boxes.append(box)
# 在图表中添加文本
ax.text(x - 0.015, 0.988 - (idx // 52) * 0.0165, str(year), {'rotation': 90.0, 'fontsize': 22.0})
# 更新年份
year += 1
对于每一本书的封面,代码计算其RGB值,并将这些值归一化到0到1的范围内。这些值被添加到存储矩形颜色的fc
列表中。代码根据之前生成的随机值来计算每个矩形的位置和大小,模拟书架上书本大小的不一。每个矩形都会被添加到boxes
列表中。
facecolor = [round(i / 255, 2) for i in eval(book)] # `eval(book)`执行book中的表达式,并计算颜色值
fc.append(facecolor) # 将facecolor添加到fc列表中
y = 0.985 - (idx // 52) * 0.0165 # 计算y坐标
x_length = 0.962 * r_arr[idx - (idx // 52) * 52] # 计算x长度
y_length = np.random.normal(0.0115, 0.001) # 从均值为0.0115,标准差为0.001的正态分布中随机抽取一个数
box = Rectangle((x, y), x_length, y_length) # 创建一个矩形,其中x和y为矩形的左下角坐标,x_length和y_length为矩形的宽和高
x += x_length # 将x值增加x长度
boxes.append(box) # 将矩形框添加到boxes列表中
处理完所有书封后,代码会创建一个包含所有矩形及其对应颜色的 PatchCollection
。然后将该集合添加到图表中。关闭坐标轴,以获得更简洁的视觉效果。并在图表底部添加一个描述性标题,描述了该可视化图,展示了从1962年到2020年每周德国排名第一畅销书的主要颜色。
pc = PatchCollection(boxes, facecolor=fc, edgecolor='k')
ax.add_collection(pc)
ax.axis('off')
ax.text(0.5, 0.005, '1962年至2020年间德国每週畅销书榜首颜色,', {'fontsize': 60, 'horizontalalignment': 'center', 'fontweight': 'bold'})
整个过程产生了书籍封面的主要颜色如何随着时间推移而变化的视觉展示,以多彩书架的形式生动地展示了书籍设计的趋势。
我学到的那些.这个项目是一次丰富的学习经历,将教育与创造力完美融合。我通过使用Matplotlib获得了创建吸引人视觉效果的图形的实践经验,同时增强了我在数据清洗和准备方面的技能。此外,我首次尝试收集图像数据,这为我未来可能涉及的项目提供了宝贵的见解。每一步都让我认识到清晰和美观在数据展示中的重要性。
展望:接下来是什么?虽然当前的可视化为我们揭示了一些有趣的问题,但仍有许多地方可供探索。未来的分析可以深入探讨颜色对畅销书商业成功的影响——例如,某些颜色是否更可能带来更高的销量?此外,研究不同类别中的颜色趋势可能揭示出一些有趣的现象,而调查书籍平均占据榜首位置的时间变化可能会为不断变化的图书市场带来新的见解。
结尾颜色与畅销书设计不仅仅关乎美学;它反映了消费者偏好及文化变迁的深层走向。通过详细的分析和引人入胜的可视化,我们可以发现不仅有助于我们理解图书市场的走向,还可以指导作者和出版商创作吸引读者眼球的书封设计。所以,敬请期待更多精彩发现,我将继续揭开这个多彩话题的更多层面!
如果你有任何想法、问题或分析想要分享,非常欢迎大家在评论区里和我交流!一起探索畅销书的世界吧!
共同学习,写下你的评论
评论加载中...
作者其他优质文章