如何使用Python自动制作《历史上的今天》宣传图片

大家好,今天分享一个通过 Python 自动创建相关图片的教程,而这个相关图片就是《历史上的今天》,那么为啥是历史呢,因为我是一个历史迷,从小就喜欢啃历史书,随着年龄的增长,这份热情还是没有减退~

好了闲话不都说,我们直接上干货


数据获取

首先就是数据哪里来,我试过使用网上的一些免费历史查询接口,但是效果都不理想,这些接口不是太不稳定,就是数据不友好。最后我还是选择了一个精简的网站,直接扒网站页面信息即可

http://jintian.160.com/

网站很简单,也没有任何反爬措施,我们直接抓取数据

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")

好了,今天的分享就到这里,想要体验的同学,可以访问这个网址

http://47.105.185.84:8080/

看到这里的朋友,如果你觉得满意请务必点个+关注支持下。

私信获取完整代码~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容