背景
最近在做的项目,需要从网络上抓取部分数据,在使用正则对数据进行匹配、提取时发现,表格类的数据因表格头和数据部分分离,直接定位这样的数据,有一定的困难(好吧,承认了,是自己没有好的解决思路_),所以简化下在解析前对内容进行预处理,方便后续的正则解析,比如:通过冗余,将表格头内容附加到内容前,使用特殊符号进行分隔,这样正则就能准确定位提取内容了。
一小步
思路有了,第一个问题就是,表格的行、列合并(rowspan、colspan)问题,那就需要把表格展开,代码是一个二维表格展开的方法,但是在生产环境下还得解决像并发这样的问题,这里就不展开了
上代码
使用库 JSoup,具体使用可以参考官方文档
表格转换
import java.io.FileInputStream;
import java.io.InputStream;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Tag;
import org.jsoup.select.Elements;
public class TableConvert {
/**
* 将表格进行二维展开
* @param table
* @return
*/
public Element[][] toTable(Element table) {
if (!table.nodeName().equals("table")) {
return null;
}
Elements tableRows = table.getElementsByTag("tr");
int tableHeight = tableRows.size();
//找 展开的最大列数,存在问题:如果某一列 全部使用 colspan 且其值都 大于2,有可能出错
int tableWidth = 0;
for (int tr_idx = 0; tr_idx < tableHeight; tr_idx++) {
Elements tds = tableRows.get(tr_idx).select("td, th");
int td_size = tds.size();
if (td_size > tableWidth)
tableWidth = td_size;
}
System.out.println("tableHeight:"+tableHeight+";tableWidth:"+tableWidth);
if (tableHeight < 2 || tableWidth < 2)
return null;
//定义二维数组
Element[][] result = new Element[tableHeight][tableWidth];
//使用canreplace 来占位
for(int i=0;i tableWidth ,直接抛出异常
try {
for (int rowIndex = 0; rowIndex < tableHeight; rowIndex++) {
Elements colCells = tableRows.get(rowIndex).select("td, th");
System.out.println("row"+rowIndex+":\n"+colCells);
int pointIndex = 0;//列的索引
for (int colIndex=0; colIndex < colCells.size();colIndex++) {
Element currentCell=colCells.get(colIndex);
//放到二维数组
if(result[rowIndex][colIndex].tagName().equalsIgnoreCase("canreplace"))
{
result[rowIndex][colIndex] = currentCell;
pointIndex=colIndex;
}else {
pointIndex=colIndex+1;
//查找可放置 一直找到一个可替换
while(!result[rowIndex][pointIndex].tagName().equalsIgnoreCase("canreplace") && pointIndex< tableWidth ) {
pointIndex++;
System.out.println("===rowIndex==="+pointIndex+"====tempColIndex==="+pointIndex+"==="+result[rowIndex][pointIndex].tagName());
}
if(pointIndex < tableWidth && result[rowIndex][pointIndex].tagName().equalsIgnoreCase("canreplace") ) {
result[rowIndex][pointIndex] = currentCell;
}else {
throw new Exception("table格式有错误!");
}
}
// 检查 colspan
int colspan = 1;
if (currentCell.hasAttr("colspan")) {
colspan = Integer.valueOf(currentCell.attr("colspan"));
currentCell.removeAttr("colspan");
}
//复制表格内容
if (colspan > 1) {
for(int emptyColindex =1;emptyColindex < colspan ;emptyColindex++)
{
pointIndex++;
while(!result[rowIndex][pointIndex].tagName().equalsIgnoreCase("canreplace") && pointIndex< tableWidth ) {
pointIndex++;
System.out.println("===rowIndex==="+pointIndex+"====tempColIndex==="+pointIndex+"==="+result[rowIndex][pointIndex].tagName());
}
if(pointIndex < tableWidth && result[rowIndex][pointIndex].tagName().equalsIgnoreCase("canreplace") ) {
result[rowIndex][pointIndex] = currentCell;
}else {
throw new Exception("table格式有错误!");
}
}
}
// 检查rowspan
int rowspan = 1;
if (currentCell.hasAttr("rowspan")) {
rowspan = Integer.valueOf(currentCell.attr("rowspan"));
currentCell.removeAttr("rowspan");
}
if (rowspan > 1) {
for (int i = 1; i < rowspan; i++) {
if (i >= tableHeight) break; // ignore bad rowspans
System.out.println("===rowIndex==="+pointIndex+"====tempColIndex==="+pointIndex+"==="+result[rowIndex][pointIndex].tagName());
result[rowIndex+i][colIndex] = currentCell;//new Element(invalidTag, "");
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return result;
}
辅助打印
public void printTable(Element[][] table) {
if (table == null) return;
System.out.println("==================");
for (int rowIndex = 0; rowIndex < table.length; rowIndex++) {
System.out.print("|");
for (int colIndex = 0; colIndex < table[rowIndex].length; colIndex++) {
if (table[rowIndex][colIndex] == null) {
System.out.print(" ");
} else {
System.out.print(table[rowIndex][colIndex].text());
}
System.out.print(" |");
}
System.out.println();
}
System.out.println("==================");
}
运行测试
public static void main(String[] args) {
String url = "d:\\Untitled-2.html";
InputStream in;
TableConvert tableConvert = new TableConvert();
try {
in = new FileInputStream(url);;
Document doc = Jsoup.parse(in, null, "");
for (Element aTable : doc.getElementsByTag("table")) {
Elements subtables = aTable.getElementsByTag("table");
subtables.remove(aTable);
if(subtables.size() == 0) {
System.out.println("converting table...");
Element[][] result = tableConvert.toTable(aTable);
if (null != result)
tableConvert.printTable(result);
else
System.out.println("Could not convert table.");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试文件内容
序号
内容
1
英文
2
法文