awk, sed, grep, perl ... quale stampare in questo caso?


2

Ho questa sintassi in un file (tempi di risposta http da analogico):

<thead><tr><th class="x">seconds</th><th class="R">reqs</th><th class="r">%reqs</th><th class="B">Gbytes</th><th class="b">%bytes</th></tr></thead>
<tbody><tr><td class="x">0</td><td class="R">10927</td><td class="r"> 0.47%</td><td class="B">0.01</td><td class="b"> 0.18%</td></tr>
<tr><td class="x">&lt;= 0.01</td><td class="R">1026471</td><td class="r">44.59%</td><td class="B">0.11</td><td class="b"> 1.81%</td></tr>
<tr><td class="x">0.01-0.02</td><td class="R">535390</td><td class="r">23.26%</td><td class="B">0.06</td><td class="b"> 0.95%</td></tr>
<tr><td class="x">0.02-0.05</td><td class="R">93298</td><td class="r"> 4.05%</td><td class="B">0.27</td><td class="b"> 4.29%</td></tr>

eccetera.

Quello che voglio essere lasciato è il valore per secondi, quindi 2 caratteri dopo "x" e prima del primo <

E anche la lunghezza della richiesta, quindi 2 caratteri dopo "R" e prima del successivo successivo <

Probabilmente non è l'esercizio migliore per affrontare la regex, ma è quello che mi fa impazzire. Qualsiasi aiuto sarebbe incredibilmente utile.

Risultato atteso:

seconds reqs 
0 10927 
&lt= 0.01 1026471 
0.01-0.02 535390 
0.02-0.05 93298

1
L'implementazione della tua teoria: sed 's/.*"x".\([^<]*\).*"R".\([^<]*\).*/\1\t\2/' filenota che l'ho pubblicato solo per la tua educazione. Meglio non usare tale soluzione.
arte

4
Sento che questo può essere rilevante qui: stackoverflow.com/questions/1732348/… ;). No, ma seriamente, se hai bisogno di analizzare HTML potresti voler esaminare i parser HTML.
Kotte

Risposte:


3

A meno che tu non sia molto sicuro del formato dell'HTML, non ne abbia il controllo, non è critico con errori, ecc. Potresti usare regex, ma come detto non è raccomandato.

Lo uso spesso da solo, ma poi di solito per una volta l'estrazione di alcuni dati semplici.


Ad esempio, potresti usare Perl con HTML :: TokeParser :: Simple .

Molto semplificato:

#!/usr/bin/perl

use strict;
use warnings;

use HTML::TokeParser::Simple;
use HTML::Entities;
use utf8;

die "$0 [file | url]\n" unless defined $ARGV[0];

my $tp;
if ($ARGV[0] =~ /^http:\/\//) {
    $tp = HTML::TokeParser::Simple->new(url => $ARGV[0]);
} else {
    $tp = HTML::TokeParser::Simple->new(file => $ARGV[0]);
}

if (!$tp) {
    die "No HTML file found.\n";
}

# Array to store data.
my @val;
# Index
my $i = 0;

# A bit mixed code with some redundancy. 
# Could be done much simpler, - or much more safe. 
# E.g. Check for thead, tbody etc and call a sub to parse those.
# You could off course also print directly (not save to array),
# but you might want to use the data for something?
while (my $token = $tp->get_token) {
    if ($token->is_start_tag('th') && $token->get_attr('class') eq 'x') {
        $val[$i++] = $tp->get_token->as_is;
    } elsif ($token->is_start_tag('th') && $token->get_attr('class') eq 'R') {
        $val[$i++] = $tp->get_token->as_is;
    } elsif ($token->is_start_tag('td') && (
            ($token->get_attr('class') eq 'x') ||
            ($token->get_attr('class') eq 'R'))) {
        $val[$i++] = decode_entities($tp->get_token->as_is);
    }
}

my @width_col = (10, 8);

if ($i > 2 && !($i % 2)) {
    $i = 0;
    printf("%*s %*s\n",
        $width_col[0], "$val[$i++]",
        $width_col[1], "$val[$i++]"
    );
    while ($i < $#val) {
        printf("%*s %*d\n",
            $width_col[0], "$val[$i++]",
            $width_col[1], "$val[$i++]"
        );
    }
} else {
    die "ERR. Unable to extract data.\n"
}

Risultato del campione:

$ ./extract htmlsample 
   seconds     reqs
         0    10927
   <= 0.01  1026471
 0.01-0.02   535390
 0.02-0.05    93298

2

Come è stato menzionato, regex non è buono per l'analisi dell'html . Simile a un'altra risposta di analisi , puoi creare una Ruby one-liner come la seguente per farlo per te. Nota che richiede Nokogiri che puoi installare come gem ( sudo gem install nokogiri).

ruby -rnokogiri -e 'h = Nokogiri::HTML(readlines.join); h.css("tr .x").zip(h.css("tr .R")).each { |d| puts "#{d[0].content} #{d[1].content}" }' sample.html

Legge da sample.html e crea un array bidimensionale che contiene tutto con l'attributo class="x"all'interno di un trelemento accoppiato con tutto con l'attributo class="R"all'interno di un trelemento. Quindi stampa una di queste coppie per riga. Per il tuo esempio l'output è il seguente:

seconds reqs
0 10927
<= 0.01 1026471
0.01-0.02 535390
0.02-0.05 93298

2

Questo utilizza sede quindi è possibile utilizzare cutper ottenere i campi desiderati. Questa è una riga, ma ho scritto come file di script con commenti per chiarezza.

#!/bin/sed -f
s!</*thead!<tbody!g;      # to not get caught by 'th' below
s!<t[dh][^>]*>!%%%!g;     # replace start tag 'td' or 'th' with a delimitor
s!</t[dh]>!@@@!g;         # replace end tag 'td' or 'th' with a delimitor
s/<[^>]*>//g;             # delete any other tags
s/%%%\([^@]*\)@@@/\1 /g;  # get text between start and stop delimitors with a space
s/ $//                    # remove trailing space

Chiamandolo come:

$ sed -f glean.sed test.html
seconds reqs %reqs Gbytes %bytes
0 10927  0.47% 0.01  0.18%
&lt;= 0.01 1026471 44.59% 0.11  1.81%
0.01-0.02 535390 23.26% 0.06  0.95%
0.02-0.05 93298  4.05% 0.27  4.29%

Quindi puoi usare quello che ti piace per ottenere i primi due campi (come suggerisco con cut).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.