1. 任务背景

近日有个项目任务,要求读取压缩在Zip中的百科HTML文件,经分析发现,提供的Zip文件有如下特点(=>指代对应解决方案):

(1) 压缩为分卷文件 => 只需将解压缩在同一目录中的一个分卷zip即可解压缩出整个文件

(2) 压缩文件中又包含不同的两个文件夹,且各包含n个小zip文件,小zip文件中包含目录及对应的HTML文本文件

采用第一方案:依次解压缩各小zip文件,存放在一个目录中,然后上传到HDFS中

存在问题:每个小zip都包含上万个小文件,按照第一方案解压缩,耗费的时间太太太多了

(3) 解析的zip存在多文件的情况

(4) 数据总量共计50W

2. 优化方案

直接上传小zip文件,然后让Spark直接从zip文件中读取HTML文本,再使用jsoup解析,并存储至elasticsearch中。

实现过程中有一处需要注意! => 解析zip会遍历的ZipEntry,会识别文件夹和文件夹下的文件,即文件夹和文件在ZipEntry中被当成同类对象来对待。

例1:本地解析zip压缩文件demo

import java.io.{BufferedInputStream, BufferedReader, FileInputStream, InputStreamReader}
import java.util.zip.{ZipFile, ZipInputStream} import net.sf.json.JSONObject
import org.jsoup.Jsoup import scala.collection.mutable object Test { def testZip(): Unit = {
val baseDir = "part2/"
val path = s"$baseDir\\06.zip"
val zf = new ZipFile(path)
val in = new BufferedInputStream(new FileInputStream(path))
val zin = new ZipInputStream(in)
var zipEn = zin.getNextEntry
var count = 0
try {
while (zipEn != null) {
if (!zipEn.isDirectory) {
val buff = new BufferedReader(new InputStreamReader(zf.getInputStream(zipEn)))
val sb = new StringBuilder()
var line = buff.readLine()
while (line != null) {
count = count + 1
if (line.nonEmpty) {
sb.append(line.trim)
}
line = buff.readLine()
}
val id = zipEn.getName.substring(zipEn.getName.indexOf("/") + 1, zipEn.getName.indexOf("."))
val doc = Jsoup.parse(sb.toString()) val title = doc.select(".lemmaWgt-lemmaTitle-title h1").text()
val sb1 = new mutable.StringBuilder()
val eles = doc.select(".para")
for (i <- 0 until eles.size()) {
sb1.append(eles.get(i).text().trim).append("\t")
} val json = new JSONObject()
json.put("id", id)
json.put("title", title)
json.put("content", sb1.toString().trim)
println(json)
buff.close()
}
zipEn = zin.getNextEntry
}
zin.closeEntry()
} catch {
case _ =>
}
println(count)
} }

例2:Spark读取HDFS中的含有多文件的zip文件

def parseBaike(): Unit ={
val baseDir = "/work/ws/temp/baike"
val sc = new SparkContext(new SparkConf().setAppName("parseBaike"))
val rdd = sc.binaryFiles(s"$baseDir/data/*.zip", 40)
.flatMap{
case (zipFilePath: String, content: PortableDataStream) => {
val zis = new ZipInputStream(content.open())
Stream.continually(zis.getNextEntry)
.takeWhile(_ != null)
.flatMap(zipEn => {
if(zipEn.isDirectory) None
else{
// 基于文件名获取百科词条的id信息
val id = zipEn.getName.substring(zipEn.getName.indexOf("/")+1, zipEn.getName.indexOf("."))
val html = scala.io.Source.fromInputStream(zis, "UTF-8").getLines.mkString("")
if(html.nonEmpty){
val doc = Jsoup.parse(html)
// 解析百科中的词条名称
val title = doc.select(".lemmaWgt-lemmaTitle-title h1").text()
// 获取词条HTML中的全部正文内容
val sb = new mutable.StringBuilder()
val eles = doc.select(".para")
for(i <- 0 until eles.size()){
sb.append(eles.get(i).text().trim).append("\t")
}
if(title.trim.nonEmpty && sb.toString.trim.nonEmpty){
val json = new JSONObject()
json.put("id", id)
json.put("title", title)
json.put("content", sb.toString().trim)
Some(json)
}else None
}else None
}
})
}
}
rdd.cache()
rdd.saveAsTextFile(HDFSFileUtil.clean(s"$baseDir/result/json"))
rdd.foreach(f => {
// 保存在Es中
ESHelper.saveToEs("baike", "baike", f, "id")
})
rdd.unpersist()
sc.stop()
}

  注意:如上代码仅供参考,并隐去了部分业务相关代码,如HDFS和Es工具类,如若需要,可留言沟通交流!

3. 参考

(1)  https://stackoverflow.com/questions/28569788/how-to-open-stream-zip-files-through-spark

(2) https://stackoverflow.com/questions/32080475/how-to-read-a-zip-containing-multiple-files-in-apache-spark?r=SearchResults

Spark读取HDFS中的Zip文件的更多相关文章

  1. Spark读取HDFS文件,任务本地化(NODE_LOCAL)

    Spark也有数据本地化的概念(Data Locality),这和MapReduce的Local Task差不多,如果读取HDFS文件,Spark则会根据数据的存储位置,分配离数据存储最近的Execu ...

  2. python读取hdfs上的parquet文件方式

    在使用python做大数据和机器学习处理过程中,首先需要读取hdfs数据,对于常用格式数据一般比较容易读取,parquet略微特殊.从hdfs上使用python获取parquet格式数据的方法(当然也 ...

  3. 基于Python——实现解压文件夹中的.zip文件

    [背景]当一个文件夹里存好好多.zip文件需要解压时,手动一个个解压再给文件重命名是一件很麻烦的事情,基于此,今天介绍一种使用python实现批量解压文件夹中的压缩文件并给文件重命名的方法—— [代码 ...

  4. 点滴积累【C#】---C#实现上传word以流形式保存到数据库和读取数据库中的word文件。

    本文修改来源:http://www.cnblogs.com/zmgdpg/archive/2005/03/31/129758.html 效果: 数据库: 思路: 首先保存word到数据库:获取上传文件 ...

  5. Spark读取HDFS文件,文件格式为GB2312,转换为UTF-8

    package iie.udps.example.operator.spark; import scala.Tuple2; import org.apache.hadoop.conf.Configur ...

  6. spark读取hdfs上的文件和写入数据到hdfs上面

    def main(args: Array[String]): Unit = { val conf = new SparkConf() conf.set("spark.master" ...

  7. Spark 读取HDFS csv文件并写入hive

    package com.grady import org.apache.spark.SparkConf import org.apache.spark.sql.{Row, SaveMode, Spar ...

  8. Spark读取Hbase中的数据

    大家可能都知道很熟悉Spark的两种常见的数据读取方式(存放到RDD中):(1).调用parallelize函数直接从集合中获取数据,并存入RDD中:Java版本如下: JavaRDD<Inte ...

  9. spark读取hdfs数据本地性异常

    在分布式计算中,为了提高计算速度,数据本地性是其中重要的一环. 不过有时候它同样也会带来一些问题. 一.问题描述 在分布式计算中,大多数情况下要做到移动计算而非移动数据,所以数据本地性尤其重要,因此我 ...

随机推荐

  1. Java入门第二季——Java中的this关键字

    如果想访问一个private属性,通常会使用setter和getter方法来操作属性,属性名经常会与参数名相同,我们为了区分属性和参数的概念,我们会在属性前面加上this关键字,此时代表我将一个参数的 ...

  2. BFS和DFS

    1.图的两种遍历方式 图的遍历通常有两种方式,即深度优先搜索(Depth First Search)和广度优先搜索(Breadth First Search).前者类似于树的先序遍历,而后者类似于树的 ...

  3. MultipartFile类

    MultipartFile类常用的一些方法: String getContentType()//获取文件MIME类型InputStream getInputStream()//后去文件流String ...

  4. 1、docker简介:课程定位、是什么、能干什么、下载

    1.前提知识和定位 2.是什么 1.为什么会有docker出现 环境配置如此麻烦,换一台机器,就要重来一次,费力费时.很多人想到,能不能从根本上解决问题,软件可以带环境安装? 也就是说,安装的时候,把 ...

  5. 洛谷 U5122 T2-power of 2(费马小定理)

    U5122 T2-power of 2 题目提供者胡昊 题目描述 是一个十分特殊的式子. 例如: n=0时 =2 然而,太大了 所以,我们让对10007 取模 输入输出格式 输入格式: n 输出格式: ...

  6. 【luoguP1182】数列分段 Section II

    题目描述 对于给定的一个长度为N的正整数数列A-i,现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小. 关于最大值最小: 例如一数列4 2 4 5 1要分成3段 将其如下分段: [4 ...

  7. luogu3629

    P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...

  8. shell基础之一

    Shell脚本自动化管理系统的必备基础: vim编辑器的熟练使用,SSH终端及“.vimrc”的设置等等需要熟悉. 命令基础:Linux的150个常用命令的熟练使用 Linux的正则表达式以及三剑客( ...

  9. Java内置锁synchronized的实现原理及应用(三)

    简述 Java中每个对象都可以用来实现一个同步的锁,这些锁被称为内置锁(Intrinsic Lock)或监视器锁(Monitor Lock). 具体表现形式如下: 1.普通同步方法,锁的是当前实例对象 ...

  10. 查看window用户登录日志

    查看window用户登录日志 能追踪Windows系统登录时间的三种方法 Windows登录日志详解 事件ID=4798应该对应的是锁定操作(Win+L)