a node programing that fetch key infomation from more than two thousand pdf documents,and output in exceljavascript
需求描述:處理同一目錄下的2000個pdf文件,提取每一個文件中的一些關鍵信息(單位名稱,統一社會信用代碼,行業類別),並整理數據導出爲excel表格。html
最近在看node文件處理,剛好發現校友羣裏有個土木專業的同窗提出這麼一個問題,當時的第一想法就是我也許能夠作,而後就找到了那個同窗問清楚了明確需求,而且要了部分pdf文件,開始作...... 個人第一想法就是,首先讀取目錄下的文件,而後對每一個文件內容,進行正則匹配,找出目的信息,而後再導出。事實上也是這麼回事,基本上分爲三步:前端
node讀取pdf文件,引入了'pdf2json':java
npm install pdf2json --save
複製代碼
使用這個包,能夠將pdf解析爲json格式,從而獲得文件的內容node
const PDFParser = require('pdf2json');
const src = './pdf';
var pdfParser = new PDFParser(this, 1);
pdfParser.loadPDF(`${src}/${path}`);
pdfParser.on('pdfParser_dataError', errData =>reject( new Error(errData.parserError)));
pdfParser.on('pdfParser_dataReady', () => {
let data = pdfParser.getRawTextContent();
});
複製代碼
目標是找出每一個文件中的「單位名稱」、」統一社會信用代碼「、「行業類別」,仔細分析上一過程當中輸出的結果:python
由於要處理的文件內容格式都很是嚴謹,咱們所要獲取的信息都在第三頁,解析出的json數據中,目標文本分佈在page(1)和page(3)中,且目標文本格式都是key:value的格式,每個文本都換行,因此處理起來就方便多了,最終匹配的是以「單位名稱:」開頭的一個或者多個非空字符,因爲要匹配三個值,因此用(red|blue|green)這種方式來查找目標值。git
let result = data.match(/(統一社會信用代碼|單位名稱|行業類別):[\S]*/g);
複製代碼
match匹配最終獲得一個數組:github
result = ['統一社會信用代碼:xxx','單位名稱:xxx','行業類別:xxx']
複製代碼
網上有不少js代碼將table導出爲excel的代碼,這裏使用了'node-xlsx',安裝:web
npm install node-xlsx --save
複製代碼
使用這個是由於簡單,而且也符合需求,上手快。正則表達式
const xlsx = require('node-xlsx');
var buffer = xlsx.build([{name: 'company', data: list}]);
fs.writeFileSync('list.csv', buffer, 'binary');
複製代碼
三行代碼就搞定了,就獲得了一個csv格式的excel,剩下的處理就是對list的處理了,傳入的list需爲一個二維數組,數組的第一項爲表頭,其餘項爲每一行對應得數據,也爲數組格式。整理的list以下:
[
['序號','統一社會信用代碼','單位名稱','行業類別'],
['xxx','xxx','xxx','xxxx']
]
複製代碼
解析PDF的過程爲異步,因此在批量處理大量文件的狀況下,要考慮內存泄漏問題,每次只處理五個,處理完成以後再去處理剩餘的文件,直到所有完成處理,輸出爲excel。 當文件數量超過30個,報錯信息以下:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
複製代碼
出現問題的緣由:
解釋:因爲node是基於V8引擎,在node中經過javascript使用內存時只能使用部份內存,64位系統下約爲1.4GB,32位系統下約爲0.7GB,當執行的程序佔用系統資源太高,超出了V8對node默認的內存限制大小就會報上圖所示錯誤。
若是是編譯項目,V8提供的默認內存大小不夠用,能夠去修改 --max-old-space-size,可是我目前的需求是處理2000多個pdf文件,解析爲json,因此使用的內存大小是不肯定的,不能採起這種方案。
個人理解:node js 不少操做都是異步執行的,而異步操做的結果就是不能確保執行完成的時間,因此當多個異步操做同時進行的時候,不能確保執行完成的順序,可是執行的結果又是相互關聯的,因此在執行的時候,會一直佔用內存,而不被垃圾回收機制回收,形成內存泄漏。(也有一種多是隊列裏等待執行的任務太多了。。。)
const PDFParser = require('pdf2json');
const fs = require('fs');
const src = './pdf';
const xlsx = require('node-xlsx');
let list = [['序號','統一社會信用代碼','單位名稱','行業類別']];
let index = 1;
let len = 0;
fs.readdir(src, (err, files) => {
len = files.length;
files.forEach(item => {
var pdfParser = new PDFParser(this, 1);
pdfParser.loadPDF(`${src}/${item}`);
pdfParser.on('pdfParser_dataError', errData => console.error(errData.parserError)); pdfParser.on('pdfParser_dataReady', () => {
let data = pdfParser.getRawTextContent();
let result = data.match(/(統一社會信用代碼|單位名稱|行業類別):[\S]*/g);
for (let i = 0 ;i < 3;++i){
result[i] = result[i].split(':')[1];
}
list.push(result);
++index;
if( index === len){
var buffer = xlsx.build([{name: 'company', data: list}]); // Returns a buffer
fs.writeFileSync('list.csv', buffer, 'binary');
}
});
});
});
複製代碼
可是究竟這個異步操做的併發量的上限是多少,不能肯定,有一個同窗嘗試過,讀取PDF文件的時候,上限是30,分析以上結果,進行改進,改進以後,每次執行五個異步操做,執行完成以後再繼續執行下一個五個異步函數。
測試過,這種方式處理100個文件時沒有問題的,對比了兩種方式方法,以34個文件爲測試用例:
方法 | 文件數量 | 讀取時間(s) | CPU | 內存
ConvertToJSON(path){
return new Promise((resolve,reject) => {
var pdfParser = new PDFParser(this, 1);
pdfParser.loadPDF(`${src}/${path}`);
pdfParser.on('pdfParser_dataError', errData =>reject( new Error(errData.parserError)));
pdfParser.on('pdfParser_dataReady', () => {
// 省略處理部分
resolve(result);
});
}).catch(error => {
console.log(error);
});
}
seek(callback){
let arr = this.files.splice(0,5);
let all = [];
arr.forEach(item => {
all.push(this.ConvertToJSON(item));
});
let promise = Promise.all(all);
promise.then(result => {
// 省略處理部分
return this.files.length === 0 ? callback(this.list) : this.seek(callback);
});
}
複製代碼
源碼地址,歡迎指正。 可以幫助到別人同時本身又嘗試了新鮮事物,因此以爲很開心。
參考文檔:
在此鳴謝大學好友邢旭磊。
個人我的博客:下雨天DY的前端成長記