上一篇介绍了基于SmartETL框架实现arxiv采集处理的基本流程,通过少量的组件定制开发,配合yaml流程配置,实现了复杂的arxiv采集处理。
由于其业务流程复杂,在实际应用中还存在一些不足需要优化。
由于arXiv论文数量庞大、解析处理流程复杂,实际应用中可能会遇到不少问题。主要包括:
应对这些问题的一个比较好的办法就是引入消息中间件技术,基于生产者-消费者模式,将不同处理环节进行解耦,既可以避免相互影响,而且可以针对不同处理环节的计算复杂度进行独立设计以提高整体吞吐。
引入kafka消息中间件,设计多个消息队列,将不同环节作为独立的生产者/消费者,实现流程解耦。如下图所示:
设计消息队列包括:
arxiv_task
):arxiv采集处理任务队列,即kaggle数据集排序后的论文元数据。arxiv_html
):待处理的html解析任务。arxiv_parsed
):解析后待入库的论文信息。流程解耦后的每个环节作为一个子流程,与Kafka进行读取或写入。具体包括包括:
arxiv_task
队列。arxiv_html
队列中插入一条数据,可以是输入的论文元数据,也可以附加html文件路径信息。arxiv_parsed
队列中。上述每个子流程都运行为一个SmartETL实例。根据速度要求,对部分子流程可以启动多个SmartETL实例。
如前所述,基于kafka解耦的流程将分成4个子流程,通过kafka消息队列数据进行驱动。
此子流程较为简单,读取kaggle数据集文件(JSON格式),写入Kafka即可。按需运行。
name: json数据写入kafka
consts:
kafka_config:
kafka_ip: 10.60.1.148:9092
kafka_topic: arxiv_task
loader: JsonLine('data/sorted_arxiv.json')
nodes:
writer: database.kafka.KafkaWriter(**kafka_config, buffer_size=50)
processor: writer
基于前文流程进行简单改造即可。
name: arxiv论文下载
consts:
consumer:
kafka_ip: 10.60.1.148:9092
kafka_topic: arxiv_task
group_id: arxiv_crawler
producer:
kafka_ip: 10.60.1.148:9092
kafka_topic: arxiv_html
loader: database.kafka_v2.KafkaConsumer(**consumer)
nodes:
#拼接html下载url
mk_url: Map('gestata.arxiv.url4html', key='id', target_key='url_html')
#下载html内容
download: Map('util.http.content', key='url_html', target_key='content', most_times=3, ignore_error=True)
#保存html文件
save: WriteFiles('data/arxiv_html', name_key='id', suffix='.html')
#元数据集写入kafka
write: database.kafka.KafkaWriter(**producer, buffer_size=1)
processor: Chain(Print("id"), mk_url, download, save, write)
name: arxiv论文解析
consts:
consumer:
kafka_ip: 10.60.1.148:9092
kafka_topic: arxiv_html
group_id: arxiv_html
producer:
kafka_ip: 10.60.1.148:9092
kafka_topic: arxiv_parse
loader: database.kafka_v2.KafkaConsumer(**consumer)
nodes:
#论文解析
parse: Map('gestata.arxiv.extract')
#元数据集写入kafka
write: database.kafka.KafkaWriter(**producer, buffer_size=1)
processor: Chain(parse, write)
name: arxiv论文入库ES和Qdrant
consts:
bge_large: http://10.208.63.29:8001/embed
qd_config:
host: '10.60.1.145'
es_config:
host: '10.208.61.117'
port: 9200
index: doc_arxiv
buffer_size: 3
consumer:
kafka_ip: 10.60.1.148:9092
kafka_topic: arxiv_parse
group_id: arxiv_parse
loader: database.kafka_v2.KafkaConsumer(**consumer)
nodes:
# 入库处理
write: gestata.arxiv.ArxivProcess(bge_large, qd_config, es_config)
processor: write
这里出于效率考虑,将论文正文chunk拆分、向量化、入向量库的工作集中在一个组件中实现。核心代码如下:
abstract = paper.get('abstract')
self.embed_and_write2(index=1, _id=_id, content=abstract, collection='chunk_arxiv_abstract2505')
sections = paper.get('sections')
for index, section in enumerate(sections,start=1):
figures = section.get('figures')
if figures:
self.image_embed_and_write(_id=_id, index=index, figures=figures)
title = section['title'].lower()
content = section['content']
collection = 'chunk_arxiv_discusss2505'
if 'introduction' in title:
collection = 'chunk_arxiv_introduction2505'
elif 'method' in title:
collection = 'chunk_arxiv_method2505'
elif 'experiment' in title:
collection = 'chunk_arxiv_experiment2505'
self.embed_and_write2(index=index, _id=_id, content=content, collection=collection)
self.ESWriter.index_name = self.es_index
self.ESWriter.write_batch(rows=[paper])
本系列文章讨论了arXiv论文数据采集和预处理的相关技术,实现了对arXiv论文内容抽取以及建立向量化索引,通过Kafka消息中间件实现了各处理环节的解耦,更加方便实际业务中使用。整个流程基于SmartETL框架进行开发,同时也对SmartETL框架进行了完善。
相关代码已经全部推送到 SmartETL项目,具体流程定义在 这里,欢迎下载体验