Parsing tabulky

Tommy

Parsing tabulky
« kdy: 23. 08. 2015, 10:37:23 »
Zdravím

Pokouším se napsat parsing tabulky do txt resp. csv souboru. Adresa URL je zadávána v BASH a celkem je těch parsovaných stránek hodně. Příklad :
http://en.tutiempo.net/climate/07-2015/ws-115200.html ?? - 11520.txt tak aby zůstala jen tabulka a ve sloupcích výskytu jedničky a nuly.

Jedno řešení jsem vymyslel:
XXX=`wget -qO - "http://en.tutiempo.net/climate/${m}-${y}/${ID}.html" | gawk '/&nbsp;<\/td><td>&nbsp;/' | sed  "s;</tr>;\n;g" | sed "s/&nbsp;//g" | sed "s/<strong>//g" | sed "s/<\/strong>//g" | sed "s/<tr><td>//g" | sed "s/<\/td><td>/\t/g" | sed "s/<\/td>//g" | grep -A100 "Indicates whether there was fog" | grep -B100 "Medias y totales mensuales" | head -n-1 | tail -n+2 | sed 's;o\+;1;g' | sed "s;\(^[0-9]*\);\1.${mu}.${y};" | sed "s/\t\t/\t0\t/g" | sed "s/\t\t/\t0\t/g" | sed "s/\t\t/\t0\t/g" | sed "s/\t\t/\t0\t/g" | sed "s/\t\n/\t0\n/g"  | tr -s "\n" "@" | sed "s/\t@/\t0@/g" | tr -s "@" "\n" | tr "\n" "@" | tr "\t" "&"`
if [ "${XXX}" ]
then
LEN=`expr length "${XXX}"`
if [ "${LEN}" -ge "${LENmin}" ] ;
then
echo "${XXX}" | tr -s "@" "\n" | tr "&" "\t" >> ${W}TUTIEMPO/Tutiempo_ALL/${CONTINENT}/${Countries}/${Station}_${IDA}.txt
echo "${XXX}" | tr -s "@" "\n" | tr "&" ";" >> ${W}TUTIEMPO/Tutiempo_ALL_CSV/${CONTINENT}/${Countries}/${Station}_${IDA}.csv
echo "${XXX}" | tr -s "@" "\n" | tr "&" "\t" >> ${W}TUTIEMPO/Tutiempo_ALL_By_Years/${CONTINENT}/${Countries}/${Station}_${IDA}/${Station}_${IDA}_${y}.txt
echo "${XXX}" | tr -s "@" "\n" | tr "&" ";" >> ${W}TUTIEMPO/Tutiempo_ALL_By_Years_CSV/${CONTINENT}/${Countries}/${Station}_${IDA}/${Station}_${IDA}_${y}.csv
echo "${CONTINENT};${Countries};${Station};${IDA};${y};${m};${XXX};" | sed "s/@/"@${CONTINENT}\;${Countries}\;${Station}\;${IDA}\;${y}\;${m}\;"/g" | tr -s "@" "\n" | tr -s "&" "\t" | tr -s ";" "\t" | grep "[0-9]\.[0-9]" >> ${W}TUTIEMPO/Tutiempo_ALL_By_Years_Solid/Tutiempo_ALL_By_Years_Solid.txt
echo "${CONTINENT};${Countries};${Station};${IDA};${y};${m};${XXX};" | sed "s/@/"@${CONTINENT}\;${Countries}\;${Station}\;${IDA}\;${y}\;${m}\;"/g" | tr -s "@" "\n" | tr -s "&" "\t" | tr -s "\t" ";" | grep "[0-9]\.[0-9]" >> ${W}TUTIEMPO/Tutiempo_ALL_By_Years_Solid/Tutiempo_ALL_By_Years_Solid.csv
fi
fi


Jenže zpracování proměnné XXX potřebje spustit hodně procesů, což není yrovna ideální. U pár stránek by to bylo jedno, ale při velkém počtu se to dost projeví na rychlosti. V awk se nevyznám.

Díky za nápad


Kit

Re:Parsing tabulky
« Odpověď #1 kdy: 23. 08. 2015, 11:26:18 »
Parsovat HTML ručně mi připadá jako pokus o sebevraždu. Zkusil bych se raději odpíchnout od tohoto kousku kódu:
Kód: [Vybrat]
xmllint --html --xpath '//*[@class="medias mensuales"]/tr' ws-115200.htmlNení to celé, ale snad to pomůže. Teď po ránu mě nic lepšího nenapadá.

wamba

Re:Parsing tabulky
« Odpověď #2 kdy: 23. 08. 2015, 14:42:52 »
imho bude lepší to přepsat celé, nebo velkou část do nějakého skriptovacího jazyka (Perl, Python, Awk, ...), např.
Kód: [Vybrat]
use 5.010;
use strict;
use warnings;
#use lib '/home/wamba/perl5/lib/perl5/';

use HTML::TreeBuilder;
use Text::CSV;

my $html      = HTML::TreeBuilder->new_from_url(shift);
my $table     = $html->find('table');
my @tablerows = $table->find('tr');

my $csv = Text::CSV->new( { binary => 1, eol => qq{$/} } );

foreach my $tr (@tablerows) {
    my @arr = map { $_->as_text } $tr->find( 'th', 'td' );
    $csv->print( *STDOUT, \@arr );
}
tento Perl skript převede první tabulku z HTML do CSV,
parametr je URL stránky, výsledné CSV vytiskne na standardní výstup
perl html_table_to_csv.pl http://en.tutiempo.net/climate/ws-115200.html

Kit

Re:Parsing tabulky
« Odpověď #3 kdy: 23. 08. 2015, 15:51:47 »
Tohle mi vyplivne CSV:
Kód: [Vybrat]
xmllint --html --xmlout --xpath '//*[@class="medias mensuales"]' ws-115200.html 2>/dev/null |
    xsltproc climate.xsl -

a tohle je výstupní šablona climate.xsl:
Kód: [Vybrat]
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="//table">
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="tr">
<xsl:apply-templates/>
<xsl:text>
</xsl:text>
</xsl:template>

<xsl:template match="td">
<xsl:apply-templates/>
<xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="strong">
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="*"/>
</xsl:stylesheet>

Unknown

Re:Parsing tabulky
« Odpověď #4 kdy: 23. 08. 2015, 21:50:03 »


Unknown

Re:Parsing tabulky
« Odpověď #5 kdy: 23. 08. 2015, 22:06:37 »
Prosty extract cele tabulky z HTML:

xidel -q -e '//*[@id="ColumnaIzquierda"]/div/div[3]/table/tbody/tr
-115200.html'

Unknown

Re:Parsing tabulky
« Odpověď #6 kdy: 23. 08. 2015, 22:08:30 »
Nejak se to domrvilo, tak jeste jednou:

Kód: [Vybrat]
xidel -q -e '//*[@id="ColumnaIzquierda"]/div/div[3]/table/tbody/tr[*]/ join(td)' 'http://en.tutiempo.net/climate/07-2015/ws-115200.html'

Tommy

Re:Parsing tabulky
« Odpověď #7 kdy: 25. 08. 2015, 10:50:52 »
Diky moc, tahle to bude mnohem rychlejsi, nez spouset desitky procesu pro kazdou tabulku. Zatim se mi neopdarilo, jak to presne do toho skriptu implementovat.

Unknown

Re:Parsing tabulky
« Odpověď #8 kdy: 25. 08. 2015, 13:30:21 »
Na windows vcetne downloadu:

Execution time: 0.368 s

Kit

Re:Parsing tabulky
« Odpověď #9 kdy: 25. 08. 2015, 18:18:59 »
Na windows vcetne downloadu:
Execution time: 0.368 s

Na Ubuntu jsem měl se svým skriptem podobný výsledek a myslím si, že obojí měření bude silně zatíženo chybou. Nehledě k tomu, že určitě máme rozdílné procesory - u mne jednojádrový Celeron na 2.8 GHz.

Kód: [Vybrat]
curl -s http://en.tutiempo.net/climate/07-2015/ws-115200.html |
    xmllint --html --xmlout --xpath '//*[@class="medias mensuales"]' - 2>/dev/null |
    xsltproc climate.xsl -

Rozdělení na více procesů může být výhodné, pokud má procesor více jader. Hezky se o ně podělí.

fos4

Re:Parsing tabulky
« Odpověď #10 kdy: 26. 08. 2015, 12:42:37 »
Kit: proč tam máš "enter" a nehodíš tam entitu? Aspoň můžeš zachovat formátování xsl..
<xsl:text>&#10;</xsl:text>

Kit

Re:Parsing tabulky
« Odpověď #11 kdy: 26. 08. 2015, 15:14:08 »
Kit: proč tam máš "enter" a nehodíš tam entitu? Aspoň můžeš zachovat formátování xsl..
<xsl:text>&#10;</xsl:text>

Na číselné entity jsem si moc nezvykl, ale je to alternativa.

Tommy

Re:Parsing tabulky
« Odpověď #12 kdy: 26. 08. 2015, 15:51:14 »
Na jednu tabulku 0,35-0,4 s by odpovídalo i s tím mým prvním skriptem, kde se spouští velké množství procesů. Co tabulka, to měsíc a rok trvá okolo 2,5-4 s. Rozsah dat je různý, většinou od 1973, obecně ale 1929-2015, tedy 87 let, což znamená 3-6 minut na jednu stanici. Vytvořit list stanic nebylo těžké, je jich něco málo přez 18 000. 18000*87 = 1 566 000 let, při 2,5-4 s na jeden rok tedy okolo 7-10 mil. s, tedy 80-110 dní celková doba procesu. Co měsíc, to dotaz, takže celkem 19 000 000 dotazů. Naštrěstí u prázdných stránek je čas nižší, celkově je tedy doba odhadnuta na 40-50 dní. Vzhledem k počtu puštěných procesů s každou stránkou je to cca 300 -600 mil. spuštěných procesů v té první navržené verzi.

Samotný download není tak dlouhý, do 0,2 s u plných stránek. Zatížení CPU 5-7 % takže vícejaderný proces nepomůže.

Kit

Re:Parsing tabulky
« Odpověď #13 kdy: 26. 08. 2015, 16:18:11 »
V tom prvním skriptu bych se tedy vrtat nechtěl. Program má být robustní a čitelný. Nevím, co budeš dělat, když některá ze stanic bude mít odlišnou strukturu HTML.

Spotřeba strojového času u mého skriptu je 0.07 sekundy, ale úzkým hrdlem bude spíš komunikace se serverem a I/O režie, která v tom není započtena.

Nebylo by jednodušší požádat provozovatele těch stanic o poslání celého balíku dat, případně najít jiné rozhraní či formát než HTML? Třeba tam někde mají data úhledně zabalená do zipu...

singerko

Re:Parsing tabulky
« Odpověď #14 kdy: 26. 08. 2015, 16:32:59 »
mozes pouzit nejaky prehliadac, napriklad links

links -dump ws-115200.html > ws-115200.txt

spravi to peknu tabulku s fixnou sirkou slpcov, potom uz rozparsujes iba tu tabulku. Do toho html mozes dat uz iba cast kodu obsahujucu iba html tabulku (vyparsujes nariklad cez xmlint)...