我有一个 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 文件?
任何帮助表示赞赏。
最佳答案
//*[@id="ctl03_DesktopThreePanes1_ThreePanes_ctl01_MDlisting"]/tr
(也就是说,将 /tr
附加到您在问题中的 XPath 表达式)将只抓取每一行,并跳过 table
包装器(你不需要在你的输出中做任何事情)。
然后你还需要管道 xmllint --xpath
通过sed
输出或 perl
或者其他的东西:
示例: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 以下。改用 python 和 lxml
作为
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
格式化输出的命令如果您希望在控制台中读取打印/格式化的列/ TableView 并滚动/翻页,请将输出进一步通过管道传输到
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
如果不是像上面那样 pretty-print /格式化输出,你真的想要真正的 CSV,那么你还必须在字段周围发出引号,并 CSV 转义字段内的现有引号;像这样:
示例:真正的 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'
上面的代码片段的一部分就是这样做的。上述示例的 CSV 输出
"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…"
免责声明:您应该避免对 HTML/XML 进行基于正则表达式的处理
(强制性免责声明) 综上所述,很多人会告诉你,基于正则表达式的 HTML/XML 处理很容易出错。确实如此,因此请谨慎使用上述方法(如果有的话)。
如果你有时间把它做好,你应该做的是:而是使用一个好的网络抓取库 , 或使用 Python+
lxml
实际处理从评估 XPath 表达式返回的结果(而不是对结果进行字符串化),或使用 xsltproc
或其他一些 XSLT 引擎。但是你只需要命令行中的一些快速n-dirty,上面的工作就完成了。
但是,它很脆弱,所以如果输出的某些部分以某种意想不到的方式损坏,请不要感到震惊。 如果您想要一些健壮的 HTML/XML,请不要使用基于正则表达式的方法 .
关于bash - bash中的xpath解析表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32428678/