Because an XHTML web page is an XML document, it can be processed using XML tools (XSLT, XPath, XQuery etc.).
In this example, we will use relpipe-in-xmltable
to get list of headlines (outline) and other objects from a web page.
wget -O - http://blog.frantovo.cz/c/373 \
| relpipe-in-xmltable \
--namespace 'h' 'http://www.w3.org/1999/xhtml' \
--relation 'headlines' \
--records '//h:h1|//h:h2' \
--attribute 'level' string 'name()' \
--attribute 'headline' string '.' \
| relpipe-out-tabular
This pipeline looks for h1
and h2
headlines and presents them as a relation.
We can fine-tune the XPath expression to get only certain kinds of headlines (this is specific to particular site):
wget -O - http://blog.frantovo.cz/c/373 \
| relpipe-in-xmltable \
--namespace 'h' 'http://www.w3.org/1999/xhtml' \
--relation 'headlines' \
--records '//h:div[@id="obsah"]//h:h1|//h:div[@id="obsah"]//h:h2' \
--attribute 'level' string 'name()' \
--attribute 'headline' string '.' \
| relpipe-out-tabular
And get this listing:
headlines:
╭────────────────┬────────────────────────────────────╮
│ level (string) │ headline (string) │
├────────────────┼────────────────────────────────────┤
│ h1 │ Opravujeme myš: výměna spínačů │
│ h2 │ Popis problému │
│ h2 │ Výdrž 50 milionů kliknutí? │
│ h2 │ Oprava – výměna spínače │
│ h2 │ Myš Razer Orochi │
│ h2 │ Trackball Logitech TrackMan Marble │
│ h2 │ Myší spínače Omron a Panasonic │
│ h2 │ Závěr │
╰────────────────┴────────────────────────────────────╯
Record count: 8
Using slightly modified expressions:
wget -O - http://blog.frantovo.cz/c/376 \
| relpipe-in-xmltable \
--namespace 'h' 'http://www.w3.org/1999/xhtml' \
--relation 'images' \
--records '//h:div[@id="obsah"]//h:img' \
--attribute 'image_file' string '@src' \
--attribute 'title' string '@title' \
| relpipe-out-tabular
we will get list of images in different article on the same site:
images:
╭──────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────╮
│ image_file (string) │ title (string) │
├──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ /s/1467/nahled_IMG_2778.JPG │ Nordic nRF52840, Logitech, bezpečnost bezdrátových myší a klávesnic, MouseJack │
│ /s/1472/nahled_IMG_2782.JPG │ nRF24, hackování bezdrátových klávesnic a myší │
│ /s/1465/nahled_IMG_2768.JPG │ Nordic nRF52840, Logitech, bezpečnost bezdrátových myší a klávesnic, MouseJack │
│ /s/1475/logitacker-prikazy.png │ LOGITacker – rozhraní a příkazy │
│ /s/1476/logitacker-zarizeni-modra-1.png │ LOGITacker – zařízení – bez klávesnice a myši │
│ /s/1477/logitacker-zarizeni-modra-2.png │ LOGITacker – zařízení – rozpoznána klávesnice a myš │
│ /s/1478/logitacker-zarizeni-zelena-1.png │ LOGITacker – zařízení – dešifrovaná klávesnice a myš │
│ /s/1463/nahled_IMG_2762.JPG │ Nordic nRF52840, Logitech, bezpečnost bezdrátových myší a klávesnic, MouseJack │
╰──────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────╯
Record count: 8
There might be multiple --relation
sections and we can get multiple relations from a single XML stream:
wget -O - http://blog.frantovo.cz/c/373 \
| relpipe-in-xmltable \
--namespace 'h' 'http://www.w3.org/1999/xhtml' \
--relation 'headlines' \
--records '//h:div[@id="obsah"]//h:h1|//h:div[@id="obsah"]//h:h2' \
--attribute 'level' string 'name()' \
--attribute 'headline' string '.' \
--relation 'images' \
--records '//h:div[@id="obsah"]//h:img' \
--attribute 'image_file' string '@src' \
--attribute 'title' string '@title' \
| relpipe-out-tabular
So we can collect various types of objects in a single run. Such data can be stored/catalogized for later use. Or we can e.g. run a shell command for each of them – like if we have a website with some interesting content, we will find the XPath pattern of such content and use it to download desired files:
# our favorite function used also in other examples;
# reads values separated by a \0 byte into a variable;
# this is a safer way than a space or newline separated data:
read_nullbyte() { local IFS=; for v in "$@"; do export "$v"; read -r -d '' "$v"; done }
wget -O - http://blog.frantovo.cz/ \
| relpipe-in-xmltable \
--namespace 'h' 'http://www.w3.org/1999/xhtml' \
--relation 'images' \
--records '//h:div[@id="plakáty"]//h:img' \
--attribute 'image_file' string '@src' \
| relpipe-out-nullbyte \
| while read_nullbyte img; do wget "https://blog.frantovo.cz$img"; done
XPath is a very powerful language and allows us to work with the context of the nodes (XPath axes) or call various functions – so we can easily pick exactly what we want and download just it, or process it in a different way (compute some statistics, catalogize etc.).
n.b. many web pages are poorly written and contain invalid formatting.
But fortunatelly there is the tidy
tool which can usually clean up such garbage:
wget -O - https://www.abclinuxu.cz/blog \
| tidy -asxhtml -numeric \
| relpipe-in-xmltable \
--namespace 'h' 'http://www.w3.org/1999/xhtml' \
--relation 'headlines' \
--records '//h:h1|//h:h2' \
--attribute 'level' string 'name()' \
--attribute 'title' string 'normalize-space(.)' \
| relpipe-tr-awk --relation '.*' --where 'NR <= 10'
| relpipe-out-tabular
so we can fix their mistakes and process even such web sites:
headlines:
╭────────────────┬────────────────────────────────────────────────────╮
│ level (string) │ title (string) │
├────────────────┼────────────────────────────────────────────────────┤
│ h2 │ KOMIX - Časový posun │
│ h2 │ Opus Magnum │
│ h2 │ Jednoduchá CRUD aplikace (Go a MySQL) │
│ h2 │ Vzdálená správa většího počtu strojů │
│ h2 │ Haluan kadota, vielä paremmin, en koskaan syntynyt │
│ h2 │ LibreOffice a viac ako 1024 stĺpcov │
│ h2 │ The Catch CTF 2019 │
│ h2 │ Zprávička: Nový programovací jazyk Č++ │
│ h2 │ Stroj se zastaví │
│ h2 │ KOMIX - Užívání │
╰────────────────┴────────────────────────────────────────────────────╯
Record count: 10
The AWK transformation is used just as an illustration how we can combine various tools together.
However, limiting of the records can be done by the --records '(//h:h1|//h:h2)[position() <= 10]'
XPath expression in the relpipe-in-xmltable
transformation.
Relational pipes, open standard and free software © 2018-2022 GlobalCode