大家好,今天分享一个通过 Python 自动创建相关图片的教程,而这个相关图片就是《历史上的今天》,那么为啥是历史呢,因为萝卜哥是一个历史迷,从小就喜欢啃历史书,随着年龄的增长,这份热情还是没有减退~
好了闲话不都说,我们直接上干货
数据获取
首先就是数据哪里来,我试过使用网上的一些免费历史查询接口,但是效果都不理想,这些接口不是太不稳定,就是数据不友好。最后我还是选择了一个精简的网站,直接扒网站页面信息即可
网站很简单,也没有任何反爬措施,我们直接抓取数据
def get_data(month, day): result_dict = dict() for i in range(1, 3): url = "http://jintian.160.com/ashx/GreatThing.ashx?act=getgreatthinglist&page=%s&m=%s&d=%s&c=" % (str(i), month, day) data = requests.get(url) html = BeautifulSoup(data.json()["data"]) data_li = html.find_all("li") for li in data_li: result = deal_some(li.text) tmp = result.split(" ") year = tmp[0].split("年")[0] new_day = tmp[0].split("年")[1] result_dict[year] = tmp[1] return result_dict, new_day
这里提供了月和天的变量,就是为了后面我们做成 web 服务时可以方面的获取任何时间的历史信息
然后我们再编写一个函数,把获取到的数据转化成 DataFrame 格式
def gen_df(result_dict): df = pd.DataFrame.from_dict(result_dict, orient='index', columns=['事件']) df = df.reset_index().rename(columns={'index': '年份'}) return df
图片制作
对于最终生成的图片,使用的是 PyEcharts 制作,核心代码复用了《可以叫我才哥》公众号号主才哥
的相关代码,下面我们简单解析下相关代码
首先我们明确图片基础是 Line 类型,没错就是我们平时用的最多的折线图!
先生成 Y 轴 数据
def gen_y(data): y_data = [] counter = 0 position = ['left', 'right'] for idx, row in data.iterrows(): msg = '{bbb|%s}\n{aaa|%s}' % (row['年份'], row['事件']) l_item = opts.LineItem( name=10, value=counter, symbol='emptyCircle', symbol_size=10, label_opts=opts.LabelOpts( is_show=True, font_size=16, position=position[counter%2], formatter=msg, rich = { 'aaa': { 'fontSize': 18, 'color': 'red', 'fontWeight':'bold', 'align':position[(counter+1)%2], }, 'bbb': { 'fontSize': 15, 'color': '#000', 'align':position[(counter+1)%2]}} ) ) y_data.append(l_item) counter+=1 return y_data
使用系列配置pyecharts.options
当中的LineItem
类,不过很奇怪的是,这个类竟然在 PyEcharts 官网中找不到,还是查看了官方源码才大概了解其作用
class LineItem(BasicOpts): def __init__( self, name: Union[str, Numeric] = None, value: Union[str, Numeric] = None, *, symbol: Optional[str] = "circle", symbol_size: Numeric = 4, symbol_rotate: Optional[Numeric] = None, symbol_keep_aspect: bool = False, symbol_offset: Optional[Sequence] = None, label_opts: Union[LabelOpts, dict, None] = None, itemstyle_opts: Union[ItemStyleOpts, dict, None] = None, tooltip_opts: Union[TooltipOpts, dict, None] = None, ): self.opts: dict = { "name": name, "value": value, "symbol": symbol, "symbolSize": symbol_size, "symbolRotate": symbol_rotate, "symbolKeepAspect": symbol_keep_aspect, "symbolOffset": symbol_offset, "label": label_opts, "itemStyle": itemstyle_opts, "tooltip": tooltip_opts, }
大概的意思就是批量的设置 Line 的属性,这里不得不吐槽下 PyEcharts 官方文档,真的该好好维护下啊~(如果我这里理解的不对,欢迎指出,咱们一起学习~)
也就是说上面的代码生成了一系列数据,这些数据 X 轴都是 10,Y 轴是从 0 开始,一直到循环的最后一个值递增,同时还通过LabelOpts
设置了 msg 信息,也就是我们最终看到的历史信息
XY 轴数据设置好之后,就是其他的样式调整了
def myLine(y, day): line = Line( init_opts=opts.InitOpts( theme='light', width='1000px', height='800px' ) ) line.add_xaxis( [''] ) line.add_yaxis( '', y, linestyle_opts={ 'normal': { 'width': 4, # 设置线宽 'color':'red', 'shadowColor': 'rgba(155, 18, 184, .3)', # 阴影颜色 'shadowBlur': 10, # 阴影大小 'shadowOffsetY': 10, # Y轴方向阴影偏移 'shadowOffsetX': 10, # x轴方向阴影偏移 } }, itemstyle_opts={ 'normal': { 'color':'red', 'shadowColor': 'rgba(155, 18, 184, .3)', # 阴影颜色 'shadowBlur': 10, # 阴影大小 'shadowOffsetY': 10, # Y轴方向阴影偏移 'shadowOffsetX': 10, # x轴方向阴影偏移 } }, tooltip_opts=opts.TooltipOpts(is_show=False) ) line.set_global_opts( xaxis_opts=opts.AxisOpts(is_show=False, type_='category'), yaxis_opts=opts.AxisOpts(is_show=False, type_='value', max_=len(y)), title_opts=opts.TitleOpts( title="历史上的今天-%s" % day, pos_left='center', pos_top='2%', title_textstyle_opts=opts.TextStyleOpts(color='red', font_size=20), subtitle="公众号:萝卜大杂烩 出品" ), toolbox_opts=opts.ToolboxOpts( is_show=True, orient="vertical", feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="jpeg", title="保存为jpeg", background_color="white"), restore=opts.ToolBoxFeatureRestoreOpts(), data_view=opts.ToolBoxFeatureDataViewOpts(), data_zoom=opts.ToolBoxFeatureDataZoomOpts(), magic_type=opts.ToolBoxFeatureDataViewOpts(), brush=opts.ToolBoxFeatureDataZoomOpts(), ) ), graphic_opts=[ opts.GraphicGroup( graphic_item=opts.GraphicItem(id_='1',left="center", top="center", z=-1), children=[# tokyo opts.GraphicImage(graphic_item=opts.GraphicItem(id_="logo", left='center', z=-1), graphic_imagestyle_opts=opts.GraphicImageStyleOpts( image="1.jpg", width=800, height=1000, opacity=0.1,) ) ] ) ] ) return line
这里考验的就是 PyEcharts 的熟练程度了,反正萝卜我是不达标的,这样样式如果是我自己,可能要对照官网调整大半天,哈哈哈
好了,图片制作就介绍到这里
部署 Web 服务
因为有个需求就是每天获取图片,然后转发到微信群,那么最方便的方法就是部署成 Web,在公网上访问即可
对于这种临时的,个人网站,还是推荐使用 Flask,毕竟快就是优势(这里的快指的是编写快,上手快~)
导入 Flask 和 PyEcharts 相关库
from flask import Flask from jinja2 import Markup, Environment, FileSystemLoader from pyecharts.globals import CurrentConfig import datetime from flask import request # 关于 CurrentConfig,可参考 [基本使用-全局变量] CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates"))
然后设置路由函数
def gen_line(month, day): result_dict, day = get_data(month, day) df = gen_df(result_dict) y = gen_y(df) line = myLine(y, day) return line @app.route("/") def index(): month = request.args.get("month") day = request.args.get("day") if month and day: c = gen_line(month, day) return Markup(c.render_embed()) i = datetime.datetime.now() c = gen_line(i.month, i.day) return Markup(c.render_embed())
这样就好了,通过 Flask 自带的 Web 容器启动即可
if __name__ == "__main__": app.run(debug=True, host="0.0.0.0")
好了,今天的分享就到这里,想要体验的同学,可以访问这个网址