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 linecache.getline()读取文件中特定一行的脚本
Sep 06 Python
python基础教程之对象和类的实际运用
Aug 29 Python
python中input()与raw_input()的区别分析
Feb 27 Python
Python使用matplotlib实现绘制自定义图形功能示例
Jan 18 Python
python中kmeans聚类实现代码
Feb 23 Python
使用python将图片按标签分入不同文件夹的方法
Dec 08 Python
python 在某.py文件中调用其他.py内的函数的方法
Jun 25 Python
python多线程使用方法实例详解
Dec 30 Python
Python StringIO如何在内存中读写str
Jan 07 Python
win10下opencv-python特定版本手动安装与pip自动安装教程
Mar 05 Python
使用pandas库对csv文件进行筛选保存
May 25 Python
python如何控制进程或者线程的个数
Oct 16 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 接口类与抽象类的实际作用
2009/11/26 PHP
PHP+Mysql+jQuery实现发布微博程序 jQuery篇
2011/10/08 PHP
解析PHP 使用curl提交json格式数据
2013/06/29 PHP
thinkphp的URL路由规则与配置实例
2014/11/26 PHP
IE JS编程需注意的内存释放问题
2009/06/23 Javascript
发布一个基于javascript的动画类 Fx.js
2010/11/05 Javascript
JavaScript中的比较操作符>、=、
2014/12/31 Javascript
Three.js学习之文字形状及自定义形状
2016/08/01 Javascript
纯JS实现弹性导航条效果
2017/03/06 Javascript
jQuery实现的3D版图片轮播示例【滑动轮播】
2019/01/18 jQuery
react-native滑动吸顶效果的实现过程
2019/06/03 Javascript
微信小程序如何获取群聊的openGid以及名称详解
2019/07/17 Javascript
vue 中 命名视图的用法实例详解
2019/08/14 Javascript
微信小程序间使用navigator跳转传值问题实例分析
2020/03/27 Javascript
vue 实现在同一界面实现组件的动态添加和删除功能
2020/06/16 Javascript
vue tab切换,解决echartst图表宽度只有100px的问题
2020/07/19 Javascript
Element InputNumber 计数器的实现示例
2020/08/03 Javascript
Python实现列表转换成字典数据结构的方法
2016/03/11 Python
基于python3实现socket文件传输和校验
2018/07/28 Python
基于Python实现大文件分割和命名脚本过程解析
2019/09/29 Python
关于Flask项目无法使用公网IP访问的解决方式
2019/11/19 Python
Python如何使用argparse模块处理命令行参数
2019/12/11 Python
解决pytorch 数据类型报错的问题
2021/03/03 Python
英国最大的女士服装零售商:Bonmarché
2017/08/17 全球购物
英国顶级家庭折扣店:The Works
2017/09/06 全球购物
奥地利度假券的专家:we-are.travel
2019/04/10 全球购物
澳大利亚香水在线商店:City Perfume
2020/09/02 全球购物
集团公司党的群众路线教育实践活动工作总结
2014/03/03 职场文书
《老山界》教学反思
2014/04/08 职场文书
员工年终自我评价
2014/09/14 职场文书
授权委托书(公民个人适用)
2014/09/19 职场文书
2014年教师批评与自我批评思想汇报
2014/09/20 职场文书
党员教师学习党的群众路线教育实践活动心得体会
2014/10/31 职场文书
毕业赠语大全
2015/06/23 职场文书
JavaScript文档对象模型DOM
2021/11/20 Javascript
如何使用注解方式实现 Redis 分布式锁
2022/07/23 Redis