我有一个html表,我想用bash解析 (注意:我使用R来执行此操作,但希望尝试使用bash轻松地与另一个shell脚本集成)。
该表格可以从以下网址获得: http://faostat.fao.org/site/384/default.aspx
通过查看源 - 特定表的xpath引用是:
//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]
如何直接从bash将此表解析为csv文件?
我尝试了以下内容:
curl "http://faostat.fao.org/site/384/default.aspx" | xpath '//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]' > test.txt
这只返回test.txt的空白文本。
有人可以帮我解决使用bash中的xpath解析有效的html表并创建它的CSV文件吗?
任何帮助表示感谢。
答案 0 :(得分:2)
//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]/tr
(也就是说,/tr
附加到你问题中的XPath表达式中)会抓住每一行,并跳过table
包装器(你没有需要在输出中做任何事情)。
然后您还需要通过xmllint --xpath
或sed
或其他方式输出perl
输出:
wget -q -O - "http://faostat.fao.org/site/384/default.aspx" \
| xmllint --html \
--xpath '//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]/*' - \
2>/dev/null \
| perl -pe 's/<tr[^>]+>//' \
| perl -pe 's/<\/tr>//' \
| perl -pe 's/^\s+<t[dh][^>]*>//' \
| perl -pe 's/<\/t[dh]><t[dh][^>]*>/|/g' \
| perl -pe 's/<\/t[dh]>//' \
| grep -v '^\s*$'
示例:sed版本
wget -q -O - "http://faostat.fao.org/site/384/default.aspx" \
| xmllint --html \
--xpath '//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]/*' - \
2>/dev/null \
| sed -E 's/<tr[^>]+>//' \
| sed -E 's/<\/tr>//' \
| sed -E 's/^[[:space:]]+<t[dh][^>]*>//' \
| sed -E 's/<\/t[dh]><t[dh][^>]*>/|/g' \
| sed -E 's/<\/t[dh]>//' \
| grep -v '^\s*$'
在这两种情况下,grep -v '^\s*$'
只是为了删除空行。
严格来说不是CSV;它用|
(管道)字符而不是逗号分隔字段/单元格 - 因为一些(许多)字段本身也有逗号和引号。如果您真的是真的CSV,请向下滚动并阅读下面的如何为此案例生成真实的CSV 。
作为xmllint --xpath
的替代方法,您可以使用Python和lxml.html
库:
wget -q -O - "http://faostat.fao.org/site/384/default.aspx" \
| python -c "import lxml.html as html; import sys; \
expr = sys.argv[1]; print '\n'.join([html.tostring(el) \
for el in html.parse(sys.stdin).xpath(expr)])" \
'//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]//tr' \
| sed -E 's/<tr[^>]+>//' \
| sed -E 's/<\/tr>//' \
| sed -E 's/^[[:space:]]+<t[dh][^>]*>//' \
| sed -E 's/<\/t[dh]><t[dh][^>]*>/|/g' \
| sed -E 's/<\/t[dh]>//' \
| grep -v '^\s*$'
column
和colrm
命令格式化输出如果您希望在控制台中读取结果的漂亮打印/格式化列/表视图并滚动/翻页,请将输出进一步输入column
和colrm
命令,例如这样:
wget -q -O - "http://faostat.fao.org/site/384/default.aspx" \
| xmllint --html \
--xpath '//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]/*' - \
2>/dev/null \
| sed -E 's/<tr[^>]+>//' \
| sed -E 's/<\/tr>//' \
| sed -E 's/^[[:space:]]+<t[dh][^>]*>//' \
| sed -E 's/<\/t[dh]><t[dh][^>]*>/|/g' \
| sed -E 's/<\/t[dh]>//' \
| grep -v '^\s*$' \
| column -t -s '|' \
| colrm 14 21 | colrm 20 28 | colrm 63 95 | colrm 80
这将为您提供如下输出的结果:
使用column
和colrm
进行格式设置的结果
Group Name Item FAO Code Item HS+ Code Item Name Definition
Crops 800 5304_c Agave fib Including int
Crops 221 0802.11_a Almonds, Prunus amygda
Crops 711 0909 Anise, ba Include: anis
Crops 515 0808.10_a Apples Malus pumila;
Crops 526 0809.10_a Apricots Prunus armeni
…
或者,您可以使用cut
命令而不是colrm
来获得相同的格式。
如果不是像上面这样的漂亮打印/格式化输出,你真的想要真正的CSV,那么你还必须在字段周围发出引号,并在字段内CSV转义现有的引号;像这样:
示例:true CSV输出wget -q -O - "http://faostat.fao.org/site/384/default.aspx" \
| xmllint --html \
--xpath '//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]/tr' - \
| sed -E 's/"/""/g' \
| sed -E 's/<tr[^>]+>//' \
| sed -E 's/<\/tr>//' \
| sed -E 's/^[[:space:]]+<t[dh][^>]*>/"/' \
| sed -E 's/<\/t[dh]><t[dh][^>]*>/","/g' \
| sed -E 's/<\/t[dh]>/"/' \
| grep -v '^\s*$'
使用CSV的工具显然希望看到所有引号字符一起转义为两个引号字符;例如,因为单词""fufu""
在下面。
"In West Africa they are consumed mainly as ""fufu"", a stiff glutinous dough."
因此,上面代码段的sed -E 's/"/""/g'
部分就是这样做的。
"Group Name","Item FAO Code","Item HS+ Code","Item Name ","Definition"
"Crops","800","5304_c","Agave fibres nes","Including inter alia: Haiti hemp…"
"Crops","221","0802.11_a","Almonds, with shell","Prunus amygdalus; P. communis…"
"Crops","711","0909","Anise, badian, fennel, coriander","Include: anise…"
(强制性免责声明)以上所述,很多人会告诉你基于regexp的HTML / XML处理是kludgy +容易出错。它是,所以请谨慎使用上述方法(如果有的话)。
如果你有时间做对,你应该做的是:使用一个好的Web抓取库,或使用Python + lxml
来实际处理从评估XPath表达式(而不是对结果进行字符串化),或使用 xsltproc
或其他一些XSLT引擎。
但是你只需要从命令行中快速弄脏,上面就可以完成工作了。 但是,它很脆弱,所以如果输出的某些部分以某种意想不到的方式被打破,不要感到震惊。 如果您想要HTML / XML的强大功能,请不要使用基于正则表达式的方法。