社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

使用 Python 制作“历史上的今天”

进击的Coder • 4 年前 • 665 次点击  
这是「进击的Coder」的第 550 篇技术分享
作者:周萝卜
来源:萝卜大杂烩

“

阅读本文大概需要 10 分钟。


”


大家好,今天分享一个通过 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/

End

崔庆才的新书《Python3网络爬虫开发实战(第二版)》已经正式上市了!书中详细介绍了零基础用 Python 开发爬虫的各方面知识,同时相比第一版新增了 JavaScript 逆向、Android 逆向、异步爬虫、深度学习、Kubernetes 相关内容,‍同时本书已经获得 Python 之父 Guido 的推荐,目前本书正在七折促销中!

内容介绍:《Python3网络爬虫开发实战(第二版)》内容介绍



扫码购买







点个在看你最好看

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/125517