使用jsoup将表格内容展开-方便Regex进行内容定位

75 阅读1分钟

背景

最近在做的项目,需要从网络上抓取部分数据,在使用正则对数据进行匹配、提取时发现,表格类的数据因表格头和数据部分分离,直接定位这样的数据,有一定的困难(好吧,承认了,是自己没有好的解决思路^_^),所以简化下在解析前对内容进行预处理,方便后续的正则解析,比如:通过冗余,将表格头内容附加到内容前,使用特殊符号进行分隔,这样正则就能准确定位提取内容了。

一小步

思路有了,第一个问题就是,表格的行、列合并(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<tableHeight;i++) {
			for(int j=0;j<tableWidth;j++) {
				result[i][j]=new Element(Tag.valueOf("canreplace"),"");
			}
		}
		
		
		//出现不规范的 colspan 则会出现   实际列数 > 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();
		}
	}
}

测试文件内容

<table>
    <tr>
        <td>序号</td>
        <td>内容</td>
    </tr>
    <tr>
            <td colspan="2" rowspan="2">1</td>
    </tr>
    <tr>
            <td>英文</td>
    </tr>
    <tr>
            <td>2</td>
            <td>法文</td>
    </tr>
</table>