分类档案: 大数据

将HTML表转换为Excel下载HTTP响应:慢速OLAP DB连接的黑客

总览

善科 (“Good fox”日本)是一个报告系统(在这里查看Github上的代码) 一世’是在过去两周内在Mozilla创建的。基本上,我的非技术同事对Tableau感到非常沮丧(“INNER JOIN和OUTER JOIN有什么区别?”),我决定为他们创建一个简单的仪表板界面。

它是一个简单的引导程序前端,它包含数据库的活动统计信息 赞助瓷砖. You can drill down to 每 tile or 客户/partner 和 pivot 通过 things like locale, 国家 和 日期.

善科’s堆栈(从高到低)

一项新功能

加载分析页面之一时,将显示一个表格。我的同事希望能够将数据下载到Excel。我想出了4种可能的方法来实现此目的:

  1. 只需重新运行查询,在后端将结果格式设置为csv,然后将其保存并在window.open()文件位置即可。
  2. 自动保存每个分析请求服务器请求中的数据,并定期清除旧文件。
  3. 使用像 ExcelBuilder
  4. Send the 数据 back to the server, 格式 it, 和 then back to the 客户 via an 如果 rame

哪个是最佳解决方案?

  1. 这是有问题的,因为我们的重点是查询速度。 redshift数据库是一个 OLAP面向列的数据库和仅追加。这意味着添加数据的速度非常快,但是查询速度却很慢(通常超过6秒)。是的,它正在处理数十亿行这样令人难解的行,但是就等待这么长时间的用户体验而言并不是那么好。’如果他们已经有数据,则要再等待6秒钟重新运行分析。
  2. 听起来这可能最终会在客户端上存储大量数据,但是效果很好。在安全性方面,我’我不确定数据应该留在用户身上’不需要PC。
  3. 这没有’t work out so well –在Firefox中,文件命名错误。将来,我’d根据分析参数命名文件,例如<client>-<date>-<country>.xls
  4. 这是最怪异的解决方案,但是它有效! 烧瓶 在本地运行,因此运行速度非常快。具有文件许可权的JQuery / 的JavaScript并不会带来很多麻烦,而且您可以在服务器上轻松地操纵数据这一事实也很不错。

解决方案4

该过程如下“Download 对于 Excel” button is clicked:

  1. 使用JavaScript引用HTML表并将其转换为数组数组
  2. 将iframe附加到DOM
  3. 将带有POST操作和隐藏字段的表单附加到iframe
  4. 将表内容插入隐藏字段’s 值
  5. 提交表格
  6. 让Flask接收POST请求并将信息格式化为CSV
  7. 返回带有包含CSV的文件附件的HTTP响应

让’s implement it

添加此功能以获取表内容并将其放入数组中
的JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
功能 convert_table_to_array() {
//将当前表转换为列表列表
可耐 = 文件.getElementById(“ impressions_table”) //该表在zenko中将始终称为this
//将表格转换为列表列表(即数组数组)
变种 数据 = [];
//元数据
col_count = 可耐.孩子们[0].孩子们[0].孩子们.长度 //列数
行 _count = 可耐.孩子们[1].孩子们.长度 //行数
//获取标题(即包含列标题的第一行)
标头_cells = 可耐.孩子们[0].孩子们[0].孩子们
标头 = []
对于 (i=0;i<标头_cells.长度;i ++ ) {
标头.(标头_cells[i].textContent)
}
数据 .(标头)
//遍历每一行
行 _cells = 可耐.孩子们[1].孩子们
对于 (i=0;i< 行 _cells.长度;i ++ ) {
= 可耐.孩子们[1].孩子们[i].孩子们
//获取行中的每个单元格
行 _content = []
对于 (j=0;j<.长度;j ++ ) {
cell_content = [j].textContent.更换( “,” , ) //一些文字输入已经包含一个逗号,使事情变得混乱
行 _content.(cell_content)
}
数据 .( 行 _content)
}
返回 数据
}

在JQuery中,有多种方法可以使用 可重复的.()  但我遇到了麻烦,仅使用.children引用细胞就容易得多。

这就是所谓的按钮单击事件
的JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
功能 download_xls() {
//将当前表下载为excel文件
 
//1. Create an 如果 rame
如果 rame = 文件.createElement(“ 如果 rame”)
如果 rame.setAttribute(“宽度”, 1)
如果 rame.setAttribute(“高度”, 1)
如果 rame.setAttribute(“框架边框”, 0)
如果 rame.setAttribute( “ src” , “关于:空白”)
 
//2. Create a 形成 that can send 数据 to 烧瓶
形成 = 文件.createElement(“形成”)
形成 .setAttribute(“方法”, “ POST”)
形成 .setAttribute(“行动”, “ / download_excel”)
 
//3. Put the table 数据 在 to a hidden field 在 that 形成
数据 = 文件.createElement(“输入”)
数据 .setAttribute(“类型”, “隐”)
数据 .setAttribute(“值”, convert_table_to_array())
数据 .setAttribute(“名称”, “数据”)
//4. Append these new elements to the DOM
形成 . 应用程式 endChild( 数据 )
如果 rame. 应用程式 endChild( 形成 )
文件. 身体 . 应用程式 endChild( 如果 rame)
//5. Send off the 数据
形成 .提交()
}

然后,(本地运行中的)Flask将在以下位置接收POST请求: /download_excel . 让’s set up the 路线 :

烧瓶路线
蟒蛇
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#接受POST数据(大于GET数据)
@ 应用程式 . 路线 ('/ download_excel', 方法=[ '得到' , 'POST'])
定义 download_excel():
“”“根据收到的帖子信息创建文件下载”“”
#POST数据可通过request.form上的字典进行访问
数据 = 请求. 形成 [“数据”]
数据 = 数据 . 分裂 ( “,” ) #以逗号分隔
#数据是列表的列表,例如[[1,2],[3,4]],但不幸的是发送了
#如  [1,2,3,4]。但是,我们知道有6列,因此我们可以将其拆分
#以列表理解进入六十年代
数据 = [ 数据 [x:x+6] 对于 x Xrange(0, ( 数据 ), 6)]
#N现在每个人都用逗号重新加入,所以它很csv-ish
数据 = “ \ n” . 加入 ([ ',' . 加入 (x) 对于 x 数据 ])
响应 = make_response( 数据 )
#返回带有附件的HTTP响应
响应.标头[“内容处置”] = “附件;文件名= 数据 .csv”
返回 响应

现在,当用户单击按钮时:

下载Excel链接

他们立即得到:

下载弹出窗口

对不起,我可以’无法显示Excel中的数据,因为数据不是’目前还没有公开。所有代码均可用 在github上!

然而,一件奇怪的事情是表格没有’t出现在检查器中(在Chrome或Firefox中):

在检查器中不可见

不过,您可以使用一些相当长的吸气剂来访问它:

why.jpg

未来功能 

  • 这些文件的名称可能比data.csv更直观–可能是网址中出现的各种情况的组合’s query string
  • 可容纳超过6行的表格。使用不同的定界符(例如,“###”.
  • 如果有好处,请创建一个.xls文件而不是CSV文件

蟒蛇多处理中的共享变量以预映射/减少

I’我最近在Python中使用了多处理库,并开始使用共享变量功能。它可以改变我这样的事情 以前的帖子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
多处理 进口 池子
pymongo 进口 连接
 
定义 多少(server_number):
    “返回集合中有多少个文档”
    c = 连接(“ 192.168.0。” + server_number) #连接到远程数据库
    返回 c. 我的数据库 .我的收藏. 计数 ()
 
池子 = 泳池 (流程=4)
伺服器 = [1,2,3,4]
结果 = 池子 . 地图 (多少,伺服器) #map阶段
池子 .()
 
#减少阶段
结果 = (结果)
 
打印 “您在所有MongoDB服务器上都有{0}个文档!”.格式(结果)

变得更好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
多处理 进口 , 池子
pymongo 进口 连接
 
定义 多少(server_number):
    “返回集合中有多少个文档”
    c = 连接(“ 192.168.0。” + server_number) #连接到远程数据库
    shared_count. + = c. 我的数据库 .我的收藏. 计数 ()
 
#设定
池子 = 泳池 (流程=4)
伺服器 = [1,2,3,4]
shared_count = ( “一世” , 0)
 
结果 = 池子 . 地图 (多少,伺服器) #map阶段
池子 .()
 
打印 “您在所有MongoDB服务器上都有{0}个文档!”.格式(shared_count)

从而省去了还原阶段。如果您有一个共享字典,这特别有用。’从多个服务器重新更新。那里’是另一个可能的共享数据类型,称为Array,正如建议的那样,它是共享数组。 注意: 一个陷阱(我爱上了)认为 “一世”   in ( “一世” , 0)  是变量的名称。其实,它的 类型码 代表“integer”.

但是,还有其他方法可以做到这一点,每种方法都有其自身的取舍:

# 优点 缺点
1 共享文件 易于实施和访问后 非常慢
2 共享的mongoDB文档 易于实施 缓慢不断查询
3 多处理值/数组(此示例) 非常快,易于实施 仅在一台PC上,可以’进程被杀死后无法访问
4 Memcached共享价值 网络方面对于大型分布式数据库很有用,shared.set()函数已经可用 TCP可能会使您慢一点

使用ACORA一次处理数百个停用词

“80%的数据分析是花费在清洁数据上,其中20%的花费是在抱怨清洁数据” – 长社 and 大数据宝

这是我听到的最好的报价之一 PyData 2013。当处理大量数据时,通常只有一小部分与一个数据相关’的分析,尝试清洁它可能会很痛苦。但这也是 基本阶段, 所以让’使它尽可能的无痛。

一个示例是巨大的日志文件。说我们’重新处理多TB的Apache日志文件,如下所示:

1
2
3
4
5
6
7
8
9
10
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-dropdown.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-scrollspy.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-tab.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-tooltip.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-popover.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-button.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-collapse.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-carousel.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:38 -0500] “获取/static/js/bootstrap-typeahead.js HTTP / 1.1” 304 - "http://127.0.0.1/" “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”
127.0.0.1 - - [17/ 七月 /2013:07:22:39 -0500] “ GET /favicon.ico HTTP / 1.1” 404 4025 “-” “ Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_4)AppleWebKit / 537.36(KHTML,例如Gecko)Chrome / 28.0.1500.71 Safari / 537.36”

这是包含数千行的有用数据,我们’d想使用 我之前提到的大文件处理脚本。但是,有些行’不关心–所以你可以写一个简单的条件:

1
2
3
4
打开 ('mylogfile.txt', 'r' ) f:
     对于 线 f:
         如果 “ some_keyword” 线 :
            做点什么()

如果您有2件事情该怎么办’t want 在 每 线 ?

1
2
3
4
打开 ('mylogfile.txt', 'r' ) f:
     对于 线 f:
         如果 (“ some_keyword” 线 ) (“ other_keyword” 线 ):
            做点什么()

如果您有3件事情该怎么办’t want 在 每 线 ?

1
2
3
4
5
6
7
8
9
打开 ('mylogfile.txt', 'r' ) f:
     对于 线 f:
         线 _is_ok = 真正
         对于 停用词 [“ some_keyword”, “ other_keyword”, “不”]:
             如果 停用词 线 :
                 线 _is_ok =
                 打破 #退出循环
         如果 线 _is_ok: #仅在line_is_ok未被篡改的情况下处理行
            做点什么()

但这变得超级低效,有点愚蠢。每个额外的关键字都需要通过该行。使用此代码,基本上所有情况都是最坏的情况。

带上ACORA!

阿科拉 是Stefan Behnel’的库基于 Aho-Corasick字符串匹配算法。无需深入了解其背后的数学原理,它基本上会将您拥有的所有停用词编译为一个über-停用词,这意味着在您的日志文件行中对该停用词进行一次扫描将检查所有停用词。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
阿科拉 进口 AcoraBuilder
 
#创建停用词列表-这些是条目
#我们想忽略的
停用词 = [ '.js' , '.ico', '.css', '.jpg', ...]
 
#将它们输入新的Acora Builder中
停用词_matcher = AcoraBuilder()
对于 停用词 停用词:
    停用词_matcher.(停用词)
 
#now让它构造超级停用词
停用词_matcher = 停用词_matcher. 建立 ()
 
#this使用生成器迭代匹配项
#发现任何东西都会停止
对于 比赛 停用词_matcher.寻找者( 线 _from_log_file):
     打印 “找到匹配:{0}”.格式( 比赛 )

但是我们如何将其从以前集成到行扫描器中呢?像这样!

1
2
3
4
5
6
7
8
打开 ('mylogfile.txt', 'r' ) f:
     对于 线 f:
         线 _is_ok = 真正
         对于 比赛 停用词_matcher.寻找者( 线 ):
             线 _is_ok =
             打破 #退出循环
         如果 线 _is_ok:
            做点什么()

We’ve用单个ACORA匹配器替换了整个多个停用词匹配的for循环。

性能说明

阿科拉很棒,但是如果只有几个停用词或只有几行,性能可能会下降。当您有大约20个以上的停用词并且至少有1000个左右的日志文件行要扫描时,它的性能最佳。

在Python中读取非常大的文件

有很多方法可以读取python中的文件,这有点不直观并且违反了 PEP-8准则。但是,这是最好的:

蟒蛇
1
2
3
打开 (“ my_large_file.txt”, “ r” ) f:
     对于 线 f:
        ( 这里 )

它创建一个生成器,一次只将一行读入内存。一世’我们使用此代码轻松处理了多个千兆字节的文件。使用   很有用,因为它会在紧跟其后的代码结束后自动关闭文件。

人们经常会犯以下错误:

  • 对于 线 f.()  –这将立即加载整个文件并按字符读取
  • 对于 线 f.阅读线()  –这会将整个文件加载到内存中的列表中