整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

50行Python代码绘制数据大屏,这个可视化框架真的太神了

者: 俊欣

来源:关于数据分析与可视化

今天小编来为大家安利另外一个用于绘制可视化图表的Python框架,名叫Dash,建立在FlaskPlotly.js以及React.js的基础之上,在创建之出的目的是为了帮助前端知识匮乏的数据分析人员,以纯Python编程的方式快速制作出交互特性强的数据可视化大屏,在经过多年的迭代发展,如今不仅仅可以用来开发在线数据可视化作品,即便是轻量级的数据仪表盘、BI应用甚至是博客或者是常规的网站都随处可见Dash框架的影子,今天小编就先来介绍一下该框架的一些基础知识,并且来制作一个简单的数据可视化大屏。

Dash框架中的两个基本概念

我们先来了解一下Dash框架中的两个基本概念

  • Layout
  • Callbacks

Layout顾名思义就是用来设计可视化大屏的外观和布局,添加一些例如下拉框、单选框、复选框、输入框、文本框、滑动条等组件,其中Dash框架对HTML标签也进行了进一步的封装,使得我们直接可以通过Python代码来生成和设计每一个网页所需要的元素,例如

<div>
    <h1>Hello World!!</h1>
    <div>
        <p>Dash converts Python classes into HTML</p>
    </div>
</div>

我们转化成DashPython结构就是

html.Div([
    html.H1('Hello Dash'),
    html.Div([
        html.P('Dash converts Python classes into HTML'),
    ])
])

Callbacks也就是回调函数,基本上是以装饰器的形式来体现的,实现前后端异步通信的交互,例如我们在点击按钮或者下拉框之后出现的功能就是通过回调函数来实现的。

安装和导入模块

在导入模块之前,我们先用pip命令来进行安装,

! pip install dash   
! pip install dash-html-components
! pip install dash-core-components                           
! pip install plotly

然后我们导入这些刚刚安装完的模块,其中dash-html-components用来生成HTML标签,dash-core-components模块用来生成例如下拉框、输入框等组件,这里我们还需要用到plotly模块,因为我们需要用到的数据来自该模块,里面是一众互联网公司过去一段时间中股价的走势

import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.graph_objects as go
import plotly.express as px

读取数据并且绘制折线图

那么我们读取数据并且用plotly来绘制折线图,代码如下

app = dash.Dash()   #实例化Dash
df = px.data.stocks() #读取股票数据 

def stock_prices():
    # 绘制折线图
    fig = go.Figure([go.Scatter(x=df['date'], y=df['AAPL'],
                                line=dict(color='firebrick', width=4), name='Apple')
                     ])
    fig.update_layout(title='股价随着时间的变幻',
                      xaxis_title='日期',
                      yaxis_title='价格'
                      )
    return fig
    
app.layout = html.Div(id='parent', children=[
    html.H1(id='H1', children='Dash 案例一', style={'textAlign': 'center',
                                                 'marginTop': 40, 'marginBottom': 40}),
    dcc.Graph(id='line_plot', figure=stock_prices())
])

if __name__ == '__main__':
    app.run_server()

我们点击运行之后会按照提示将url复制到浏览器当中便可以看到出来的结果了,如下所示

从代码的逻辑上来看,我们通过Dash框架中的Div方法来进行页面的布局,其中有参数id来指定网页中的元素,以及style参数来进行样式的设计,最后我们将会指出来的图表放在dcc.Graph()函数当中。

添置一个下拉框

然后我们再添置一个下拉框,当我们点击这个下拉框的时候,可是根据我们的选择展示不同公司的股价,代码如下

dcc.Dropdown(id='dropdown',
             options=[
                 {'label': '谷歌', 'value': 'GOOG'},
                 {'label': '苹果', 'value': 'AAPL'},
                 {'label': '亚马逊', 'value': 'AMZN'},
             ],
             value='GOOG'),

output

options参数中的label对应的是下拉框中的各个标签,而value对应的是DataFrame当中的列名

df.head()

output

添加回调函数

最后我们将下拉框和绘制折线图的函数给连接起来,我们点击下拉框选中不同的选项的时候,折线图也会相应的产生变化,

@app.callback(Output(component_id='bar_plot', component_property='figure'),
              [Input(component_id='dropdown', component_property='value')])
def graph_update(dropdown_value):
    print(dropdown_value)
    # Function for creating line chart showing Google stock prices over time
    fig = go.Figure([go.Scatter(x=df['date'], y=df['{}'.format(dropdown_value)],
                                line=dict(color='firebrick', width=4))
                     ])
    fig.update_layout(title='股价随着时间的变幻',
                      xaxis_title='日期',
                      yaxis_title='价格'
                      )
    return fig

我们看到callback()方法中指定输入和输出的媒介,其中Input参数,里面的component_id对应的是下拉框的id也就是dropdown,而Output参数,当中的component_id对应的是折线图的id也就是bar_plot,我们来看一下最后出来的结果如下

最后,全部的代码如下所示

ava中解析HTML框架之Jsoup

场景是这样的,本来是想申请一个第三方支付接口判断用户支付是否成功,后来发现不需要申请接口也可以通过订单号查询页面获取支付结果,这样就可以直接解析html来判断支付结果了,这就引入了本文的主题,Jsoup解析html

当然jsoup不只有上面的应用场景,它还有一个应用场景,就是爬虫!

题外话:上面场景中,使用支付接口其实才是最稳当的办法,但是支付接口申请周期长,而且一些情况下并不是免费的,再者一些支付接口只支持一种语言,可能和本项目不是一个语言(比如项目是Java的,但是人家提供的支付接口只支持PHP),这样增加了系统复杂度,如果业务量大且要求准确的场景下应当使用支付接口,否则可以取巧解析html,解析html有一个不好的地方就是如果html结构变化了,那么接口就得重写,都有优缺点,看场景选择。

Jsoup简介与使用

官网:https://jsoup.org/

jsoup 提供了简便的API,使用了HTML5 DOM方法和CSS选择器用来解析HTML。其实现了WHATWG HTML5 规范,像浏览器一样解析HTML。

  • 从文件,URL,字符串抓取和解析HTML
  • 使用DOM遍历或者CSS选择器来查找和提取数据
  • 操作HTML元素,属性和文字
  • 清除用户提交的安全白名单以外的内容,以防止XSS攻击
  • 美化HTML

引入依赖

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.11.2</version>
</dependency>

解析HTML

  1. 从字符串解析
String html = "<html><head><title>First parse</title></head>"
  + "<body><p>Parsed HTML into a doc.</p></body></html>";
Document doc = Jsoup.parse(html);
  1. 从URL解析
// 简洁版
Document doc = Jsoup.connect("http://example.com/").get();
String title = doc.title();

//完整版
doc = Jsoup.connect("http://example.com")
  .data("query", "Java")
  .userAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36")
  .cookie("auth", "token")
  .timeout(3000)
  .post();
  1. 从文件解析
File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

提取HTML

  • 遍历HTML
File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

Element content = doc.getElementById("content");
Elements links = content.getElementsByTag("a");
for (Element link : links) {
  String linkHref = link.attr("href");
  String linkText = link.text();
}
  • 提取数据
String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";
Document doc = Jsoup.parse(html);
Element link = doc.select("a").first();

String text = doc.body().text(); // "An example link"
String linkHref = link.attr("href"); // "http://example.com/"
String linkText = link.text(); // "example""

String linkOuterH = link.outerHtml(); 
    // "<a href="http://example.com"><b>example</b></a>"
String linkInnerH = link.html(); // "<b>example</b>"
  • 相对路径转换成绝对路径,一些a标签使用的是相对路径,下面的代码可以将其转换成绝对路径
Document doc = Jsoup.connect("http://jsoup.org").get();

Element link = doc.select("a").first();
String relHref = link.attr("href"); // == "/"
String absHref = link.attr("abs:href"); // "http://jsoup.org/"

CSS选择器

Jsoup支持CSS选择器,用的是 Element.select(String selector)方法

File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

Elements links = doc.select("a[href]"); // a with href
Elements pngs = doc.select("img[src$=.png]");
  // img with src ending .png

Element masthead = doc.select("div.masthead").first();
  // div with class=masthead

Elements resultLinks = doc.select("h3.r > a"); // direct a after h3

如何快速定位页面上元素的内容?答案是打开Chrome,按F12打开开发者工具,定位到想要的DOM节点,右键,copy,选择Copy selector,即可生成CSS选择器,类似于body > div > div.content > div.col2 > div > h3:nth-child(10)

遗憾的是Jsoup不支持Xpath选择器,但是早就有人意识到这个问题了,所以诞生了JsoupXpath

JsoupXpath 是一款纯Java开发的使用xpath解析提取html数据的解析器,针对html解析完全重新实现了W3C XPATH 1.0标准语法,xpath的Lexer和Parser基于Antlr4构建,html的DOM树生成采用Jsoup,故命名为JsoupXpath. 为了在java里也享受xpath的强大与方便但又苦于找不到一款足够好用的xpath解析器,故开发了JsoupXpath。JsoupXpath的实现逻辑清晰,扩展方便, 支持完备的W3C XPATH 1.0标准语法,W3C规范:http://www.w3.org/TR/1999/REC-xpath-19991116 ,JsoupXpath语法描述文件Xpath.g4

项目地址:https://github.com/zhegexiaohuozi/JsoupXpath

感兴趣的可以看一下测试用例:里面包含了大量的使用场景:https://github.com/zhegexiaohuozi/JsoupXpath/blob/master/src/test/java/org/seimicrawler/xpath/JXDocumentTest.java

操作HTML

jsoup可以在插入、删除、提取HTML,直接看例子代码

  • 设置属性
//设置属性
doc.select("div.comments a").attr("rel", "nofollow");
doc.select("div.masthead").attr("title", "jsoup").addClass("round-box");
  • 插入html
//插入html
Element div = doc.select("div").first(); // <div></div>
div.html("<p>lorem ipsum</p>"); // <div><p>lorem ipsum</p></div>
div.prepend("<p>First</p>");
div.append("<p>Last</p>");
// now: <div><p>First</p><p>lorem ipsum</p><p>Last</p></div>

Element span = doc.select("span").first(); // <span>One</span>
span.wrap("<li><a href='http://example.com/'></a></li>");
// now: <li><a href="http://example.com"><span>One</span></a></li>
  • 设置文本
//设置文本
Element div = doc.select("div").first(); // <div></div>
div.text("five > four"); // <div>five > four</div>
div.prepend("First ");
div.append(" Last");
// now: <div>First five > four Last</div>

避免XSS攻击( cross-site scripting )

String unsafe = 
  "<p><a href='http://example.com/' onclick='stealCookies()'>Link</a></p>";
String safe = Jsoup.clean(unsafe, Whitelist.basic());
// now: <p><a href="http://example.com/" rel="nofollow">Link</a></p>

参考

  • https://jsoup.org/
  • http://www.wanghaomiao.cn/archives/25/
  • https://github.com/zhegexiaohuozi/JsoupXpath

人都是产品经理旗下【起点学院】推出产品经理“365天”成长计划,BAT大牛带你学产品!

掏出手机,打开虎嗅,开始了软文之旅。虎嗅APP不得不说是我了解互联网世界的一扇窗口。

若干篇咨询过后,停顿下来,闲暇之余,端详这款熟悉而又陌生的面孔。

APP:虎嗅;

版本号:v2.5.0

体验设备:iphone6;

操作系统:ios8.3;

虎嗅APP的主界面除了内容区域,最为突出的就是logo,一个汉堡菜单按钮和一个个人中心的按钮。首先对这款APP的汉堡菜单,进行一下简单的初体验。

为了更好的理解,我采用了Javascript对象的的思想去解析。

先科普一下,在 JavaScript 中,对象是拥有属性和方法的数据。属性是一种值,方法是执行的动作。

汉堡菜单是近年来APP比较流行的菜单方式,由于移动端设备的尺寸有限,不能将更多的区域留给除了展示内容以外的其他选项,因此汉堡菜单应用而生。它将一些功能选项隐藏在一个菜单按钮之下,用户不仅可以通过此菜单找到相应的功能选项,更主要的是节省了大部分的屏幕面积留作内容展示,毕竟无论是什么产品,内容为王。但是汉堡菜单也不是万能的,也有它的缺点,那就是功能选项的曝光率低,隐藏深,不易用户点击。关于各种各样导航方式的优缺点在人人都是PM社区中有提到,大家可以搜索查看。

今天我们暂且不说虎嗅APP功能选项采用汉堡菜单是否合适,我们先看一下汉堡菜单下的内容:

菜单选项下面分了六个小的功能项:看点,观点,读点,收藏,待读和搜索。

看点:主要是互联网门户网站(比如网易科技,腾讯科技)的最新商业资讯,经过编辑精心筛选和加工上线;

观点:大多是虎嗅注册会员的原创文文章;

读点:互联网领域最新的图书。

这三个标签将虎嗅的内容分成了三大部分,如果以面向对象编程的思想去理解的话,这就应该是虎嗅APP内容(对象)的一个属性,简而言之,菜单这里的看点,观点和读点全都是对于整个APP内容的分类,也就是一个APP内容整体的标签。但是,收藏和待读这两个功能选项(或者称为方法)的对象却是用户。也就是收藏和待读这种行为的发起者是用户,这里感觉放在个人区域更为合适,也便于理解。

针对以上理解重新梳理一下虎嗅APP的信息架构:

此处着重说明一下为啥要将搜索功能拿出来。搜索,顾名思义,利用用户提供的关键字,为用户提供精确匹配的结果。搜索功能很多,那么就虎嗅APP的搜索功能,我想到两种使用场景:

(1)通过关键词搜索来获取相关的文章。这是用户通过搜索功能精确匹配自己感兴趣的内容,一次来达到碎片化时间的最优化利用;

(2)通过搜索功能来获取历史文章。某天,我突然需要一张图标来佐证我的观点,而这张图标隐隐约约在虎嗅APP上面见过,但是并不是最新的新闻资讯。一方面可以查看自己是否收藏该文,另一方面就可以利用搜索功能粗略匹配关键字,进而缩短查找时间。

以上就是个人对虎嗅APP的一些简单的思考,按照上面的分析,日后继续进行APP的界面设计,欢迎大家提出建议