Python如何把Spark数据写入ElasticSearch


Posted in Python onApril 18, 2020

这里以将Apache的日志写入到ElasticSearch为例,来演示一下如何使用Python将Spark数据导入到ES中。

实际工作中,由于数据与使用框架或技术的复杂性,数据的写入变得比较复杂,在这里我们简单演示一下。

如果使用Scala或Java的话,Spark提供自带了支持写入ES的支持库,但Python不支持。所以首先你需要去这里下载依赖的ES官方开发的依赖包包。

下载完成后,放在本地目录,以下面命令方式启动pyspark:

pyspark --jars elasticsearch-hadoop-6.4.1.jar

如果你想pyspark使用Python3,请设置环境变量:

export PYSPARK_PYTHON=/usr/bin/python3
理解如何写入ES的关键是要明白,ES是一个JSON格式的数据库,它有一个必须的要求。数据格式必须采用以下格式

{ "id: { the rest of your json}}

往下会展示如何转换成这种格式。

解析Apache日志文件
我们将Apache的日志文件读入,构建Spark RDD。然后我们写一个parse()函数用正则表达式处理每条日志,提取我们需要的字

rdd = sc.textFile("/home/ubuntu/walker/apache_logs")
regex='^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+)\s?(\S+)?\s?(\S+)?" (\d{3}|-) (\d+|-)\s?"?([^"]*)"?\s?"?([^"]*)?"?$'

p=re.compile(regex)
def parse(str):
  s=p.match(str)
  d = {}
  d['ip']=s.group(1)
  d['date']=s.group(4)
  d['operation']=s.group(5)
  d['uri']=s.group(6)
  return d

换句话说,我们刚开始从日志文件读入RDD的数据类似如下:

['83.149.9.216 - - [17/May/2015:10:05:03 +0000] "GET /presentations/logstash-monitorama-2013/images/kibana-search.png HTTP/1.1" 200 203023 "http://semicomplete.com/presentations/logstash-monitorama-2013/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36"']

然后我们使用map函数转换每条记录:

rdd2 = rdd.map(parse)

rdd2.take(1)

[{'date': '17/May/2015:10:05:03 +0000', 'ip': '83.149.9.216', 'operation': 'GET', 'uri': '/presentations/logstash-monitorama-2013/images/kibana-search.png'}]

现在看起来像JSON,但并不是JSON字符串,我们需要使用json.dumps将dict对象转换。

我们同时增加一个doc_id字段作为整个JSON的ID。在配置ES中我们增加如下配置“es.mapping.id”: “doc_id”告诉ES我们将这个字段作为ID。

这里我们使用SHA算法,将这个JSON字符串作为参数,得到一个唯一ID。
计算结果类似如下,可以看到ID是一个很长的SHA数值。

rdd3.take(1)

[('a5b086b04e1cc45fb4a19e2a641bf99ea3a378599ef62ba12563b75c', '{"date": "17/May/2015:10:05:03 +0000", "ip": "83.149.9.216", "operation": "GET", "doc_id": "a5b086b04e1cc45fb4a19e2a641bf99ea3a378599ef62ba12563b75c", "uri": "/presentations/logstash-monitorama-2013/images/kibana-search.png"}')]

现在我们需要制定ES配置,比较重要的两项是:

  • “es.resource” : ‘walker/apache': "walker"是索引,apache是类型,两者一般合称索引
  • “es.mapping.id”: “doc_id”: 告诉ES那个字段作为整个文档的ID,也就是查询结果中的_id

其他的配置自己去探索。

然后我们使用saveAsNewAPIHadoopFile()将RDD写入到ES。这部分代码对于所有的ES都是一样的,比较固定,不需要理解每一个细节

es_write_conf = {
    "es.nodes" : "localhost",
    "es.port" : "9200",
    "es.resource" : 'walker/apache',
    "es.input.json": "yes",
    "es.mapping.id": "doc_id"
  }
    
rdd3.saveAsNewAPIHadoopFile(
    path='-',
   outputFormatClass="org.elasticsearch.hadoop.mr.EsOutputFormat",    keyClass="org.apache.hadoop.io.NullWritable",
    valueClass="org.elasticsearch.hadoop.mr.LinkedMapWritable",
    conf=es_write_conf)

rdd3 = rdd2.map(addID)

def addId(data):
  j=json.dumps(data).encode('ascii', 'ignore')
  data['doc_id'] = hashlib.sha224(j).hexdigest()
  return (data['doc_id'], json.dumps(data))

最后我们可以使用curl进行查询

curl http://localhost:9200s/walker/apache/_search?pretty=true&?q=*
{
    "_index" : "walker",
    "_type" : "apache",
    "_id" : "227e977849bfd5f8d1fca69b04f7a766560745c6cb3712c106d590c2",
    "_score" : 1.0,
    "_source" : {
     "date" : "17/May/2015:10:05:32 +0000",
     "ip" : "91.177.205.119",
     "operation" : "GET",
     "doc_id" : "227e977849bfd5f8d1fca69b04f7a766560745c6cb3712c106d590c2",
     "uri" : "/favicon.ico"
    }

如下是所有代码:

import json
import hashlib
import re

def addId(data):
  j=json.dumps(data).encode('ascii', 'ignore')
  data['doc_id'] = hashlib.sha224(j).hexdigest()
  return (data['doc_id'], json.dumps(data))

def parse(str):
  s=p.match(str)
  d = {}
  d['ip']=s.group(1)
  d['date']=s.group(4)
  d['operation']=s.group(5)
  d['uri']=s.group(6)
  return d  

regex='^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+)\s?(\S+)?\s?(\S+)?" (\d{3}|-) (\d+|-)\s?"?([^"]*)"?\s?"?([^"]*)?"?$'

p=re.compile(regex)

rdd = sc.textFile("/home/ubuntu/walker/apache_logs")

rdd2 = rdd.map(parse)

rdd3 = rdd2.map(addID)

es_write_conf = {
    "es.nodes" : "localhost",
    "es.port" : "9200",
    "es.resource" : 'walker/apache',
    "es.input.json": "yes",
    "es.mapping.id": "doc_id"
  }
   
rdd3.saveAsNewAPIHadoopFile(
    path='-',
   outputFormatClass="org.elasticsearch.hadoop.mr.EsOutputFormat",    keyClass="org.apache.hadoop.io.NullWritable",
    valueClass="org.elasticsearch.hadoop.mr.LinkedMapWritable",
    conf=es_write_conf)

也可以这么封装,其实原理是一样的

import hashlib
import json
from pyspark import Sparkcontext

def make_md5(line):
  md5_obj=hashlib.md5()
  md5_obj.encode(line)
  return md5_obj.hexdigest()

def parse(line):
  dic={}
  l = line.split('\t')
  doc_id=make_md5(line)
  dic['name']=l[1]
  dic['age'] =l[2]
  dic['doc_id']=doc_id
  return dic  #记得这边返回的是字典类型的,在写入es之前要记得dumps

def saveData2es(pdd, es_host, port,index, index_type, key):
  """
  把saprk的运行结果写入es
  :param pdd: 一个rdd类型的数据
  :param es_host: 要写es的ip
  :param index: 要写入数据的索引
  :param index_type: 索引的类型
  :param key: 指定文档的id,就是要以文档的那个字段作为_id
  :return:
  """
  #实例es客户端记得单例模式
  if es.exist.index(index):
    es.index.create(index, 'spo')
  es_write_conf = {
    "es.nodes": es_host,
    "es.port": port,
    "es.resource": index/index_type,
    "es.input.json": "yes",
    "es.mapping.id": key
  }

  (pdd.map(lambda _dic: ('', json.dumps(_dic))))  #这百年是为把这个数据构造成元组格式,如果传进来的_dic是字典则需要jdumps,如果传进来之前就已经dumps,这便就不需要dumps了
  .saveAsNewAPIHadoopFile(
    path='-',
    outputFormatClass="org.elasticsearch.hadoop.mr.EsOutputFormat", keyClass="org.apache.hadoop.io.NullWritable",
    valueClass="org.elasticsearch.hadoop.mr.LinkedMapWritable",
    conf=es_write_conf)
  )
if __name__ == '__main__':
  #实例化sp对象
  sc=Sparkcontext()
  #文件中的呢内容一行一行用sc的读取出来
  json_text=sc.textFile('./1.txt')
  #进行转换
  json_data=json_text.map(lambda line:parse(line))

  saveData2es(json_data,'127.0.01','9200','index_test','index_type','doc_id')

  sc.stop()

看到了把,面那个例子在写入es之前加了一个id,返回一个元组格式的,现在这个封装指定_id就会比较灵活了

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python微信企业号开发之回调模式接收微信端客户端发送消息及被动返回消息示例
Aug 21 Python
Python简单计算文件MD5值的方法示例
Apr 11 Python
linux下python使用sendmail发送邮件
May 22 Python
pandas DataFrame实现几列数据合并成为新的一列方法
Jun 08 Python
python 读取文件并替换字段的实例
Jul 12 Python
tensorflow 中对数组元素的操作方法
Jul 27 Python
django使用LDAP验证的方法示例
Dec 10 Python
Pandas之Fillna填充缺失数据的方法
Jun 25 Python
ZABBIX3.2使用python脚本实现监控报表的方法
Jul 02 Python
Django如何实现网站注册用户邮箱验证功能
Aug 14 Python
解决tensorflow训练时内存持续增加并占满的问题
Jan 19 Python
Python数据可视化之绘制柱状图和条形图
May 25 Python
Python virtualenv虚拟环境实现过程解析
Apr 18 #Python
python实现贪吃蛇双人大战
Apr 18 #Python
Python的in,is和id函数代码实例
Apr 18 #Python
Python json读写方式和字典相互转化
Apr 18 #Python
Python figure参数及subplot子图绘制代码
Apr 18 #Python
Python数组拼接np.concatenate实现过程
Apr 18 #Python
Python稀疏矩阵及参数保存代码实现
Apr 18 #Python
You might like
PHP使用pear自带的mail类库发邮件的方法
2015/07/08 PHP
php生成二维码
2015/08/10 PHP
PHP5.6读写excel表格文件操作示例
2019/02/26 PHP
Laravel框架查询构造器简单示例
2019/05/08 PHP
PHP 8新特性简介
2020/08/18 PHP
点击标签切换和自动切换DIV选项卡
2014/08/10 Javascript
jQuery 实现自动填充邮箱功能(带下拉提示)
2014/10/14 Javascript
jQuery操作cookie方法实例教程
2014/11/25 Javascript
javascript弹出窗口实现代码
2015/11/12 Javascript
js匿名函数作为函数参数详解
2016/06/01 Javascript
vuejs2.0实现一个简单的分页示例
2017/02/22 Javascript
JavaScript实现两个select下拉框选项左移右移
2017/03/09 Javascript
ES6新特性之模块Module用法详解
2017/04/01 Javascript
vue-prop父组件向子组件进行传值的方法
2018/03/01 Javascript
js实现简单选项卡功能
2020/03/23 Javascript
python通过pil模块获得图片exif信息的方法
2015/03/16 Python
让Python代码更快运行的5种方法
2015/06/21 Python
用Python抢过年的火车票附源码
2015/12/07 Python
python使用matplotlib绘图时图例显示问题的解决
2017/04/27 Python
Python编程实现正则删除命令功能
2017/08/30 Python
Python3之文件读写操作的实例讲解
2018/01/23 Python
Python循环中else,break和continue的用法实例详解
2019/07/11 Python
python 数据类型强制转换的总结
2021/01/25 Python
Html5无刷新修改browser Url的方法
2014/01/15 HTML / CSS
Html5 页面适配iPhoneX(就是那么简单)
2019/09/05 HTML / CSS
html5默认气泡修改的代码详解
2020/03/13 HTML / CSS
护士求职推荐信范文
2013/11/23 职场文书
成考报名单位证明范本
2014/01/16 职场文书
房地产财务管理制度
2014/02/02 职场文书
会计专业自我鉴定
2014/02/10 职场文书
初中家长评语和期望
2014/12/26 职场文书
幽灵公主观后感
2015/06/09 职场文书
驻村工作简报
2015/07/20 职场文书
特种设备安全管理制度
2015/08/06 职场文书
学校2016年圣诞节活动总结
2016/03/31 职场文书
MySQL读取JSON转换的方式
2022/03/18 MySQL