Portál AbcLinuxu, 10. května 2025 02:35
tr -td -td -td tr -td -td -td --input --@onclickPřičemž těch řádků je hodně a některé (jako ten první) nenesou žádnou informaci. Ty podstatné řádky mají v poslední buňce input a potřeboval bych znát hodnotu textu v prvních dvou buňkách a parametr onclick elementu input v poslední buňce. Jak na to jít? Napadly mě dvě cesty jak toho dosáhnout: - sed: vymyslet regulární výraz, který z HTML vytáhne zajímavé HTML řádky (každý na jeden plaintext řádek) a pak pomocí prostého s/regexp/\1;\2;\3/ z těchto řádků vytáhnout řetězce a poskládat je. Vůbec ale netuším, vytáhnout ty podstatné HTML řádky - xsltproc --html: Zkusil jsem si vytvořit xsl styl, který tyto informace vytáhne. Nedaří se mi ale vytvořit šablonu tak, abych dostal obsah pouze ze mnou žádaných uzlů, buď se nevypíše nic nebo se vypíše obsah všech textových uzlů. Díky za jakoukoliv pomoc.
Jakmile z toho dostanu jeden HTML řádek na jeden plaintext řádek, tak jsem za vodou
A co třeba:
sed 's#</tr>#</tr>\n#' | sed -n '/<tr>/p' | \ sed 's#<tr><td>\([^<]*\)</td>...<input onclick="\([^"]*\)">...#\1 ..#'
's#</tr>#</tr>\n#'
=> 's#</tr>#</tr>\n#g'
PS: je to jenom nástřel, možná je to úplně špatně (a nakonec bude to XSLT nejlepším řešením)
--html The input document is an HTML file.
text/html
, ve skutečnosti není HTML.
Ještě je tady alternativa někde na pomezí - pokud umíte v AWK, tak bude možná nejrychlejší použít xgawk, což AWK upravený pro zpracování XML (místo řádkových vzorů se používá vzor pro počáteční elementy atp.) - více v dokumentaci.
sed
em, jak už jsem psal...
[html] [head] [title]titulek[/title] [script language="javascript"] skript, ktery obsahuje ruzne [elementy /] a mate tim xsltproc [/script] [head] [body] [table] [!-- layout --] [tbody] [tr] [td] [div] [table] [script language="javascript"]dalsi skript[/script] [tbody] [tr] [td]nadpis sloupce 1[/td] [td]nadpis sloupce 2[/td] [td]nadpis sloupce 3[/td] [td]nadpis sloupce 4[/td] [td][/td] [/tr] [tr] [td]text11[/td] [td]text12[/td] [td]text13[/td] [td]text14[/td] [td][input onclick="akce1"][/td] [/tr] [tr] [td]text21[/td] [td]text22[/td] [td]text23[/td] [td]text24[/td] [td][input onclick="akce2"][/td] [/tr][/tbody][/table][/div][/td][/tr][/tbody][/table][/body]přičemž celé <body> je na jednom řádku a potřebuji z toho dostat takovéto .csv:
text11;text12;akce1 text21;text22;akce2
Nejdřív bych naházel každý tag na jeden řádek (teda ukončit řádku za uzavíracím):
sed 's#</[^>]*>#&\n#g'
Tohle by mohlo dát všechny [tr]
do jednoho řádku a vytisknout je:
sed -n '/<tr>/{:a;s#</tr>#t k;N;s#\n##;b a;:k;p}'
Možná to nebude fungovat úplně přesně, ale myšlenka (použít N
a podmíněný skok t
) je snad správná.
Pak by mělo stačit prohnat to grep
em a sed
em přetransformovat...
user@stroj:~$ w3m tabulky.htm | awk '{...}'Taky mi z toho není jasné, jestli se v tom inputu objevují nějaké hodnoty generované javascriptem atp. To s ukázkou kódu nasimuluji stěží..
awk ' $0 ~ "[tr]" { ln = 0; } $0 ~ "[td]" { line[ln++] = $0; } $0 ~ "[/tr]" { print line[0] line[1] line[2] line[3] line[4]; } ' < file.html | sed 's/.../\1 \2 \3 \4 \5/'Ten sed už zvládneš určitě sám...
#!/usr/bin/env perl use strict; use warnings; undef $/; $_ = <>; my @ctx = m{ <tr>\s* # Úvodní TR <td>([^<]*)</td>\s* # První buňka <td>([^<]*)</td>\s* # Druhá buňka (?:<td>[^<]*</td>\s*)* # Zbývající buňky <td><input\s*onclick="(.*?)"> # INPUT, od kterého se odpíchneme (?{print "$1;$2;$3\n"}) # Vypsat výstup }gsx;Na zadané ukázce frčí dobře:
zoul@naima:~$ ./tabulky.pl < vstup.html text11;text12;akce1 text21;text22;akce2 zoul@naima:~$
<tr[^>]*>([^<]*)</td>\s* # První buňka?
#!/usr/bin/env perl -w0n my @ctx = m{ <tr[^>]*>\s* <td[^>]*>([^<]*)</td>\s* <td[^>]*>([^<]*)</td>\s* (?:<td[^>]*>[^<]*</td>\s*)* <td[^>]*><input\s*onclick="(.*?)"> (?{print "$1;$2;$3\n"}) }gsx;Byl bych řekl, že místo těžkopádného
[^>]*
(cokoliv kromě znaku >) bude fungovat i skromné srovnávání (.*?
), ale to mi z nějakého důvodu nešlo.
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="text"/> <xsl:template match="/html/body/table/tbody/tr/td/div/table/tbody/tr[td[last()]/input]"> <xsl:value-of select="td[1]"/>;<xsl:value-of select="td[2]"/>;<xsl:value-of select="td[last()]/input/@onclick"/> <xsl:text> </xsl:text> </xsl:template> <xsl:template match="*"><xsl:apply-templates select="*" /></xsl:template> </xsl:stylesheet>
creating dictionary for stylesheet reusing dictionary from opory.xsl for stylesheet xsltParseStylesheetProcess : found stylesheet xsltPrecomputeStylesheet: removing ignorable blank node xsltParseTemplateContent: removing text xsltCompilePattern : parsing '/html/body/table/tbody/tr/td/div/div/table/tbody/tr[td[last()]/input]' xsltCompilePattern : parsed /html/body/table/tbody/tr/td/div/div/table/tbody/tr[td[last()]/input], default priority 0.500000 added pattern : '/html/body/table/tbody/tr/td/div/div/table/tbody/tr[td[last()]/input]' priority 0.500000 xsltCompilePattern : parsing '*' xsltCompilePattern : parsed *, default priority -0.500000 added pattern : '*' priority -0.500000 parsed 2 templates Resolving attribute sets references Creating sub-dictionary from stylesheet for transformation Registered 0 modules reusing transformation dict for output Registering global variables Registering global variables from opory.xsl xsltProcessOneNode: no template found for / xsltProcessOneNode: applying template '*' for html xsltApplyTemplates: node: 'html' xsltApplyTemplates: select * xsltApplyTemplates: list of 2 nodes (...) xsltProcessOneNode: applying template '*' for tr xsltApplyTemplates: node: 'tr' xsltApplyTemplates: select * xsltApplyTemplates: list of 6 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 1 nodes xsltProcessOneNode: applying template '*' for input xsltApplyTemplates: node: 'input' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodesa v podobném duchu pokračuje dalších ~ 17k řádků.
/html/body/table/tbody/tr/td/div/tabletak, aby se chytil na tu správnou tabulku. Pokud v tom html jsou nějaká id, je nejlepší se chytit na ně. Jinak je potřeba zvolit správné pořadí elementů. Pokud ty tagy
<input>
jinde na stránce nejsou, můžete taky zkusit /html/body//table/tbody/tr[td[last()]/input]
/html/body/table/tbody/tr/td/div/div/table/tbody/tr[td[last()]/input]a výsledek jsem posílal minule.
Pokud v tom html jsou nějaká id, je nejlepší se chytit na ně.Vnější div má id "
p_r_i_n_t
". Zkusil jsem vztáhnout xpath výraz od něj:
id('p_r_i_n_t')/div/table/tbody/tr[td[last()]/input]a výsledek se od předchozího nijak neliší: Celé XML sem nechci posílat, tak dávám alespoň odkaz na zastřelenou obrazovku DOM inspektoru.
//div[@id='p_r_i_n_t']/div/table/tbody/tr[td[last()]/input]A podívejte se, jak jsou tagy zapsané v tom očištěném (XHTML) souboru, zda jsou malými písmeny, resp. přizpůsobte velikost písmen v XPath. Případně sem pošlete (nebo někam vystavte, přinejhorším pošlete e-mailem) alespoň začátek to XML – aby tam byl alespň jeden řádek s daty. Ta data a texty klidně nahraďte něčím jiným.
<div id='p_r_i_n_t'> <div align='center'> <h2>nadpis 2</h2> <p>lorem ipsum ...</p> <table border='1'> <tr> <th>nadpis 1</th> <th>nadpis 2</th> <th>nadpis 3</th> <th>nadpis 4</th> <th>nadpis 5</th> <th>nadpis 6</th> </tr> <tr bgcolor=""> <td>text11</td> <td>text12</td> <td>text13</td> <td align='right'>text14</td> <td align='right'>text15</td> <td><input type='button' value='Otevřít' onclick= 'js_fce("id")' /></td> </tr> <tr bgcolor="#FFCCCC"> <td>text21</td> <td>text22</td> <td>text23</td> <td align='right'>text24</td> <td align='right'>text25</td> <td><input type='button' value='Otevřít' onclick= 'js_fce("id")' /></td> </tr> (...) </table> </div> </div>Xpath výraz jsem podle toho upravil takto:
//div[@id='p_r_i_n_t']/div/table/tr[td[last()]/input]a xsltproc si stále mele svou:
creating dictionary for stylesheet reusing dictionary from opory.xsl for stylesheet xsltParseStylesheetProcess : found stylesheet xsltPrecomputeStylesheet: removing ignorable blank node xsltParseTemplateContent: removing text xsltCompilePattern : parsing '//div[@id='p_r_i_n_t']/div/table/tr[td[last()]/input]' xsltCompilePattern : parsed //div[@id='p_r_i_n_t']/div/table/tr[td[last()]/input], default priority 0.500000 added pattern : '//div[@id='p_r_i_n_t']/div/table/tr[td[last()]/input]' priority 0.500000 xsltCompilePattern : parsing '*' xsltCompilePattern : parsed *, default priority -0.500000 added pattern : '*' priority -0.500000 parsed 2 templates Resolving attribute sets references Creating sub-dictionary from stylesheet for transformation Registered 0 modules reusing transformation dict for output Registering global variables Registering global variables from opory.xsl xsltProcessOneNode: no template found for / xsltProcessOneNode: applying template '*' for html xsltApplyTemplates: node: 'html' xsltApplyTemplates: select * xsltApplyTemplates: list of 2 nodes (...) xsltProcessOneNode: applying template '*' for div xsltApplyTemplates: node: 'div' xsltApplyTemplates: select * xsltApplyTemplates: list of 1 nodes xsltProcessOneNode: applying template '*' for div xsltApplyTemplates: node: 'div' xsltApplyTemplates: select * xsltApplyTemplates: list of 3 nodes xsltProcessOneNode: applying template '*' for h2 xsltApplyTemplates: node: 'h2' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for p xsltApplyTemplates: node: 'p' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for table xsltApplyTemplates: node: 'table' xsltApplyTemplates: select * xsltApplyTemplates: list of 531 nodes (radek s nadpisy) xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes xsltProcessOneNode: applying template '*' for td xsltApplyTemplates: node: 'td' xsltApplyTemplates: select * xsltApplyTemplates: list of 1 nodes xsltProcessOneNode: applying template '*' for input xsltApplyTemplates: node: 'input' xsltApplyTemplates: select * xsltApplyTemplates: list of 0 nodes
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="tr[td[last()]/input]"> <xsl:value-of
select="td[1]"/>;<xsl:value-of select="td[2]"/>;<xsl:value-of
select="td[last()]/input/@onclick"/> <xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="//div[@id='p_r_i_n_t']/div/table"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="text"/> <xsl:template match="tr[td[last()]/input]"><xsl:value-of select="td[1]"/>;<xsl:value-of select="td[2]"/>;<xsl:value-of select="td[last()]/input/@onclick"/><xsl:text> </xsl:text> </xsl:template> <xsl:template match="/"> <xsl:apply-templates select="//div[@id='p_r_i_n_t']/div/table"/> </xsl:template> <xsl:template match="*"><xsl:apply-templates select="*" /></xsl:template> </xsl:stylesheet>
<div>
níže, transformace funguje, ale pokud mu podstrčím celé html, tak se mu nechce. Mimochodem umí XSL vyříznout z onoho <input onclick='jsFunkce("číselné_ID")'/>
; ono číselné_ID
?
<html>
jmenný prostor? Ale to by snad xsltproc
něco zahlásil…
Pokud je jsFunkce("…")
(to okolo) vždy stejné, dalo by se použít funkcí substring
a string-length
, případně replace
. Např.
<xsl:value-of select="replace(td[last()]/input/@onclick,'jsFunkce\("(.*)"\)','$1')"/>
<html xmlns="http://www.w3.org/1999/xhtml">
Pokud jeJe. Takto to vypadá dobře, jestli to bude funkční uvidíme až po vyřešení toho namespace.jsFunkce("…")
(to okolo) vždy stejné, dalo by se použít funkcísubstring
astring-length
, případněreplace
.
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0"> …
sed -e 's/\ / /g;s/<html[^>]*>/<html>/;s/jsFunkce(\"\([^"]*\)\")/\1/;'Díky všem za čas a pomoc.
Nicméně DOM inspektor pěkně kecá, protože žádný tag tbody v původním ani očištěném html není!No jo, na tenhle oblíbený trik jsem zapomněl. On zobrazuje skutečný aktuální DOM dokumentu, tj. to co vznikne parsováním a „hádáním“ toho, jak ten dokument má správně vypadat.
xsltproc
v tom ukecaném výstupu zmínilo.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.