最近带领小伙伴们处理了一下爬虫爬下来的数据,爬下来的数据有excel表格,word文档,pdf.其中以pdf的数据最难处理,只想要保留pdf中的表格部分,但是却不得其法,目前还没有攻克.
在这篇文章里记录一下使用apache poi读取excel文件用到的方法以及问题等,希望能帮助到大家吧:
一、excel中内容介绍
在excel文件中,我们首先遇到的是一个excel中有多个表,如何遍历和提取每一个单元格的数据这是个问题.
Idea中的pom依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.14</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
读取excel文件:
/**
* 读取Office 2007 excel
* */
private Map<String,List<List<Object>>> read2007Excel(File file)
throws IOException {
// 构造 XSSFWorkbook 对象,strPath 传入文件路径
XSSFWorkbook xwb = new XSSFWorkbook(new FileInputStream(file));
evaluator = xwb.getCreationHelper().createFormulaEvaluator();
//创建一个存储数据的HashMap对象
Map<String,List<List<Object>>> map = new HashMap<>(16);
// 读取第一章表格内容
for (int a = 0; a < xwb.getNumberOfSheets(); a++) {
List<List<Object>> list = new LinkedList<>();
XSSFSheet sheet = xwb.getSheetAt(a);
//待存取的value值
Object value;
//当前行
XSSFRow row;
//当前格
XSSFCell cell;
int counter = 0;
//从第一行开始遍历
for (int i = sheet.getFirstRowNum();
counter < sheet.getPhysicalNumberOfRows(); i++) {
if (i == 0) {
//跳过第一行,不存取标题信息
continue;
}
row = sheet.getRow(i);
if (row == null) {
break;
}
//按行存储,将该表中所有的元素存储在list对象中
List<Object> linked = new LinkedList<>();
//遍历当前行的所有cell元素
for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
cell = row.getCell(j);
//判断当前cell存储的数据类型,将其转化为Java中对应的数据类型
//例如时间转为Date类型
value = getSheetValue(cell);
linked.add(value);
}
list.add(linked);
}
//key为该表的名称,eg:1-潜在,基本信息
//value为当前表,是一个二维List对象
map.put(xwb.getSheetName(a),list);
}
return map;
}
看起来还是挺简单的,但是在这一段代码中我用到了evaluator,这个evaluator其实是poi提供的用于处理excel文件中的函数的问题.比如说"='10-评委'!F3"和"SUM(B5:B10)",这样的.通过evaluator就可以达到提取时,不管函数是什么样子,直接获取该函数的结果.我这里把它作为当前类文件的一个属性来使用了.
看完代码,再说一说我读excel文件的思路:为了能够快速的通过表签名(也就是最下面那一栏)找到该表,我创建了一个Map<List<List<Object>>>对象,其中key就是表签的名字.遮掩我通过map的get方法就能够快速的获取到这个表格.
表格中我按行读取,所以value值为一个二维的List集合,遍历当前表的所有元素只需要两个for循环嵌套就可以了.
二、通过Object存数据,方便以后入库
我存储成Object的唯一目的就是为了方便以后存储到数据库中,避免了来回转换格式的问题.可是同样的,我在对单个object进行判断和取值的时候,就需要疯狂的转来转去.如果有需要,大家可以清一色的存储成String类型.
/**
* @Description : 获取cell对象中的值,并且转为java中对应的类型
*
* @param cell
* @return : java.lang.Object
* @author : 申劭明
* @date : 2019/8/9 15:50
*/
public Object getSheetValue(Cell cell){
Object value ;
if (cell == null) {
//导入不能为空
value = "无";
return value;
}
// 格式化 number String
DecimalFormat df = new DecimalFormat("0");
// 字符,格式化日期字符串
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd");
// 格式化数字
DecimalFormat nf = new DecimalFormat("0");
//判断当前单元格的元素类型
switch (cell.getCellType()) {
case XSSFCell.CELL_TYPE_STRING:
value = cell.getStringCellValue();
break;
case XSSFCell.CELL_TYPE_NUMERIC:
value = cell.getNumericCellValue();
//避免手机号,身份证等自动识别为double类型
//由于精度问题需要用减法判断大小
if (80000000.0 - (double)value < 0){
value = (long)((double)value);
value = String.valueOf(value);
}
break;
case XSSFCell.CELL_TYPE_BOOLEAN:
value = cell.getBooleanCellValue();
break;