Parser Python per pagine HTML

Python Parser HTML
Python Parser HTML in azione

Dopo (tanti) anni di studio ed un lavoro (precario) credo sia arrivata finalmente l’ora di iscrivermi all’albo degli Ingegneri, ma prima di farlo ho cercato di fare una piccola statistica su quanti ingegneri c’erano nella mia provincia (Potenza) e a quale tipo di specializzazione appartenessero.

Purtroppo lo strumento di ricerca del sito ( http://www.ordingpz.it/albo/Albolist.asp ) mette a disposizione dell’utente poche opzioni di ricerca, per tale motivo ho rispolverato la cara e vecchia libreria python Beautiful Soup per scrivere un piccolo script python il quale mi aiutasse ad effettuare il parsing di più pagine HTML e tirasse fuori i dati che mi interessavano.

Il sito in questione (http://www.ordingpz.it) ha una pagina web per ogni ingegnere iscritto all’albo. Tale pagina contiene i vari dettagli dell’ utente, compreso il campi che a me interessavano: “Settore” e “Specializzazione“.

Al seguente link è possibile avere un esempio reale del dettaglio dell’utente (l’ingegnere non è stato scelto a casa, ma è un mio amico con cui ha avuto il piacere di lavorare assieme).

http://www.ordingpz.it/albo/Alboview.asp?N__ISCR_=2714

Dalla sintassi dell’URL si capisce che ad ogni account è associato un ID (N__ISCR_) numerico univoco.

Per tale motivo il primo passo è stato quello di capire come estrapolare questi ID per poi andare a processare ogni singola pagina.

La prima parte dello script serve proprio a questo, a tirare fuori tutti gli ID dal sito.

Get ID

import urllib2
from bs4 import BeautifulSoup
import sys

f = open(‘IngPZAll.txt’, ‘w’)

url = “http://www.ordingpz.it/albo/Albolist.asp?pageno=1&t=Albo&RecPerPage=3000”
print url

try:
data = urllib2.urlopen(url).read()
except:
print url

soup = BeautifulSoup(data)

n = soup.findAll(“td”,{“width”:”100″})

#print n
numIscr = []

for i in n:
numIscr.append(i.div.string)

A questo punto la lista numIscr contiene tutti gli ID dell’albo degli Ingegneri di Potenza, adesso non ci resta che accedere alle singole pagine degli user e memorizzare tutte le informazioni che ci servono.

Il ciclo for può esterno non va altro che prendere tutti gli ID contenuti in numIscr , accedere alla pagina dell’utente ad esso associato e tramite BeautifulSoup effettuare il parsing della pagina HTML per tirare fuori le informazioni.

Parse HTML

[sourcecode language=”python”]
for i in range(len(numIscr)):
url = “http://www.ordingpz.it/albo/Alboview.asp?N__ISCR_=”+str(numIscr[i])
f.write(url+”\n”)
print “Parsing “+ str(i) + ” of ” + str(len(numIscr))
print url

try:
data = urllib2.urlopen(url).read()
except:
print url

soup = BeautifulSoup(data)
#print soup.prettify()
n = soup.findAll(“td” , “ewTableHeader”)

fields = []

for i in n:
fields.append(i.string.encode(‘ascii’,’ignore’))

n = soup.findAll(“div”)

values = [];

#print n
count = 0;

for i in n:
count=count+1;
#print count
if(i.string != None and count > 19):
#print i.string
values.append(i.string.encode(‘ascii’,’ignore’))

for i in range(len(values)):
try:
f.write(str(fields[i]+ “: “+values[i]).encode(‘utf-8’)+”\n”)
except:
print fields[i]+ “: “+values[i]

f.write(“\n”)

f.close()
[/sourcecode]

Nella lista fields saranno elencati tutti i campo della tabella (Ex. Nome, Cognome, Specializzazione ecc) ed in quella values tutti i valori dei campi prima citati. Lo step successivo è quello di scrivere tutte queste informazioni in un unico file per poi poter fare tutte le nostre statistiche in maniera semplice ed immediata.

Il file IngPZAll.txt è output dello script, ed avrà al suo interno il dettaglio di tutti gli ingegneri iscritti all’albo.

Output Example

[sourcecode language=”text”]
..
..
..
http://www.ordingpz.it/albo/Alboview.asp?N__ISCR_=2714
N Iscrizione: 2714
Cognome: MONACO
Nome: Pierpaolo
Luogo di Nascita: Potenza
Data di Nascita:
C.F.:
Indirizzo:
CAP:
Citt:
Provincia: PZ
Sezione: A
Settore: Civile ed Ambientale, Industriale, Dell’Informazione
N Iscrizione: 2714
Data Iscrizione: 04/01/2012
Specializzazione: Informatica
Sede Laurea: Roma
Data Laurea: 21/03/2011
Sede Abilitazione: Roma
Anno Abilitazione: 2011
Ordine di Provenienza: Potenza
Data Prima Iscrizione: 04/01/2012
..
..
[/sourcecode]

Ora con una semplice comando bash posso facilmente capire ciò che volevo…

[sourcecode language=”bash”]
unicondor@iMac:> grep -c “Specializzazione: Informatica” IngPZAll.txt
19
[/sourcecode]

Ecco l’informazione che cercavo, su 2506 iscritti all’albo solo 19 hanno come specializzazione Informatica 😀

Attach:

alboIngPzParser.py

IngPZAll.txt

GitHub Project:

https://github.com/flaviopace/SimplePythonHTMLParser

P.s. tutti i dati che trovate in questo articolo solo liberamente disponibili sul sito dell’albo degli Ingegneri di Potenza

Creazione Video Da Sequenza di Immagini Timelapse

Già mi ero cimentato qualche volta con la creazione di un video tramite sequenza di immagini timelapse, usando dei tool che in automatico mi creavano il flusso video da una sequenza di immagini.

Questa volta, da buon smanettone :P, ho voluto utilizzare solo la potenza della shell di Linux e strumenti OpenSource come ffmpeg.

L’hardware a disposizione non è stato uno dei migliori. Ho dovuto utilizzare il mio vecchio Asus EeePC 701 dotato di una misera telecamera da  640×480 px … e tanta pazienza :D.

1) Cattura Immagini Timelapse

Per la cattura delle immagini dalla videocamera ho usato il tool sviluppato da Ubuntu: cheese  e l’utility scrot per quanto riguarda la cattura delle immagine tramite screenshot.

Il mio script non fa altro che lanciare cheese in background a tutto schermo e catturare le immagini ogni 5 sec via scrot. So che esistono molti altri metodi per catturare le immagini direttamente dalla fotocamera senza effettuare lo screenshot, ma nel mio caso avevo bisogno di mostrare anche al pubblico le immagini che stavo riprendendo.

[sourcecode language=”bash”]
#!/bin/sh

/usr/bin/cheese &

while true
do
scrot /media/photo/’%H%M%S.png’
sleep 5
done

[/sourcecode]

2) Creazione Flusso Video

Se tutto è andato nel verso giusto avremo le nostre .png pronte per essere unite tra loro per creare il flusso video vero e proprio. FFMPEG è proprio il tool che fa a caso nostro, infatti basta dare questo piccolissimo comando per avere il video finale.

[sourcecode language=”bash”]
$ ./ffmpeg -i ../media/photo/%*.png -r 15 -vb 4096k out.mp4
[/sourcecode]

Dove:

  •  -i ../media/photo/%*.png  indica la sorgente (nel nostro caso le immagini) da cui assemblare il video
  •  -r 15 indica il frame rate che vogliamo utilizzare
  • -vb 4096k  indica il birate da utilizzare

Nel caso volessimo aggiungere anche una base musicale (non guasta mai) basta inserire un altro piccolo flag:

[sourcecode language=”bash”]
$./ffmpeg -i out.mp4 -i my_music.mp3 -vcodec codec -acodec codec output_video_with_music.avi -newaudio
[/sourcecode]

Questo è il mio timelapse effettuato durante la Sagra del Pecorino di Filiano 2013. Buona visione!

STM32F3Discovery + ChibiOS/RT + Fortune Wheel

STM32f3discovery

I problemi erano gia’ parecchi prima di iniziare la tesi, ma ora credo di aver passato ogni limite. Non posso passare le nottate appresso ad una board con 48K di RAM, 256KB di Flash ed un processore ARM Cortex-M a 78 Mhz preso da una insana, quanto assurda, smania verso ogni tipo di controllore per montarci su un RTOS 😛

La board in questione mi e’ stata gentilmente concessa dal mio Professore, per giunta lo stesso professore relatore della mia tesi. Per tale motivo, credo di aver una scusa decente per essere un po’ in ritardo nella stesura  :P.

Il programma di esempio su cui mi sono cimentato utilizza un RTOS ( ChibiOS/RT ) e la sua toolchain (davvero semplicissima da usare grazie alla sua integrazione con Eclipse) usata per compilare il file oggetto e caricarlo direttamente in flash.

Una passeggiata rispetto a quello fatto per il porting di ThreadX su un microcontrollore ARC-based, ma questo (forse) sara’ un articolo che cerchero’ di scrivere un po’ piu’ il la… sempre che il lavoro svolto sia sufficientemente decente.

Ritornando all’applicazione, e’ davvero un programmino semplice che emula il comportamento del famoso gioco della fortune wheel (non sono sicuro del nome 😛 ) ed e’ composto da tre threads:

  • main
  • thread1
  • thread2

Dove il main si occupara di gestire un interrupt che gli arriva quando premiamo il pulsante USER; thread1 non e’ altro che un semplice ciclo while che accende e spegne, in maniera circolare, gli 8 LED;  il thread2 che si occupa di verificare se nell’attimo in cui e’ stato premuto il pulsante USER la luce del LED rosso era accesa…. vabbe’ e’ piu’ semplice a farsi che a dirsi.

[sourcecode language=”C”]
#include "ch.h"
#include "hal.h"
#include "test.h"

static int check=0;

/*
* This is a periodic thread that does absolutely nothing except flashing LEDs.
*/
static WORKING_AREA(waThread1, 128);
static msg_t Thread1(void *arg) {

(void)arg;
chRegSetThreadName("blinker");
int i=0;
int ledTime[10]={215,260,220,600,40,20,10,5,15,25};
while (TRUE) {

/* pseudo random generator */
if(i%9 ==0)
i=0;
palSetPad(GPIOE, GPIOE_LED3_RED);
check=1;
chThdSleepMilliseconds(ledTime[i]);
i++;
palClearPad(GPIOE, GPIOE_LED3_RED);
check=0;
palSetPad(GPIOE, GPIOE_LED5_ORANGE);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED5_ORANGE);
palSetPad(GPIOE, GPIOE_LED7_GREEN);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED7_GREEN);
palSetPad(GPIOE, GPIOE_LED9_BLUE);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED9_BLUE);
palSetPad(GPIOE, GPIOE_LED10_RED);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED10_RED);
palSetPad(GPIOE, GPIOE_LED8_ORANGE);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED8_ORANGE);
palSetPad(GPIOE, GPIOE_LED6_GREEN);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED6_GREEN);
palSetPad(GPIOE, GPIOE_LED4_BLUE);
chThdSleepMilliseconds(125);
palClearPad(GPIOE, GPIOE_LED4_BLUE);

}
}

static WORKING_AREA(waThread2, 128);

static msg_t Thread2(void *arg) {

(void)arg;
chRegSetThreadName("allLed");

if(check){
palSetPad(GPIOE, GPIOE_LED3_RED);
palSetPad(GPIOE, GPIOE_LED5_ORANGE);
palSetPad(GPIOE, GPIOE_LED7_GREEN);
palSetPad(GPIOE, GPIOE_LED9_BLUE);
palSetPad(GPIOE, GPIOE_LED10_RED);
palSetPad(GPIOE, GPIOE_LED8_ORANGE);
palSetPad(GPIOE, GPIOE_LED6_GREEN);
palSetPad(GPIOE, GPIOE_LED4_BLUE);

}
}
/*
* Application entry point.
*/
int main(void) {

/*
* System initializations.
* – HAL initialization, this also initializes the configured device drivers
* and performs the board-specific initializations.
* – Kernel initialization, the main() function becomes a thread and the
* RTOS is active.
*/

//palSetPad(GPIOE, GPIOE_LED3_RED);

halInit();
chSysInit();

//palSetPad(GPIOE, GPIOE_LED3_RED);

/*
* Activates the serial driver 1 using the driver default configuration.
* PA9(TX) and PA10(RX) are routed to USART1.
*/
sdStart(&SD1, NULL);
palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(7));
palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(7));

//palSetPad(GPIOE, GPIOE_LED3_RED);

/*
* Creates the example thread.
*/
chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);

//palSetPad(GPIOE, GPIOE_LED3_RED);

/*
* Normal main() thread activity, in this demo it does nothing except
* sleeping in a loop and check the button state, when the button is
* pressed the test procedure is launched.
*/
while (TRUE) {
if (palReadPad(GPIOA, GPIOA_BUTTON)){
chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO, Thread2, NULL);
chThdSleepMilliseconds(500);
}
}
}

[/sourcecode]

P.s. forse e’ ora di trovarmi una ragazza 😀

 

GitHub

https://github.com/flaviopace/FortuneWheel_STM32F3Discovery-

Facebook Hacker Cup 2 Round: my Card Game Solution

FacebookHackerCup 2 Round

This is my solution to the problem of Card Game, work fine for small input… but when the value of k grows (k>500 ) my code spent a lot of time.

The bottleneck it’s about the iteration to find the max in the array…

So i hope you can optimize it 😀

[sourcecode language=”python”]

import sys
import math
from collections import Counter
import re
import itertools

in_file = open(sys.argv[1],"r")
exp = in_file.read()
in_file.close()

inputFile= exp.split("\n")

index=[]
values=[]

def findsubsets(S,m):
return set(itertools.combinations(S, m))
a=[]
flavio=1
for i in range(1, len(inputFile)-1) :
values=[]
allsub=[]
if(i%2) :
# index.append(inputFile[i])
n,k=map(int,inputFile[i].split())
else :

values= map(int,inputFile[i].split())

# Optimization , not call a function
allsub=itertools.combinations(values,k)

my_sum=0

for f in allsub:

my_max=max(f)
my_sum += (my_max)

if(my_sum>=1000000007):
my_sum = my_sum -1000000007

print "Case #"+str(flavio)+": "+str(my_sum)

flavio +=1

[/sourcecode]

GitHub

https://github.com/flaviopace/FacebookHackerCup2_CardGameSolution

Facebook Hacker Cup Qualification 2013: le mie soluzioni in Python

Hacker Cup Facebook

Quest’anno sono riuscito a partecipare (me ne sono ricordato :P) all’Hacker Cup che Facebook organizza ogni anno. I problemi per le qualificazioni erano 3 e di seguito vi posto le mie soluzioni scritte in python.

Il terzo problema (Find the min) non e’ difficile, sono riuscito a risolverlo ma il tempo che ci impiega il mio algoritmo e’ molto al di sopra di quello che mette a disposizione Facebook. In realta’ bastava guardare un po output della lista che utilizzavo per memorizzare i valori per capire che da un certo punto in poi si ripeteva ( per cui non serviva tutta la potenza di calcolo che avevo immaginato 😛 ). In poche parole per gli indici della liste pari a 2k+1, i valori si ripetevano… me ne sono accorto troppo tardi ed ho finito solo i primi due step 😀

1 – Beautiful strings  (20 points)

beautiful_stringstxt.txt

outputFile.txt

[sourcecode language=”python”]import sys
import math
from collections import Counter

in_file = open(sys.argv[1],"r")
exp = in_file.read()
in_file.close()

a= exp.split("\n")

b=[]
c=[]

values=[]

d={}
u={}

for i in range(1, len(a)-1) :

b.append(a[i].lower()[::-1])

flavio=1
for i in b:
d={}

values=[]
num=26
find=1
numSum=0
for k in range(0,len(i)):

#d[str(i[k])] = i.count(i[k])
if(str(i[k]).isalpha()):# or str(i[k])==’!’):
d[str(i[k])] = i.count(i[k])

for k in range(0,len(i)):
if (str(i[k]) in d):
# print i[k] + " num "+ str(num) + " # " + str(d[i[k]])
numSum += num*d[i[k]]
values.append(d[i[k]])
del d[i[k]]
num -=1

values_sorted= sorted(values, reverse=True)

num=26
numSum =0
for item in range(len(values_sorted)):

numSum += num * values_sorted[item]
num -=1
print "Case #" + str(flavio)+ ": " +str(numSum)
flavio +=1
[/sourcecode]

2 – Balanced Smileys  (35 points)

balanced_smileystxt.txt

outputBalanced.txt

[sourcecode language=”python”]import sys
import math
from collections import Counter
import re

in_file = open(sys.argv[1],"r")
exp = in_file.read()
in_file.close()

b=[]
c=[]

values=[]

d={}
u={}

for i in range(1, len(a)-1) :

b.append(a[i].lower())

replacements = {":(",":)"}

for i in range(0,len(b)) :
#b[i]= b[i].replace(

b[i]= re.sub(r’\([^)]*\)’, "", b[i])

b[i]= b[i].replace(‘:(‘,”)
b[i]= b[i].replace(‘:)’,”)

def isBalanced(strInput):
"""Validate if an input string is having balanced bracket pairs
this includes bracket ordering. i.e a round bracket must be closed
by a round bracket. Emtpy strings are treated as balanced."""
#if strInput:
# list of all bracket kinds, in paired tuples
brackets = [ (‘(‘,’)’), (‘[‘,’]’), (‘{‘,’}’)]
# define fake constants – python does not support the concept of constants
kStart = 0
kEnd = 1
# internal stack used to push and pop brakets in the input string
stack = []

for char in strInput:
for bracketPair in brackets:
if char == bracketPair[kStart]:
stack.append(char)
elif char == bracketPair[kEnd] and len(stack) > 0 and stack.pop() != bracketPair[kStart]:
return False

if len(stack) == 0:
return True

#return False

flavio=1
for i in range(0,len(b)) :
if(isBalanced(b[i])):
print "Case #" + str(flavio) + ": YES"
else:
print "Case #" + str(flavio) + ": NO"

flavio +=1

[/sourcecode]

3 – Find the Min  (45 points)

find_the_mintxt.txt

outputFindTheMin.txt

[sourcecode language=”python”]import os, sys

f = open(sys.argv[1], ‘r’)

T = int(f.readline())

def next(ary, start):
j = start
l = len(ary)
ret = start – 1
while j < l and ary[j]:
ret = j
j += 1
return ret

for t in range(T):
n, k = map(int, f.readline().strip().split(‘ ‘))
a, b, c, r = map(int, f.readline().strip().split(‘ ‘))

m = [0] * (4 * k)
s = [0] * (k+1)
m[0] = a
if m[0] <= k:
s[m[0]] = 1
for i in xrange(1, k):
m[i] = (b * m[i-1] + c) % r
if m[i] < k+1:
s[m[i]] += 1

p = next(s, 0)
m[k] = p + 1
p = next(s, p+2)

for i in xrange(k+1, n):
if m[i-k-1] > p or s[m[i-k-1]] > 1:
m[i] = p + 1
if m[i-k-1] <= k:
s[m[i-k-1]] -= 1
s[m[i]] += 1
p = next(s, p+2)
else:
m[i] = m[i-k-1]
if p == k:
break

if p != k:
print ‘Case #%d: %d’ % (t+1, m[n-1])
else:
print ‘Case #%d: %d’ % (t+1, m[i-k + (n-i+k+k) % (k+1)])
[/sourcecode]

Riferimenti:

https://www.facebook.com/hackercup/problems.php?round=185564241586420

Correct Solution Find The Min

http://facebook.stackoverflow.com/questions/14536384/python-too-slow-of-large-inputs

 

GitHub

https://github.com/flaviopace/FacebookHackerCupQualification2013

 

Python: UnicodeEncodeError: ‘ascii’ codec can’t encode character u’\u20ac’

Chiunque abbia fatto un minimo di parsing HTML tramite Python, ha incontrato almeno una volta questo fatidico errore (almeno lo spero, altrimenti sono l’unico pseudo smanettone ad averlo avuto 😛 ), soprattutto se il parsing e’ stato effettuato su pagine web in cui la valuta e’ l’Euro, dato che \u20ac rappresenta il simbolo “” (dannato Euro, anche nel mondo dell’informatica ci da’ problemi).

Il problema deriva avviene quando Python cerca di codificare il simbolo  € con un carattere ASCII.

Codifica?? Uhm?? se sei confuso come me… continua a leggere 😀

Prima di continuare facciamo un piccolo ricapitolazione di “Fondamento di Informatica”…la cosa piu’ importante da sapere e’ che: l’oggetto str in Python memorizza il suo valore come bytes, cioe’ con una sequenza di 8-bit, a.k.a string. Per questo motivo ogni carattere ASCII viene codificato in 8-bit, questo vuol dire che abbiamo a disposizione 0-255 rappresentazioni diverse… ma questo non e’ sufficiente per rappresentare i caratteri Russi, Arabi, Giapponessi… ed il nostro famoso simbolo dell’euro.

Per superare questa limitazione, Python usa unicode che memorizza il carattere con 16-bit o 32-bit dandoci la possibilita’ di rappresentare qualsiasi simbolo in qualsiasi linguaggio.

Voi direste.. Evviva! basta usare l’unicode e risolvo il problema, giusto? NO, non puoi…. ci mancherebbe che il mondo dell’informatica sia cosi’ logico e “clean” 😀

In particolare, non si puo scrivere su un file usando l’unicode, perche’ il file vuole una stringa che sia rappresentata da 8-bit. Dato che Python e’ molto “smart” cerchera’ in automatico di codificare/uniformare il carattere in unicode per la scrittura su file (o qualsiasi altro stream, come la shell), ma ahime’ c’e’ un problema… vediamo meglio con un esempio.

[sourcecode language=”python”]
>>> price_info = u’the price is \u20ac 5′
>>> type(price_info)
<type ‘unicode’>
>>> f = open(‘priceinfo.txt’,’wb’)
>>> f.write(price_info)
Traceback (most recent call last):
File "", line 1, in ?
UnicodeEncodeError: ‘ascii’ codec can’t encode character u’\u20ac’ in position 9: ordinal not in range(128)
>>>
[/sourcecode]

What happened?? Quando Python cerca di scrivere nel file “the price is € 5”, egli cerca di trasformare il valore unicode ‘\u20ac‘ in un carattere ASCII ad 8-bit… ma prima abbiamo detto che ASCII non contiene il carattere in questione… per cui… mi stai seguendo?? 😀

La soluzione e’ semplice (dopo che ho passato almeno una nottata su stackoverflow 😛 ):

[sourcecode language=”python”]
>>> price_info_enc = price_info.encode(‘utf-8’) # >> price_info_enc
‘it costs \xe2\x82\xac 5’
>>> type(price_info_enc)
<type ‘str’>
>>> f.write(price_info_enc)
>>> f.close()
[/sourcecode]

Semplice!! abbiamo codificato l’unicode tramite ‘utf-8’, il quale usa una sequenza di 3 caratteri ‘\xe2\x82\xac‘ per rappresentare il simbolo dell’Euro , in questo modo possiamo passare facilmente da ‘utf-8′ ad ASCII.

Ovviamente nel caso volessimo leggere il simbolo al contrario ( cioe’ vedere sullo schermo il simbolo dell’euro) dovremmo procedere con la decodifica dei tre caratteri in ‘utf-8′ e poi effettuare il print 😉

Ma c’e’ davvero gente che legge i miei post??? Mmmm ne dubito… ma come sempre, questo post mi servira’ da qui ad una settimana, visto che la mia memoria e’ scarsa e mi capitera’ di nuovo di imbattermi in questo problema… spero almeno di ricordarmi di aver scritto qualcosa sull’argomento 😛

Personalizzare Asterisk Web Manager con i Log Custom di Asterisk

Dopo aver visto come automatizzare il logratate dei file di log di Asterisk, passiamo alla visualizzazione dei file di Log ( cdr-custom)  Custom,  che Asterisk ci mette a disposizione, tramite l’interfaccia web di Asterisk WEB/PHP Management Interface.

Step I

Per prima cosa dobbiamo abilitare il Mappings del custom config file editando il seguente file: /etc/asterisk/cdr_custom.conf

il quale creera’ il file /var/log/asterisk/cdr-custom/Master.cvs con i campi che a noi interessano.

cdr_custom.conf

[sourcecode language=”bash”]

unicondor@server:~$ cat /etc/asterisk/cdr_custom.conf
;
; Mappings for custom config file
;
; To get your CSV output in a format tailored to your liking, uncomment the
; following lines and look for the output in the cdr-custom directory (usually
; in /var/log/asterisk). Depending on which mapping you uncomment, you may see
; Master.csv, Simple.csv, or both.
;
[mappings]
Master.csv =>${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(disposition)})},${CDR(sequence)},${CVS_QUOTE(${CDR(start)})}

[/sourcecode]

La modifica del file riporato il alto servirà a creare un file di log customizzato per le nostre neccessità. Il path ( installazione di default di Asterisk ) dove potete trovare il file è il seguente:

/var/log/asterisk/cdr-custom/Master.csv

Al seguente indirizzo potete trovare le info riguardanti i campi che ho usato per creare il mio file di Log Custom
Ora non ci resta altro che effettuare un bel reload dalla console di Asterisk per fargli vedere le modifiche

[sourcecode language=”bash”]

server*CLI> realod

[/sourcecode]

Step II

Fatto questo, andiamo a modificare il file logs.php che si trova nella directory web del WEB/PHP Management Interface, aggiungendo la voce che riguarda il file di log appena creato

[sourcecode language=”php”]
[rot@server asterisk]# cat /var/www/html/asterisk/logs.php
<?php <br ?>
if (isset($_POST[‘radio’])) {
if ($_POST[‘radio’] == "cdrformat") {
if (isset($_POST[‘pattern’])) $pattern = $_POST[‘pattern’];
header("Location: cdr.php?pattern=$pattern");
// header("Location: http://$_SERVER/asterisk/cdr.php?pattern=$pattern");
}
}

if (isset($_POST[‘radio’])) {
if ($_POST[‘radio’] == "cdrformat_custom") {
if (isset($_POST[‘pattern’])) $pattern = $_POST[‘pattern’];
header("Location: cdr_custom.php?pattern=$pattern");
// header("Location: http://$_SERVER/asterisk/cdr.php?pattern=$pattern");
}
}

include(‘header.php’);
include(‘files.php’);
include(‘functions.php’);

if (isset($_POST[‘submit’])) {
$submit = $_POST[‘submit’];
if (isset($_POST[‘loglines’])) $loglines = $_POST[‘loglines’];
else $loglines = 0;
if (isset($_POST[‘pattern’])) $pattern = $_POST[‘pattern’];
else $pattern = "";
if (isset($_POST[‘radio’])) $radio = $_POST[‘radio’];
if ($submit =="SUBMIT") {
if ($pattern != "") $cmd = "/bin/grep $pattern ";
else {
if ($loglines == 0) $cmd = "/bin/cat ";
else $cmd = "/usr/bin/tail -$loglines ";
}
if ($radio == "syslog") $cmd .= $SYSTEMMESSAGES;
if ($radio == "cdr") $cmd .= $CDRMASTER;
if ($radio == "events") $cmd .= $ASTEVENTLOG;
if ($radio == "messages") $cmd .= $ASTMESSAGESLOG;
if ($radio == "queue") $cmd .= $ASTQUEUELOG;
echo "
";
mysystem($cmd);
echo "
";
echo "
";
echo $cmd;
echo "
";
echo "Done !";
}
}
else
{
?></pre>
<div align="center"><form action="<?php echo $_SERVER[‘PHP_SELF’]; ?><br />" method="post">
<table>
<tbody>
<tr>
<td>Search String <input type="text" name="pattern" /></td>
<td>
Lines to List [0=All] <input type="text" name="loglines" value="50" /></td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><input type="radio" name="radio" value="events" /> Asterisk Event Log</td>
<td> </td>
</tr>
<tr>
<td><input type="radio" name="radio" value="messages" /> Asterisk Message Log</td>
<td> </td>
</tr>
<tr>
<td><input type="radio" name="radio" value="queue" /> Asterisk Queue Log</td>
<td> </td>
</tr>
<tr>
<td><input type="radio" name="radio" value="cdr" /> Asterisk CDR Log</td>
<td> </td>
</tr>
<tr>
<td><input type="radio" name="radio" value="cdrformat" /> Asterisk CDR Log [Table Formatted]</td>
<td> </td>
</tr>
<tr>
<td><input type="radio" name="radio" value="cdrformat_custom" checked="checked" /> Asterisk CDR ***Custom*** Log [Table Formatted]</td>
<td> </td>
</tr>
<tr>
<td><input type="radio" name="radio" value="syslog" /> System Log [SYSLOG]</td>
<td> </td>
</tr>
</tbody>
</table>
<input type="submit" name="submit" value="SUBMIT" />

<input type="reset" name="reset" value="RESET" /></form></div>
<pre>
<?php } ?>

[/sourcecode]

Ora non ci resta altro che andare a creare la pagina php cdr_custom.php , responsabile della lettura del file log custom di Asterisk e la sua stampa in una tabella html

[sourcecode language=”php”]

[unicondor@server asterisk]$ cat /var/www/html/asterisk/cdr_custom.php
<?PHP <br ?>/* Modified by Flavio Pace
*/
include("header.php");
include("files.php");

//Fields of the CDR in Asterisk
//—————————–
//
// 1. accountcode: What account number to use, (string, 20 characters)
// 2. src: Caller*ID number (string, 80 characters)
// 3. dst: Destination extension (string, 80 characters)
// 4. dcontext: Destination context (string, 80 characters)
// 5. clid: Caller*ID with text (80 characters)
// 6. channel: Channel used (80 characters)
// 7. dstchannel: Destination channel if appropriate (80 characters)
// 8. lastapp: Last application if appropriate (80 characters)
// 9. lastdata: Last application data (arguments) (80 characters)
// 10. start: Start of call (date/time)
// 11. answer: Answer of call (date/time)
// 12. end: End of call (date/time)
// 13. duration: Total time in system, in seconds (integer), from dial to hangup
// 14. billsec: Total time call is up, in seconds (integer), from answer to hangup
// 15. disposition: What happened to the call: ANSWERED, NO ANSWER, BUSY
// 16. amaflags: What flags to use: DOCUMENTATION, BILL, IGNORE etc,
// specified on a per channel basis like accountcode.
// 17. user field: A user-defined field, maximum 255 characters

if (isset($_GET[‘pattern’])) $pattern = $_GET[‘pattern’];
else $pattern = "";

$fname = $CDRCUSTOM;
$fd = fopen ($fname, "r");
if (!$fd) {
echo "Error opening $fname";
exit(0);
}
echo "</pre>
<table border="1" align="center">";echo "
<tbody>
<tr>";echo "
<th>Src</th>
";echo "
<th>Dst</th>
";echo "
<th>Duration</th>
";echo "
<th>Disposition</th>
";echo "
<th>User Field</th>
";echo "
<th>Start</th>
";echo "</tr>
";$limit = 0;$d = 0;while (!feof ($fd)) { $buffer = fgets($fd, 4096); $l = trim($buffer); if ($pattern != "") { if (!strstr($l,$pattern)) continue; } $badcommapat = ‘/\"[^\"]+,[^\",]+\"/’; if (preg_match($badcommapat,$l,$matches)) { $fixcomma = str_replace(",","-",$matches[0]); $l = str_replace($matches[0],$fixcomma,$l); } $e = explode(",",$l); $len = sizeof($e); echo "
<tr>"; for ($c=0;$c echo "
<td nowrap="nowrap">";
$e[$c] = trim($e[$c],"\r\n \"");
if ($c == 4) $e[$c] = str_replace ("\"", "", $e[$c]);
if ($e[$c] == "") echo " ";
else echo htmlspecialchars($e[$c]);
echo "</td>
"; }while ($c < 6) { echo "
<td> </td>
"; ++$c; } echo "</tr>
\n"; flush(); ++$d; if ($limit != 0) { if ($d >= $limit) break; }}echo "</tbody>
</table>
<pre>
";
fclose ($fd);
?>

[/sourcecode]

Alcuni Screenshot dell’interfaccia grafica customizzata

Mac OS X: free(), The Missing Command

Il comando messo a disposizione da Linux: free(1) è molto utile per capire l’ammontare di memoria RAM e memoria Swap libera ( ed usata ) sulla nostra macchina. Semplice e molto comodo… peccato che sul nostro Mac OS X non è presente, nonostante ciò la nostra cara Apple ci mette a disposizione altri eseguibili che ci danno queste informazioni ( anche se in maniera meno esplicita e meno diretta ): top(1) e vm_stat(1).

Prima di capire come interpretare le loro informazioni e vedere lo script che mi sono creato per estrapolarle e visualizzarle in modo conciso ed “umano”, facciamo una piccola parentesi ( sperando di ricordare correttamente i concetti studiati a Sistemi Operativi 😛 ) sul funzionamento della memoria sui moderni Sistemi Operativi.

Mac OS X, come i più moderni Sistemi Operativi, usa un sistema di memoria virtuale per la gestione della memoria.

Memoria Virtuale

Tra i molti scopi di tale tecnica, vi è quello di permettere al Sistema Operativo  di dare l’illusione di poter mettere a disposizione dei processi un ammontare illimitato di memoria ( in realtà abbiamo un limite indirizzabile per ogni processo di 4Gb per i sistemi a 32bit e 18 exabyte per i sistemi a 64bit ). Ex. anche se abbiamo sul nostro PC solo 2GB di memoria RAM installata, il Sistema Operativo, grazie a tale tecnica, potrà stanziare per ogni processo in esecuzione un ammontare di memoria superiore al totale della memoria RAM fisicamente installata.

Top command con 3GB di memoria fisica

Come potete vedere nell’immagine in alto, la colonna VSIZE indica l’ammontare di memoria virtuale che il Sistema Operativo ha assegnato al processo o all’applicazione, in megabytes (M) in questo caso. Notiamo che la quantità dei memoria virtuale assegnata ai primi due processi è di granlunga superiore a quella fisicamente disponibile ( 3GB nel mio caso, dovrò abilitare le donazioni paypall prima o poi 😛 ). La colonna RSIZE indica l’ammontare di memoria fisica che il processo stà effettivamente occupando( in questo caso è in kilobytes ), ed è molto minore di quella virtuale.

Spero abbiate compreso l’importanza di usare la memoria virtuale al posto di quella fisica :P. Daltronde la memoria RAM non è illimitata, per tale motivo Mac OS X raggruppa tale memoria in quattro categorie:

  • Wired: è la memoria occupata dal S.O., e non può mai essere paginata fuori dalla memoria ( deallocata ), dato che contiene le routines del kernel, librerie di sistema e molto altro..
  • Active: è la memoria usata dai processi che sono in esecuzione ( i nostri programmi per capirci ).
  • Inactive: è la memoria che è stata usata di recente dai programmi ma non è stata ancora liberata, vuol dire che se il S.O. avrà bisogno di memoria l’andrà a prendere da questa categoria.
  • Free: è la memoria, come suggerisce il nome, che è libera e può essere immediatamente associata ad un processo.

Quando lanciate un programma/applicazione, esso viene caricato nell’Active memory. Quando uscite dal programma, tuttavia, esso non viene rimosso dalla RAM, ma la sua memroia viene classificata come Inactive . Viene attuata questa politica perchè , in tal modo, è molto più veloce ri-lanciare il programma dato che si trova già nella RAM ( chiudete e provate a ri-lanciare Chrome/Firefox e vedrete che la sua riapertura è molto più veloce).

Quando tutta la memoria fisica è occupata ( Memory Free è pari a 0 ), il S.O. sposterà la Memoria Inactiva nella Memoria di Swap, in modo da aumentare lo spazio in quella Free. La memoria di Swap è molto lenta, dato che si trova sul nostro Hard-Disk, per tale motivo si cerca di utilizzarla il meno possibile.

Per i motivi visti nel precedente paragrafo, quando vogliamo esaminare quanta memoria stà usando il nostro computer e le sue prestazioni, dobbiamo fare attenzione a quanta memoria di Swap stiamo usando. Un altro indicatore da tener ben a mente è Pageins, il quale ci indica quanto spesso il S.O. ha preso i dati dalle memoria di Swap e messi in quella Active.

Dopo questa premessa noiosa ( non per me, dato che amo capire l’architettura alla base dei S.O 😀 ), passiamo ad esaminare il comando che vedremo più nel dettaglio: vm_stat

[sourcecode language=”bash”]
unicondor@iMac:→ vm_stat
Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free: 134596.
Pages active: 343278.
Pages inactive: 211846.
Pages speculative: 11035.
Pages wired down: 85028.
“Translation faults”: 43665599.
Pages copy-on-write: 999686.
Pages zero filled: 55006291.
Pages reactivated: 22831.
Pageins: 1624923.
Pageouts: 3796.
Object cache: 29 hits of 535971 lookups (0% hit rate)
[/sourcecode]

Grazie all’introduzione fatta, spero sia più chiaro come interpertrare correttamente i dati forniti in output. Per cui vi chiedo, qual’è l’ammontare di RAM non usata o libera? 

Io me la sono fatta spesso questa domanda, e la risposta che mi davo spesso era: Heh :-).

Adesso però possiamo dare una risposta “corretta” alla domanda, l’ammontare di RAM libera è data dalla somma di Free Memory + Inactive Memory .

C’è un problema, come spesso accade nel mondo dell’informatica :P, dato che l’output non parla di Memory ma di Pages.

Infatti per poter ottenere l’informazione che vogliamo, dobbiamo moltiplicare il numero di Pages per 4096 bytes ( che è la grandezza di ogni pagina) e poi dividere il tutto per 1048576 (dato che volgiamo l’output in MB).

Quindi, ricapitolando, se vogliamo sapere l’ammontare in MB delle Pages Free dobbiamo fare: (134596*4096) / 1048576 = 525 MB.

Allo stesso modo per le Pages Inactive= (211846 * 4096) / 1048576 =  827 MB

Memoria RAM libera = Pages Free + Pages Inactive = 525 + 827 = 1352 MB  o  1.3 GB

L’ultima linea di vm_stat è il rapporto tra gli oggetti richiesti e quelli che sono già attivi in memoria. Semplificando, più il valore di hit rate è alto e meno Pageins abbiamo… quindi conviene avere un alto hit rate ( non come nel mio caso ) per avere un sistema reattivo.

Liberare la Memory Inactive è spesso un’operazione molto lenta, per questo motivo, a volte, è utile effettuare tale operazione prima dell’avvio di un processo e/o applicazione pensante. Il comando “purge” fa a caso nostro, dato che serve per liberare la Memory Inactive senza dover ravviare il PC.

Il comando è presente nella suite developer tools, per cui è necessario installarla per poterlo usare.

Basta semplicemente aprire la shell e digitare il comando:

[sourcecode language=”bash”]
unicondor@iMac:→ purge

[/sourcecode]

l’operazione potrebbe richiedere qualche minuto ( dipende dalla quantità di memoria da liberare ), durante la sua esecuzione il S.O. potrebbe rallentarsi un pò

 

Ecco il piccolo script che uso per visualizzare l’ammontare totale della RAM, quella Free e quella Used:

Update 21_02_2012: Aggiunta function Usage() ed il relativo controllo dei parametri

vm_free.sh

[sourcecode language=”bash”]

#!/bin/sh

argv=$1

bytes=1048576
size=”MB”

function usage(){
echo “Usage: $0 [option]”
echo ” OPTION:”
echo ” -g –> Output in GB”
echo ” -k –> Output in KB”
echo ” default option is: -m –> MB”
exit
}
if [ ! “$#” -le “1” ]; then

usage

fi
if [ $1 ]; then

if [ “$1″ == -g ] ; then
bytes=1073741824
size=”GB”
else
if [ “$1″ == -k ] ; then
bytes=1024
size=”KB”

else
usage
fi
fi

fi

free_memory=`vm_stat | grep -e free -e inactive -e speculative| awk ‘{sum+=$3} END { print “”(sum*4096)” “}’`

total_memory=`vm_stat | grep -e free -e inactive -e speculative -e active -e wired| awk ‘{sum+=$3} END { print “”(sum*4096)” “}’`

used_memory=`vm_stat | grep -e wired -e active| awk ‘{sum+=$3} END { print “”(sum*4096)” “}’`

let “out_total=$total_memory/$bytes”

let “out_used=$used_memory/$bytes”

let “out_free=$free_memory/$bytes”

&nbsp;

printf “%5s \t\t%d %s\n” “Total” $out_total $size
printf “%5s \t\t%d %s\n” “Used” $out_used $size
printf “%5s \t\t%d %s\n” “Free” $out_free $size

[/sourcecode]

Possiamo lanciare lo script con il flag:

  • -g per output in GB
  • -k per output in KN

Optional:

Nel caso volessimo usare il piccolo script richiamando il comando free come nei sistemi Linux, non ci resta che spostare l’eseguibile bash in /usr/local/bin e rinominarlo:

[sourcecode language=”bash”]

unicondor@iMac:→ cp vm_free.sh /usr/local/bin/free
unicondor@iMac:→ free
Total 2742 MB
Used 1709 MB
Free 1271 MB

[/sourcecode]

 

 

N.B. nel calcolo della RAM libera si è inserito anche il conteggio delle Pages speculative.

Riferimenti:

https://developer.apple.com/library/mac/#documentation/Performance/Conceptual/ManagingMemory/Articles/AboutMemory.html

http://iamleeg.blogspot.com/2006/06/use-of-vmstat1.html

 

GitHub

https://github.com/flaviopace/MacOSX_free_bash_command

Happy Christmas Nerd Script

Happy Christmas Nerd Tree

 

Da bravi Nerd… ci prepariamo al natale cazzeggiando con la nostra bella bash 😀

 
[sourcecode language=”bash”]

#!/bin/sh

function tree_off()
{
echo ‘
*
/ \
/ \
/. : \
/_ . ._\
/ \
/ . . \
/ . . \
/ . . : \
/ : . \
/ . . : \
/___ : . ___\
/ : . : . \
—————-
/ \

HAPPY NERD CHRISTMAS

}

function tree_on()
{
echo ‘
*
/ \
/* \
/. : *\
/_ . ._\
/ * \
/ . . \
/ * . . *\
/ . * . : \
/ : * * . \
/ . * . : \
/___ : . * ___\
/ : . : . \
—————-
/ \

HAPPY NERD CHRISTMAS

}

stime=1;

while true;

do

tree_off;
sleep $stime;
clear;
tree_on;
sleep $stime;
clear;

done;

[/sourcecode]

Bash: Script download automatico con MlDonkey dei link ed2k dal Forum dduniverse.net

Vi assumete tutte le responsabilità nell’uso illegale dello script 😛

Stamattina non avendo nulla di meglio da fare, o meglio, cercando una “seria” alternativa allo studio… mi sono messo a girare per il forum dduniverse.net in cerca di qualche videolezione di UniNettuno sui Sistemi Operativi 😀

Dopo una breve ricerca riesco a trovare la pagina ed inizio a copiare-incollare i link ed2k… arrivato al link 5/29 mi chiedo, ma come posso automatizzare il download dei link ed2k sul mio server MLDoneky ? (uso Safari per la navigazione e non posso usare lo script messo a disposizione nella pagina del forum)

Eccovi la risposta che mi sono dato 😀

download_link_mldonkey.sh
[sourcecode language=”bash”]

#!/bin/sh

#############################
# Author #
# Flavio Pace #
# www.vitadastudente.it #
# #
############################
function usage(){
echo “Usage $0 URL_Links IP_Server_MlDoneky –> to login and download”
echo “Example :”
echo “$ sh download_link_mldonkey.sh http://dduniverse.net/ita/viewtopic.php?p=5691388 localhost ”
}
PHPBB_URL=http://dduniverse.net/ita/
PHPBB_TARGET_URL=$1
USER=flavio_user
PASS=flavio_pass
USER_MLDONEKY=admin
PASS_MLDONKEY=admin
MLDONKEY_IP=$2
MLDONKEY_PORT=4000

#————————— login —————————
if [ $# -eq 2 ] ; then

wget –save-cookies=./session-cookies-$USER $PHPBB_URL/ucp.php?mode=login -O – 1> /dev/null 2> /dev/null

SID=`cat ./session-cookies-$USER | grep _sid | cut -d$’\011′ -f7`

echo “Login $USER –> $PHPBB_URL SID=$SID”

wget –save-cookies=./session-cookies-$USER \
–post-data=”username=$USER&password=$PASS&redirect=index.php&sid=$SID&login=Login” \
$PHPBB_URL/ucp.php?mode=login –referer=”$PHPBB_URL/ucp.php?mode=login” \
-O – > /dev/null

echo ” Download Page with Link ”
wget –load-cookies ./session-cookies-$USER $PHPBB_TARGET_URL -O link.txt

echo ” Running Parsing on file ”
grep -o ‘ed2k://[^”]*’ link.txt | grep -v ‘;’ > link_par.txt

echo ” Connect Mldoneky Server”
(echo auth $USER_MLDONEKY $PASS_MLDONEKY;echo dllinks $PWD/link_par.txt; echo vd; echo q) | nc $MLDONKEY_IP $MLDONKEY_PORT

else

usage
fi

[/sourcecode]

 

Lo script è stato scritto per l’uso con il forum phpBB dduniverse.net, ma nulla vi vieta di modificarlo a vostra piacimento per qualsiasi altro forum e/o sito web.

I parametri USER e PASS sono relativi all’accesso a dduniverse.net

I parametri  USER_MLDONKEY e PASS_MLDONEY sono relativi all’accesso al server telnet di MLDonkey.

Gli altri parametri credo si commentino da soli e non hanno bisogno di spiegazioni, ora supponiamo di voler scaricare tutti i link relativi ai video sul corso di Reti di Calcolatori:

http://dduniverse.net/ita/viewtopic.php?p=5691388

il comando da usare è il seguente:
[sourcecode language=”bash”]

$ sh download_link_mldonkey.sh http://dduniverse.net/ita/viewtopic.php?p=5691388 localhost

[/sourcecode]

 

Se tutto sarà andato per il meglio, la nostra bella bash ci restituirà qualcosa del genere a conferma del fatto che tutti i passaggi sono andati a buon fine

 

Buon Download a tutti, e ricordatevi che scaricare materiale coperto da copyright è illegale 😛