[es7]使用python向elasticsearch中插数据

​为了可以方便地实时观测插入过程中有无差错,选择使用自己独立开发的ESClient来实现数据的批量导入。其中关键方法为基于elaticsearch库中的help.bulk方法的add_date_bulk函数,只需要通过json库读取json文件形成列表,再把该列表传入该方法中,就可以不断将数据传入es中,并且可以观察到进度状态和异常信息。

img

img

插入脚本

使用方法

  • 创建一个es.ini配置文件,路径为conf/es.ini(可自定义,在main函数中更改),格式如下
[Elasticsearch]
host = xxxx
port = 9200

# 没有账号密码可不写
user = elastic
pass = xxxxxx
  • 准备好自己要插入的json数据(json文件格式)

  • 在下面这段脚本中的main函数根据自己的需求配置好mapping和setting

  • 运行脚本,开始插入

import configparser
import time
import json
from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulk
from elasticsearch import helpers
import tqdm

# 运行时间装饰器  语法糖
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        print('共耗时约 {:.2f} 秒'.format(time.time() - start))
        return res

    return wrapper


# 索引类
class BaseEsClient(object):
    '''
    TODO:连接es,增删查改index
    '''

    def __init__(self, filepath="./conf/es.ini"):
        '''
        TODO:实例和事务化单个node,若需要多个node,需要重构代码
        :param filepath:
        '''
        # 读取es配置
        self.conf = configparser.ConfigParser()
        self.conf.read(filepath, encoding='utf-8')
        # TODO:传参

        self.es_servers = [{
            "host": self.conf.get('Elasticsearch', 'Host'),
            "port": self.conf.get('Elasticsearch', 'Port')
        }]

        self.es_auth = (self.conf.get('Elasticsearch', 'User'), self.conf.get('Elasticsearch', 'Pass'))

        try:
            # 无用户名密码状态
            # self.es = Elasticsearch([ip], port=port)
            self.es_client = Elasticsearch(hosts=self.es_servers, http_auth=self.es_auth)

            # 用户名密码状态
            # http_auth是对设置了安全机制的es库需要写入 账号与密码,如果没有设置则不用写这个参数
            # self.es = Elasticsearch([ip], http_auth=('elastic', 'password'), port=port)

            print(f'连接成功,目标url为: f{self.es_servers}')
        except:
            print('连接失败')

    def create_index(self, index_name):
        '''
        TODO:进行创建一个数据库,即index, 索引
        :param index_name:  索引名
        :return: 
        '''
        self.es_client.indices.create(index=index_name)

    def create_index_by_body(self, index_name: str, body: dict):
        '''
        TODO:指定body创建一个index
        :param index_name: 
        :param body:   The configuration for the index (`settings` and`mappings`)
        :return: 
        '''
        self.es_client.indices.create(index=index_name, body=body)

    def delete_index(self, index_name: str):
        '''
        TODO:进行删除一个数据库,即index
        :param index_name: 
        :return: 
        '''
        self.es_client.indices.delete(index=index_name)

    # 数据库不用进入,也不用退出。


class MyEsClient(BaseEsClient):
    # TODO:对单个index进行增删改查

    default_body = {
        "mappings": {},
        "settings": {
            "index": {
                "number_of_shards": "1",
                "number_of_replicas": "0",
            }
        }
    }

    def __init__(self, filepath="./conf/es.ini", index='index01', doc_type='_doc', body=None):
        # TODO:输入单个index的名称
        super().__init__(filepath=filepath)

        self.index = index
        self.doc_type = doc_type

        if not self.es_client.indices.exists(index=index):
            # 创建Index
            if body:
                self.body = body
            else:
                self.body = self.default_body
            self.create_index_by_body(index_name=index, body=body)
            print(f'提示:创建了index,body结构如下')
            print(self.body)

    def set_index_mapping(self, set_mappings):
        # TODO:设置mapping结构
        """
        设置index的mapping,类似于表结构。
        注意!!!!现在仅仅对mapping中的properties参数,其他的参数还很多
        前提为:已有index,并且已自定义分词器,详情见https://blog.csdn.net/u013905744/article/details/80935846
        输入参数举例说明:
            set_mappings = {
                    "answer": {
                        "type": "string",
                        "index": "not_analyzed"
                    },
                    "answerAuthor": {
                        "type": "string"
                    },
                    "answerDate": {
                        "type": "date",
                        "format": "strict_date_optional_time||epoch_millis"//这里出现了复合类型
                    },
                    ...
                    {...
                    }
                }
        """
        mapping = {
            self.doc_type: {
                "properties": set_mappings
            }
        }
        self.es_client.indices.put_mapping(index=self.index, doc_type=self.doc_type, body=mapping)

    def add_date(self, row_obj):
        """
        TODO:单条插入ES
        :param row_obj    The document  类型:dict
        """
        self.es_client.index(index=self.index, doc_type=self.doc_type, body=row_obj)

    def add_date_bulk(self, row_obj_list):
        """
        TODO:批量插入ES,输入文本格式为单条插入的list格式
        :param row_obj_list  list 列表
        """
        load_data = []
        i = 1
        bulk_num = 2000  # 2000条为一批
        for row_obj in tqdm(row_obj_list):
            action = {
                "_index": self.index,
                "_type": self.doc_type,
                "_source": row_obj
            }
            load_data.append(action)
            i += 1

            # 批量处理
            if len(load_data) == bulk_num:
                print('插入', i / bulk_num, '批bulk')
                success, failed = helpers.bulk(self.es_client, load_data, index=self.index, raise_on_error=True)
                print(success, failed)
                # del load_data[0:len(load_data)]
                load_data.clear()

        # 处理剩下不足2000的
        if len(load_data) > 0:
            success, failed = bulk(self.es_client, load_data, index=self.index, raise_on_error=True)
            del load_data[0:len(load_data)]
            print(success, failed)

    def update_by_id(self, row_obj):
        """
        TODO:根据给定的_id,更新ES文档
        :param row_obj
        :return: None
        """

        _id = row_obj.get("_id", 1)
        row_obj.pop("_id")
        self.es_client.update(index=self.index, doc_type=self.doc_type, body={"doc": row_obj}, id=_id)

    def delete_by_id(self, _id):
        """
        TODO:根据给定的id,删除文档
        :param _id
        :return:
        """
        self.es_client.delete(index=self.index, doc_type=self.doc_type, id=_id)

    def search_by_query(self, body):
        '''
        TODO:根据查询的query语句,来搜索查询内容
        :param body
        '''
        search_result = self.es_client.search(index=self.index, doc_type=self.doc_type, body=body)
        return search_result

    def clear_doc(self):
        '''
        TODO:清空该index里的所有数据
        :return:
        '''
        choic = input('即将删除该index下的所有数据,是否继续?(Y/N):').strip()

        if choic == 'y' or choic == 'Y':
            # 删除所有
            delete_by_all = {"query": {"match_all": {}}}
            result = self.es_client.delete_by_query(index=self.index, body=delete_by_all, doc_type=self.doc_type)
            print(result)
        else:
            print('取消操作')

    @timer
    def add_data_by_bulk(self, row_obj_list):
        """
        TODO: 使用生成器批量写入数据
        :param row_obj_list
        :return None
        """
        bulk_num = 2000  # 2000条为一批

        action = ({
            "_index": self.index,
            "_type": self.doc_type,
            "_source": one_row,
        } for one_row in row_obj_list)

        helpers.bulk(self.es_client, action)


if __name__ == '__main__':


    # 包括mapping和setting  自己根据需求更改配置
    my_body = {
        "settings": {
            # 副本数
            "number_of_replicas": 0,

            # 分片数
            "number_of_shards": 3,

            # 分析
            "analysis": {

                # 自定义的分词过滤器
                "filter": {
                    "pinyin_max_word_filter": {
                        "type": "pinyin",
                        "keep_full_pinyin": "true",  # 分词全拼如雪花 分词xue,hua
                        "keep_separate_first_letter": "true",  # 分词简写如雪花 分词xh
                        "keep_joined_full_pinyin": True  # 分词会quanpin 连接 比如雪花分词 xuehua
                    },
                    "full_pinyin_filter": {
                        "type": "pinyin",
                        "keep_first_letter": False,
                        "keep_separate_first_letter": False,
                        "keep_full_pinyin": False,
                        "none_chinese_pinyin_tokenize": False,
                        "keep_original": False,
                        "limit_first_letter_length": 50,
                        "lowercase": False
                    }

                },

                # 自定义的分词器
                "tokenizer": {
                    "my_pinyin_01": {
                        "type": "pinyin",
                        "keep_separate_first_letter": False,
                        "keep_full_pinyin": True,
                        "keep_original": True,
                        "limit_first_letter_length": 16,
                        "lowercase": True,
                        "remove_duplicated_term": True
                    },
                    "my_ngram_tokenizer": {
                        "token_chars": [
                            "letter",
                            "digit"
                        ],
                        "type": "ngram",
                        "min_gram": "2",
                        "max_gram": "3"
                    }
                },

                # 字符过滤器
                "char_filter": {
                    "my_char_filter_01": {
                        "type": "mapping",
                        "mappings": [
                            "/n => <br>",
                            "/t => &emsp;&emsp;",
                        ]
                    }
                },

                # 自定义的分析器
                "analyzer": {
                    # ik-pinyin github的文档示例
                    "pinyin_analyzer": {
                        "tokenizer": "my_pinyin_01"
                    },

                    # 分析器用于处理 中文字段无法精确匹配;
                    "my_ngram_analyzer": {
                        "tokenizer": "my_ngram_tokenizer"
                    },

                },

            }
        },
        "mappings": {
            "properties": {
                # 平台
                "Platform": {
                    "type": "nested",
                    "properties": {

                        # 平台首页
                        "platformIndexUrl": {
                            "index": False,
                            "type": "keyword"
                        },

                        # 平台介绍
                        "platformIntroduction": {
                            "index": False,
                            "type": "keyword"
                        },

                        # 平台名字
                        "platformName": {
                            "type": "keyword"
                        },
                        # 平台Id
                        "platformId": {
                            "type": "keyword"
                        }
                    }
                },

                # 学校
                "School": {
                    "type": "nested",
                    "properties": {
                        "schoolIntroduction": {
                            "index": False,
                            "type": "keyword"
                        },
                        "schoolLogoUrl": {
                            "index": False,
                            "type": "keyword"
                        },
                        "schoolName": {
                            "type": "text",
                            "analyzer": "ik_max_word"
                        }
                    }
                },

                # 学期实体
                "Semester": {
                    "type": "nested",
                    "properties": {
                        # 老师实体
                        "Teacher": {
                            "type": "nested",
                            "properties": {
                                "teacherIntroduction": {
                                    "index": False,
                                    "type": "keyword"
                                },
                                "teacherName": {
                                    "index": False,
                                    "type": "keyword"
                                },
                                "teacherPhotoUrl": {
                                    "index": False,
                                    "type": "keyword"
                                }
                            }
                        },
                        "semesterEndTime": {
                            "type": "date",
                            "format": "yyyy-MM-dd"
                        },
                        "semesterReference": {
                            "index": False,
                            "type": "keyword"
                        },
                        "semesterStartTime": {
                            "type": "date",
                            "format": "yyyy-MM-dd"
                        },
                        "semesterStatus": {
                            "type": "byte"
                        },
                        "semesterNo": {
                            "type": "long"
                        }
                    }
                },
                "courseCategory": {
                    "type": "keyword"
                },
                "courseApplicant": {
                    "type": "long"
                },
                "courseCoverUrl": {
                    "index": False,
                    "type": "keyword"
                },
                "courseDetailUrl": {
                    "index": False,
                    "type": "keyword"
                },
                "courseGraderNum": {
                    "type": "long"
                },
                "courseIntroduction": {
                    "type": "text",
                    "analyzer": "ik_max_word"
                },
                "courseIsFree": {
                    "type": "byte"
                },
                "courseName": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "ik_max_word",
                    "fields": {
                        "pinyin": {
                            "type": "text",
                            "store": False,
                            "term_vector": "with_offsets",
                            "analyzer": "pinyin_analyzer",
                            "boost": 10
                        },
                        "completion": {
                            "type": "completion",
                            "analyzer": "ik_max_word"
                        },
                        "keyword": {
                            "type": "keyword",
                        }
                    }
                },
                "courseScore": {
                    "type": "float"
                },
                "courseSyllabus": {
                    "index": False,
                    "type": "keyword"
                },
                "courseId": {
                    "type": "keyword",
                }
            }
        }
    }

    # 操作es的客户端对象  记得创建一个es.ini来保存自己的es配置
    es01 = MyEsClient(filepath='./conf/es.ini', index='moocgle_02', body=my_body)

    # 自己的json格式的数据文件
    try:
        with open('./all01.json', 'r', encoding='utf-8') as fp:
            data_list_01 = json.load(fp=fp)
    except Exception as e:
        print("解析json文件时出错")
        print(e)
        exit()

    print(f'json解析完毕,有{len(data_list_01)}条正常数据')
    
    # 插入出错的数据列表
    error_list_01 = []
    print("开始插入数据")

    print("插入第1批bulk")
    for a_data in tqdm.tqdm(data_list_01):
        try:
            es01.add_date(row_obj=a_data)
        except Exception as e:
            print(e)
            error_list_01.append(a_data)

    print('---' * 50)

    # 插数据出错的
    print(len(error_list_01))
    with open("error_list.txt", "w", encoding='utf-8') as err_fp:
        err_fp.write(error_list_01)
        
    print('ok')

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Spring Boot2.5+ Spring Data ElasticSearch4.2环境搭建的 爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
曾经听许多前端从业者说:“前端发展太快了。”这里的快,十有八九是说层出不穷的新概念,余下的一二,大抵只是抒发一下心的苦闷罢——前两日刚习得的新技术转眼就“落后”——仔细品味这苦闷,除却不得不持续奔跑的无奈,更多的是一口气,一口卯足了劲儿也要把新知识全数揽入囊的不服气。作为刚入行的新人,对这点体会颇深。就像是蓦地从某个时间点切入,半数时间向前走,半数时间向后看,瞻前顾后,回味揣摩这十年间的岁月精魄,还得翘首盼着花花新世界,不时再问自己一句,这样走下去真的会好么?是的,其实答案人尽皆知,同时也无人知晓,因为没人能预言未来,顶多只能预测未来,但有一件事情永不会错,当你笃定地沿着一条路走下去,结果通常不会太糟糕,但凡能在浮躁的社会冷静下来潜心磨砺,多少总会有收获。幸而我有意弱化了对新信息的执念,开始做一些事情,《深入浅出ES6》就是其一件。 纵观整个系列,亦即纵观ECMAScript 2015的整个体系,吸取了诸多成功经验:借鉴自CoffeeScript的箭头函数;始于C++项目Xanadu,接着被E语言采用,后来分别于Python和JavaScript框架Dojo以Deferred对象的面貌出现的Promise规范(详见Async JavaScript一书3.1章);借鉴了C++、Java、C#以及Python等语言的for-of循环语句;部分借鉴Mustache、Nunjucks的模板字符串。 当然,新的语言体系也在之前的基础上查漏补缺:弥补块级作用域变量缺失的let和const关键字;弥补面向大型项目缺失的模块方案;标准委员会甚至为JavaScript增加了类特性,有关这一方面的特性褒贬不一,Douglas Crockford曾在2014年的Nordic.js大会发表了题为《The Better Parts》的演讲,重新阐述了他个人对于ECMAScript 6的看法,他认为Class特性是所有新标准最糟糕的创新(我个人也略赞同这一说法,类的加入虽然有助于其它语言的使用者开始使用JavaScript,但是却无法发挥出JavaScript原型继承的巨大优势);以及为了保持非侵入式弥补其它新特性而诞生的Symbols。 其它的新特性也相当诱人,熟练掌握可以大幅度提升开发效率:迭代器Iterator、生成器Generators、不定参数Rest、默认参数Default、解构Destructuring、生成器Generator、代理Proxy,以及几种新类型:Set、Map、WeakSet、WeakMap、集合Collection。 以上提及的新特性只是其的一部分,更多的新特性等待着大家进一步挖掘。整个系列的翻译历时150余天,坚持专栏翻译的日子艰苦也快乐,编辑徐川细心地帮我审校每一篇文章,编辑丁晓昀赠予钱歌川先生详解翻译之著作让我大开眼界,与李松峰老师的交流也让我深刻理解了“阅读、转换、表达”的奥义所在,最感谢我的母亲,在我遇到困难需要力量的时候永远支持着我。选择ES6作为前端生涯的切入点实之我幸,恰遇这样的机会让我可以一心一意地向前走,向未来走。我很敬佩在“洪荒”和“战乱”年代沉淀无数经 验的前辈们,你们在各种不确定的因素左右互搏,为终端用户提供统一的用户体验,直到如今你们依然孜孜不倦地吸取业内新鲜的经验。技术在进步,也为前端人 提供着无限的可能性,我们有责任也有义务去推动新标准的发展和普及,诚然在商业的大环境下我们不愿放弃每一寸用户的土壤,但携众人之力定将能推动用户终端 的革新。ES7标准的提案纷纷提上日程,用不了多久也将登上前端大舞台。也感谢午川同学友情提供译文《深入浅出ES6(十):集合 Collection》,让我在困难时期得以顺利过渡。最后祝愿国内前端社区向着更光明美好的未来蓬勃生长!
你可以使用Python和Locust来进行对ES7的压测。首先,确保你已经安装了Python和Locust。 接下来,你需要安装elasticsearch-py库,它是PythonElasticsearch进行交互的库。可以使用以下命令安装: ``` pip install elasticsearch ``` 然后,创建一个Python脚本,导入必要的模块和库: ```python from locust import HttpUser, task, between from elasticsearch import Elasticsearch class ESUser(HttpUser): wait_time = between(1, 5) def on_start(self): # 创建一个Elasticsearch客户端连接 self.client = Elasticsearch(['localhost:9200']) @task def search(self): # 定义一个搜索任务 query = { "query": { "match_all": {} } } # 发送搜索请求 response = self.client.search(index='your_index', body=query) # 打印搜索结果 print(response) ``` 在上面的代码,我们创建了一个名为ESUser的Locust用户类。在`on_start`方法,我们创建了一个Elasticsearch客户端连接。 然后,在`@task`装饰的`search`方法,我们定义了一个搜索任务。你可以根据自己的需求修改查询条件。在该方法,我们发送了一个搜索请求,并打印了搜索结果。 最后,你可以在命令行使用Locust命令来启动压测: ``` locust -f your_script.py --host=http://localhost:9200 ``` 替换`your_script.py`为你的脚本文件名,`http://localhost:9200`为你的ES7的地址。 然后,你可以在浏览器访问Locust的Web界面(默认为http://localhost:8089)来配置并启动压测。 注意:在进行压测之前,请确保你已经在ES7创建了索引,并且数据已经准备好。另外,压测会对目标系统造成一定的负载,请谨慎使用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值