Jak to z Microsoftem bywa nie jest łatwo. Są dwa formaty Excela
-- stary (.xls
) oraz nowy (.xlsx
).
Pakiety Perlowe
Spreadsheet::Excel
oraz Spreadsheet::ParseXLSX
radzą sobie
nieźle, aczkolwiek oczywiście gwarancji nie ma i być nie może skoro
sam Excel czasami siebie samego nie potrafi zinterpretować.
No ale jest jeszcze trzeci format: jak plik .xlsx
jest
zabezpieczone hasłem (password
protected). I na taką okoliczność nie ma zbyt wielu narzędzi.
Można wszakże problem rozwiązać w dwóch krokach korzystając
Libreoffice, który potrafi interpretować pliki Excela i można go
uruchomić w trybie batch:
#!/bin/bash XLS="$1" TMP="${XLS%.*}.xlsx" libreoffice --headless --convert-to xlsx "$XLS" --outdir ./xlsx-temp/ perl xslx2csv.pl ./xlsx-temp/"$TMP" "$OUTFILE"
Powyższy skrypt obsłuży wszystkie rodzaje plików Excela,
zamieniając je najpierw na plik w formacie XLSX
(plik password protected
zostanie
zmieniony na prawdziwy format XLSX,
interpretowalny przez np. Spreadsheet::ParseXLSX
).
Można od razu konwertować do CSV (--convert-to csv
), ale konwersji będzie podlegać
tylko pierwszy arkusz. Jak interesuje nas na przykład drugi, to kicha...
nie da się (a przynajmniej ja nie wiem jak to osiągnąć). Inny problem
to zamiana XLSX→XLSX -- nie ma w LibreOffice możliwości
określenia nazwy pliku wynikowego, a próba:
libreoffice --headless --convert-to xlsx plik.xlsx
Kończy się błędem. Na szczęście jest obejście w postaci
opcji --outdir
. Plik wyjściowy -- o tej samej nazwie
co wejściowy -- jest
zapisywany w innym katalogu i problem rozwiązany.
Po zamianie Excela na ,,kanoniczny'' XLSX do konwersji na CSV można wykorzystać następujący skrypt Perla:
#!/usr/bin/perl # Wykorzystanie perl xslx2csv.pl plik.xslx [numer-arkusza] use Spreadsheet::ParseXLSX; use open ":encoding(utf8)"; use open IN => ":encoding(utf8)", OUT => ":utf8"; $xslxfile = $ARGV[0]; $ArkuszNo = $ARGV[1] || 1; ## domyślnie arkuszu 1 my $source_excel = new Spreadsheet::ParseXLSX; my $source_book = $source_excel->parse("$xslxfile") or die "Could not open source Excel file $xslxfile: $!"; # Zapisuje zawartość wybranego arkusza do hasza %csv my %csv = (); foreach my $sheet_number (0 .. $source_book->{SheetCount}-1) { my $sheet = $source_book->{Worksheet}[$sheet_number]; print STDERR "*** SHEET:", $sheet->{Name}, "/", $sheet_number, "\n"; if ( $ArkuszNo == $sheet_number + 1 ) { next unless defined $sheet->{MaxRow}; next unless $sheet->{MinRow} <= $sheet->{MaxRow}; next unless defined $sheet->{MaxCol}; next unless $sheet->{MinCol} <= $sheet->{MaxCol}; foreach my $row_index ($sheet->{MinRow} .. $sheet->{MaxRow}) { foreach my $col_index ($sheet->{MinCol} .. $sheet->{MaxCol}) { my $source_cell = $sheet->{Cells}[$row_index][$col_index]; if ($source_cell) { $csv{$row_index}{$col_index} = $source_cell->Value; } } } } }
Arkusz jest w haszu %csv
.
Jak go przekształcić/wydrukować itp. pozostawiam inwencji ewentualnego czytelnika.
GoogleCL przestało działać, bo Google przestało obsługiwać wersję OAuth 1.0. Ponadto, wygląda na to, że dostosowanie tego użytecznego narzędzia do wersji OAuth 2.0 bynajmniej nie jest trywialne na co wskazują liczne (ale do tej pory bezskuteczne) prośby i wołania o aktualizację GoogleCL, które można znaleźć w Internecie.
Ponieważ poszukiwania
w miarę podobnego zamiennika zakończyły
się niepowodzeniem, nie pozostało nic innego zmajstrować coś samodzielnie.
Autoryzację OAuth 2.0 mam już opanową -- obsługuje ją Pythonowy skrypt oauth2picasa.py
.
(Skrypt jest (zapożyczonym) fragmentem z projektu
picasawebsync
).
Wystarczyło
dorobić następujący prosty skrypt Perlowy (por. także:
Publishing
a blog post):
#!/usr/bin/perl # *** Wyslanie posta na blogger.com *** use strict; use LWP::UserAgent; use XML::LibXML; use Getopt::Long; my $profileID="default"; my $blogID = '1928418645181504144'; # Identyfikator bloga my $blog_entry ; ## Na wypadek gdy ktoś ma kilka blogów moża podać na któr ## ma być wysłany post używając opcji -blog GetOptions( "blog=s" => \$blogID, "post=s" => \$blog_entry) ; if ( $blog_entry eq '' ) { print STDERR "*** USAGE: $0 -b blog -p message (-b is optional) ***\n" } ## sprawdź czy post jest well formed: my $parser = XML::LibXML->new(); eval {my $res_ = $parser->parse_string($blog_entry) }; if ($@) { die "*** Error parsing post message! \n"; } my $ACCESS_TOKEN=`oauth2blogger.py`; # pobierz ACCESS_TOKEN print STDERR "*** AccessToken: $ACCESS_TOKEN ***\n"; my $req = HTTP::Request->new( POST => "https://www.blogger.com/feeds/$blogID/posts/default"); $req->header( 'Content-Type' => 'application/atom+xml' ); $req->header( 'Authorization' => "Bearer $ACCESS_TOKEN" ); $req->header( 'GData-Version' => '2' ); $req->content($blog_entry); my $ua = LWP::UserAgent->new; my $res = $ua->request($req); # Jeżeli coś jest nie tak poniższe drukuje verbatim: # http://www.perlmonks.org/bare/?node_id=464442 # $ua->prepare_request($req); print($req->as_string); exit ; if ($res->is_success) { my $decoded_response = $res->decoded_content; print STDERR "*** OK *** $decoded_response\n"; } else { die $res->status_line; }
Wykorzystanie:
perl blogger_upload.pl -p 'treść-posta'
Treść posta musi być oczywiście w formacie xHTML i zawierać
się wewnątrz elementu content
, który z kolei
jest wewnątrz elementu entry
.
Element entry
zawiera także title
określający tytuł posta, oraz elementy category
zawierające
tagi. Przykładem może być coś takiego:
<entry xmlns='http://www.w3.org/2005/Atom'> <title type='text'>Marriage!</title> <content type='xhtml'> <div xmlns="http://www.w3.org/1999/xhtml"> <p>Mr. Darcy has proposed marriage to me!</p> <p>He is the last man on earth I would ever desire to marry.</p> <p>Whatever shall I do?</p> </div> </content> <category scheme="http://www.blogger.com/atom/ns#" term="marriage" /> <category scheme="http://www.blogger.com/atom/ns#" term="Mr. Darcy" /> </entry>
Opisany skrypt jest tutaj:
blogger_upload.pl
.