Archivio

Archivio per la categoria ‘python’

SublimeText2 e Virtual Environment

24 aprile 2018 Commenti chiusi

Stiamo utilizzando sublime text 2 per scrivere codice.
Il nostro complicatissimo codice è il seguente:

# example python 2

if __name__ == '__main__':
    print "Python 2 example"

Per eseguirlo accediamo al menu Tools -> Build-systems e scegliamo Python
Ora premendo CTRL-B o Tools -> Build il “programma” va in esecuzione.


SUpponiamo di voler utilizzare python 3; creiamo nella stessa directory un virtual environment (ad es. venv3) e modifichiamo il nostro codice in

# example python 3
class A:
	pass

if __name__ == '__main__':
    a = A()
    print("Python 3:", a.__mro__)

Lo eseguiamo e come è giusto che sia, riceviasmo un errore inquanto Sublime Text continua ad utilizzare il suo interprete Python, selezionato in precedenza dal menu Tools -> Build-systems.

Per poter utilizzare un virtual environments è necessario salvare il progetto con nome .
Nel caso specifico, il virtualenv è stato creato nella stessa directory del progetto.
Successivamente editiamo il file .sublime-project generato in automatico da Sublime Text ed inseriamo il seguente contenuto:

{
    "folders":
    [
        {
            "path": ".",
            "file_exclude_patterns": ["*.pyc"]
        }
    ],

    "build_systems":
    [
        {
            "name":"Virtualenv 3.6",
            "working_dir": "${project_path:${folder}}/",
            "cmd":
            [
                "${project_path}/<nome_mio_venv>/Scripts/activate"
            ],
            
            "cmd":
            [
                "${project_path}/<nome_mio_venv>/Scripts/pythonw",
                "$file"
            ]
        }
    ]
}

la chiave “folders” contiene la path del mio progetto ed è possibile scriverla come path assoluta, nella forma “/C/PATH_TO_PROJECT”

la chiave “build_systems” contiene una lista di dizionari.
Ogni dizionario rappresenta il build-system che potremo selezionare dal menu tools > Build-Systems
Nel nostro caso le chiavi fondamentali del dizionario sono:
“name”: il nome che apparirà nel menu Tools > Build-systems.
“working_dir”: la directory dove stiamo lavorando
“cmd”: il comando che viene eseguito.

L’ordine dei comandi cmd è importante, nel nostro caso attiviamo il virtual environment e poi, apriamo il file con l’eseguibile python desiderato (python o pythonw).

Fatto questo, selezioniamo l’interprete desiderato dal solito menu
Tools -> Build-systems, nel nostro caso
“Virtualenv 3.6” ed eseguiamo il codice con CTRL-B (o Tools -> Build).

Categorie:python Tag: ,

Django: favicon

13 aprile 2018 Commenti chiusi

Django favicon

Scaricare l’immagine django desiderata ad es.

e ridimensionarla a 16×16, rinominandola favicon.ico.
Posizionarla nella directory /static/ della propria app.

/path_to_django_site/app_name/static/favicon.ico

Nella template base.html, dalla quale ereditiamo tutte le altre, inserire nel tag il link alla favicon:

{% load staticfiles %}
...

{% block head %}
   ...
  <link rel="shortcut icon" href="{%  static 'favicon.ico' %}">
{% endblock %}

Ricordarsi assolutamente di caricare gli staticfiles come indicato con:

{% load staticfiles %}

Assicurarsi che nel file settings.py del sito sia definita la costante STATIC_URL:

STATIC_URL = '/static/'

Avviato il server, la favicon dovrebbe essere correttamente caricata e visualizzata senza così incorrere nell’errore

[...] "GET /favicon.ico HTTP/1.1" 404 -

Categorie:Django, python Tag: ,

pyodbc: Access primi passi

11 aprile 2018 Commenti chiusi

Installare pyodbc:

pip install pyodbc

Creare un database access

Ci serviremo di un utilissimo modulo chiamato msaccessdb l’utilizzo è molto semplice, importiamo per prima cosa il modulo msaccessdb:

>>> import msaccessdb

creaiamo un database:

>>> db_file = r'path_to_db\test.accdb'
>>> msaccessdb.create(db_file)

Il database è stato creato. Ora con pyodbc vediamo alcuni tra i comandi più comuni.

Creare una tabella su database access

Per prima cosa stabiliamo una connessione al database e creiamo il cursore che ci permetterà di lavorare con il database stesso:

>>> import pyodbc
>>> driver = r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=%s;'
>>> conn = pyodbc.connect(driver % db_file)
>>> cur = conn.cursor()
>>>

In questo momento siamo connessi al database e tramite cursor possiamo creare la tabella di prova “persone” con i campi “nome”, “cognome”, “anni”.

>>> sql = "CREATE TABLE persone(nome varchar(16), cognome varchar(32), anni integer)"
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> conn.commit()
>>>

controlliamo che la tabella sia stata creata e qui ci viene utile l’oggetto pyodbc.Cursor con i suoi metodi.
Per controllare le tabelle:

>>> for row in cur.tables():
...     print(row.table_name)
...
MSysACEs
MSysComplexColumns
MSysObjects
MSysQueries
MSysRelationships
persone
>>>

Per controllare le colonne:

>>> for row in cur.columns():
...     print(row.column_name)
...
nome
cognome
anni

Inserimento dati su database access

Ovviamente finchè la connessione al database è attiva, possiamo continuare con il cursore ad effettuare operazioni sul database. Inseriamo alcuni dati…

>>> sql = "INSERT INTO persone(nome, cognome, anni) VALUES ('Giuseppe', 'Rossi', 40)"
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> conn.commit()

vediamo se i dati sono stati inseriti:

>>> sql = "SELECT * FROM persone WHERE cognome LIKE '%{}%'".format('Rossi')
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>

l’oggetto cursore, tramite “description” ci rende disponibile la struttura dei dati trovati:

>>> cur.description
(('nome', <class 'str'>, None, 16, 16, 0, True), ('cognome', <class 'str'>, None, 32, 32, 0, True), ('anni', <class 

'int'>, None, 10, 10, 0, True))
>>>

con il metodo fetchone() dell’oggetto cursore, otteniamo la prima riga del queryset precedentemente creato:

>>> row = cur.fetchone()
>>> row
('Giuseppe', 'Rossi', 40)
>>> type(row)
<class 'pyodbc.Row'>

a differenza del metodo fetchall() che rende disponibili tutte le righe del query set.

>>> sql = "SELECT * FROM persone WHERE cognome LIKE '%{}%'".format('Rossi')
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> rows = cur.fetchall()
>>> for row in rows:
...     print(row.nome, row.cognome, row.anni)
...
Giuseppe Rossi 40
Franco Rossi 25

l’oggetto pyodbc.Row è simile ad una tupla ed è comodo poichè è possibile accedere ai dati mediante il nome della colonna:

>>> row
('Franco', 'Rossi', 25)
>>> row.nome
'Franco'

Eliminazione dati su database access

Per l’eliminazione di una riga dal database, il procedimento è simile a quelli visti fino ad ora:

>>> sql = "DELETE FROM persone WHERE nome LIKE '%{}%' AND cognome LIKE '%{}%'".format('Franco', 'Rossi')
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> conn.commit()
>>> sql = "SELECT * FROM persone WHERE cognome LIKE '%{}%'".format('Rossi')
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> rows = cur.fetchall()
>>> rows
[('Giuseppe', 'Rossi', 40)]
>>>

la riga precedente è stata correttamente eliminata.

Aggiornare dati su database access

>>> sql = "UPDATE persone SET nome='%s', anni='%s' WHERE cognome='%s'" % ('Giovanni', 18, 'Rossi')
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> conn.commit()
>>> sql = "SELECT * FROM persone WHERE cognome LIKE '%{}%'".format('Rossi')
>>> cur.execute(sql)
<pyodbc.Cursor object at 0x02091D20>
>>> row = cur.fetchone()
>>> row
('Giovanni', 'Rossi', 18)

Questi sono solo alcuni dei comandi più semplici per giocare con un database Access,
per andare nello specifico consultare il sito del progetto pyodbc.

Categorie:python Tag: ,

wxpython: statusbar + gauge

22 novembre 2017 Commenti chiusi
import wx
import time


class View(wx.Frame):
    def __init__(self, parent):
        self.parent = parent
        super(View, self).__init__(parent=self.parent)
        self.panel = wx.Panel(parent=self)
        self.SetSize((350, 100))
        self.status_bar = self.CreateStatusBar(2)
        self.status_bar.SetStatusText('0/0')
        self.status_bar.SetStatusWidths([100, -1])
        gauge_pos, gauge_size = self.get_gauge_dimensions()
        self.gauge = wx.Gauge (self.status_bar, -1, 100, gauge_pos, gauge_size)
        # bindings
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Show()

    def get_gauge_dimensions(self):
        """Get gauge position and dimensions"""
        pos_x, pos_y, dim_x, dim_y = self.status_bar.GetFieldRect(1)
        return (pos_x, pos_y), (dim_x, dim_y)
       
    def on_quit(self, event):
        self.Destroy()

    def on_size(self, event):
        """Resize gauge when the main frame is resized"""
        size = self.GetSize()
        self.SetSize(size)
        gauge_pos, gauge_size = self.get_gauge_dimensions()
        self.gauge.SetSize(gauge_size)
        event.Skip ()       
        self.Update()

    def set_range(self, value):
        """Set the range of gauge"""
        self.gauge.SetRange(value)

    def get_range(self):
        """Get the range of gauge"""
        return self.gauge.GetRange()

    def set_progress(self, value):
        """Set the indicator gauges progress"""
        self.gauge.SetValue(value)

    def set_status_text(self, value):
        """Set the indicator text gauges progress"""
        self.status_bar.SetStatusText(value)


if __name__ == "__main__":
    app = wx.App(False)
    tot = 300  # maximum value for gauge
    v = View(parent=None)
    v.set_range(tot)
    for n in range(1, tot + 1):
        v.set_progress(n)
        time.sleep(.001)
        v.set_status_text('%s/%s' % (n, tot))
    v.set_progress(0)
    app.MainLoop()

Categorie:python, wxPython Tag: ,

FantaStat 2.0

22 novembre 2017 Commenti chiusi

Introduzione

Fstat 2.0 è una semplice applicazione per visualizzare le statistiche
dei giocatori di fantacalcio.
La precedente versione serviva a visualizzare i file.txt veccio formato, quelli che indicavano anche i goal segnati, gli assist ecc.
I file più recenti indicano solo il fanta voto, voto senza bonus malus e la quotazione:

code|NAME|TEAM|fanta voto|voto|quotazione

Link utili

Il codice dell’applicazione è reperibile nel repo corrispondente di github.
I binari win con database players.db qui.
I file txt delle giornate di fantacampionato sono reperibili sul blog.

Moduli utilizzati
Python 2.7.8
grafica: wx 3.0.2.0
ORM: Django 1.10.1

L’utilizzo è molto semplice e di seguito viene descritto:

Creazione database

Qualora ci si dimenticasse di creare il database per primo, al lancio dell’applicazione si verrà avvisati da un messaggio.

Ogni baco segnalato sarà ben gradito.

Creare quindi il database con il seguente comando:

python manage.py makemigrations players

Le migrations iniziali dovrebbero già essere presenti nel repo di github, quindi potrebbero non risultare modifiche, procedere quindi con il secondo comando:

python manage.py migrate

Avviare Fstat2.0

Per avviare l’applicazione eseguire lo script main.py con il comando:

python main.py

Non avendo ancora importato nessun giocatore, verremo avvisati da un messaggio che è necessario importare il primo file:

Le prime giornata disputate sono già fornite con il repo in posizione players/days, oppure sono sempre reperibili qui.
Dal menu Import-> Import players importare il file desiderato, solitamente la giornata 1 (MCC1.txt)

Il file dei giocatori va importato solo la prima volta, dopodichè il menu non sarà più attivo.
Da qui in avanti si importeranno solo i file dei voti.
Il primo sarà nuovamente MCC1.txt. Dal menu Import -> Import Evaluations, procedere con l’importazione.

Finita un’importazione eseguire un refresh dall’apposito pulsante.
L’interfaccia si presenta come in figura:

I radio-buttons permettono di filtrare i giocatori per ruolo, mentre cliccando sulle colonne della lista,
l’ordinamento avverrà in base alla colonna cliccata. Ad esempio cliccando su real_team, i giocatori
saranno ordinati per ruolo e per squadra di appartenenza.

Cliccando invece sulla “riga” di un determinato giocatore, sarà possibile modificarlo.

Dal menu Player -> Summary si accede alla lista riassuntiva del giocatori, poichè la visualizzazione della schermata del frame principale, mostra le medie dei giocatori e non i valori iniziali.
Da qui cliccando sui giocatori si possono modificare come in precedenza.

Qualora nei file non risultasse un giocatore, sarà possibile crearlo dal menu Players -> New Player

Stesso discorso vale per le valutazioni; esiste un sommario accessibile dal menu Evaluations -> Summary

dove cliccando sulla riga di una valutazione, sarà possibile modificarla

Come per i giocatori, sarà possibile aggiungere una valutazione ad hoc, dal menu
Evaluations -> New Evaluation

Come ultima operazione, è possibile cancellare tutti i dati, dal menu Reset.

Pyinstaller: django stand alone application spec file

20 novembre 2017 Commenti chiusi

Relativamente ad applicazioni django stand-alone, descritte in questo articolo, qualora si dovessero creare i binari per windows con pyinstaller, è utile usare questo scheletro di file spec.

# -*- mode: python -*-

block_cipher = None


a = Analysis(['main.py'],
             pathex=['<path-of-application>'],
             hiddenimports=['htmlentitydefs',
                            'HTMLParser',
                            'Cookie',
			    'django.template.defaulttags',
			    'django.template.loader_tags',
                            'django.middleware.common',
                            'django.core.handlers.wsgi',
			    'settings'],
             hookspath=None,
             runtime_hooks=None,
             excludes=None,
             cipher=block_cipher)

pyz = PYZ(a.pure,cipher=block_cipher)

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name='fstat.exe',
          debug=False,
          strip=None,
          upx=True,
          console=True )

attenzione al pathex, la directory dove dovrà risiedere il binario risultante.

Negli hidden imports, come si nota, deve comparire anche il file settings.py, file descritto nell’articolo relativo alla struttura delle applicazioni django stand-alone.

La creazione del binario si eseguirà con il comando:

pyinstaller --onefile <my-app>.spec

scrapy: spider

12 ottobre 2017 Commenti chiusi

Articolo precedente: primi passi con scrapy

Spiders

Gli Spiders sono classi che definiscono come un sito o gruppi di siti, debbano essere analizzati.
In queste classi si decide come sarà il crawling/parsing del sito o dei gruppi di siti e come
saranno strutturati i dati estratti da tali pagine web.

Il progetto è stato già creato precedentemente, si tratta ora di creare lo spider adatto al nostro scopo.
Esiste un direttorio nome-progetto/nome-progetto//spiders all’interno del quale creeremo un file nome-progetto_spider.py nel mio caso, nel direttorio ansa/ansa/spiders, creerò un file ‘ansa_spider.py‘ con all’interno il codice necessario per lo scraping/crawling del sito in oggetto.

Vediamo il ciclo di funzionamento dello spider.

1. Requests iniziali

Generazione delle Requests iniziali per “raschiare” (crawl) le prime URLs e
specifica della funzione di callback da invocare con il Response ottenuto da queste prime Requests.
Le prime requests da eseguire sono ottenute con la chiamata del metodo start_requests()
che di default genera Request per le URLs specificate in una lista, chiamando per ognuna di esse
la corrispondente funzione di callback parse().

import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        urls = ["http://www.ansa.it",]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        pass

“name” è il nome univoco con il quale scrapy troverà ed utilizzerà lo spider.

“allowed_domains” è la lista dei domini autorizzati ad essere analizzati dallo spider.
Se il middleware OffsiteMiddleware sarà abilitato, i domini non presenti in questa lista,
non verranno analizzati dallo spider.

“start_requests” è il metodo che ritorna la lista con le prime Requests da analizzare. Viene invocato da Scrapy
quando apre lo spider per iniziare l’analisi.

Callback parse()

Nella funzione di callback parse(), eseguiamo appunto il parsing del response (web page) e ritorniamo:
– dizionario con i dati estratti
– Item object
– Request objects, o un iterable di tali oggetti

Con sintassi xpath, il primo abbozzo di spider sarebbe:

# ansa_spider.py
import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        xf = '//article[@class="news"]/descendant::a/@href'
        for url in response.xpath(xf).extract():
             yield {"title": url}

mentre la stessa con il metodo css():

# ansa_spider.py
import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        for url in response.css('article.news a::attr(href)').extract():
             yield {"title": url}

Lanciamo lo spider e viediamo che succede:

scrapy crawl ansa

come si nota, lo spider estrapola tutti i link presenti nella web page, ritornando un dizionario per ogni
link trovato. Se vogliamo generare un file con questi dati:

scrapy crawl ansa -o ansa.json

Nota:

Al momento scrapy esegue solo l’append dei dati e non sovrascrive il file, se già esistente.
Ricordarsi di eliminarlo prima di procedere con l’estrazione dei dati!

volendo estrarre i dati dal file, entriamo nella shell:

scrapy shell
import json


with open('ansa.json') as f:
    data = json.loads(f.read())

d = {n: v.get('link') for n, v in enumerate(data, 1) if v.get('link') != "javascript:void(0);"}

Tutte queste Requests potranno essere parsate e scaricate a loro volta da scrapy, facendo riferimento ad
una ulteriore callback (anche la stessa parse() volendo).

import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        xf = '//article[@class="news"]/descendant::a/@href'
        for url in response.xpath(xf).extract():
            if url and url != "javascript:void(0);":
                yield response.follow(url, callback=self.parse_news)

    def parse_news(self, response):
        title = response.xpath('//title/text()').extract_first()
        x_body = '//div[@class="news-txt"]/descendant::p'
        body = ' '.join(([node.xpath('string()').extract()[0]
                          for node in response.xpath(x_body)]))
        yield {'title': title,
               'body': body,
               }

Ora possiamo accedere ad un dizionario che al posto dei singoli link, ha i testi degli articoli.
Ma se volessimo portarci dietro dal primo parse, all’interno delle pagine figlie qualche attributo tipo ad es.
il link alla pagina figlia? Questo ha senso ovviamento per parametri che non sono reperibili nelle pagine figlie,
ma che debbano essere “trasportati” da un parse all’altro.

In questo caso ci vengono in aiuto gli items.

Dentro al file ansa/items.py definiamo il nostro personale Item:

import scrapy


class AnsaItem(scrapy.Item):
    # define the fields for your item here like:
    link = scrapy.Field()

Con l’ausilio di quest’ultimo, possiamo modificare il codice dello spider come segue:

import scrapy
from ansa.items import AnsaItem
from scrapy.exceptions import NotSupported


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            print '[INFO] Parsing url %s...' % url
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        xf = '//article[@class="news"]/descendant::a[position()=1]/@href'
        for url in response.xpath(xf).extract():
            if url and url != "javascript:void(0);":
                short_url = '/.../%s' % url.split('/')[-1]
                print '[INFO] Parsing urls %s = %s' % (n, short_url)
                ansa_item = AnsaItem()
                # ansa_item assignments
                ansa_item['link'] = url
                yield response.follow(url, callback=self.parse_news,
                                      meta={'ansa_item': ansa_item})

    def parse_news(self, response):
        ansa_item = response.meta.get('ansa_item')
        link = ansa_item.get('link')
        try:
            title = response.xpath('//title/text()').extract_first()
            x_body = '//div[@class="news-txt"]/descendant::p'
            body = ' '.join(([node.xpath('string()').extract()[0]
                              for node in response.xpath(x_body)]))
            yield {'link': link.strip(),
                   'title': title.strip(),
                   'body': body.strip(),
                  }
        except NotSupported:
            print "[WARNING] Response %s not crawled" % response

Nell’Item, gli assegnamenti agli attributi e l’accesso ad essi si comportano
allo stesso modo dei dizionari.
Come si vede, nella prima callback parse(), viene creato l’item e gli si assegna alla chiave ‘link’ il
valore di url. Quando utilizzeremo il metodo follow dell’oggetto response, per analizzare le sottopagine,
richiameremo la callback parse_news() e passeremo al metodo follow(),
l’attributo meta, un dizionario con all’interno il nostro item.
Questo item, viene recuperato all’interno della callback parse_news() ed il valore della sua
chiave ‘link’, ritornato nel dizionario con yield, insieme agli altri valori.

Eliminiamo un eventuale file json preesistente e ricreiamo il tutto:


>>> import json
>>> with open('ansa.json') as f:
...     data = json.loads(f.read())
...
>>> news_dict = {n: value for n, value in enumerate(data, 1)}
>>> news_1 = news_dict.get(1)
>>> news_1.get('title')
u"Ultimatum..."
>>> news_1.get('link')
u'/sito/notizie/mondo/europa/...'

link utili:
scrapy: appunti
scrapy
xpath: appunti

Categorie:python, scrapy, xpath Tag: , ,

scrapy: appunti

12 ottobre 2017 Commenti chiusi

Installazione

Per prima cosa creare un virtual-environment.

virtualenv venv

Abilitare il virtual-environment:

venv/Script/activate

per windows, mentre per linux, nel mio specifico caso Ubuntu:

source venv/bin/activate

Prima di procedere con l’installazione dei pacchetti necessari tramite
“pip”, per ubuntu (>12.04) è necessario installare alcuni pacchetti necessari
alle successive compilazioni, quindi da terminale:

sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev

sempre da venv, se stiamo lavorando da window, installare il pacchetto pypiwin32:

pip install pypiwin32

ora, per entrambi i casi, è possibile installare Scrapy:

pip install scrapy

Ora per vedere che tutto funzioni, è sufficiente eseguire il comando:

scrapy shell
...
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x04108D90>
[s]   item       {}
[s]   settings   <scrapy.settings.Settings object at 0x04108E90>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser

per uscire:

>>>exit()

Progetto

Creiamo il primo progetto, ad esempio un estrattore di ultime notizie dell’ANSA:

scrapy startproject ansa

prima però di scrivere codice, prendiamo confidenza con i principali comandi.
Lanciamo la shell di scrapy dandole in pasto la pagina web che ci interessa:

scrapy shell "www.ANSA.it"
...
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x04035DB0>
[s]   item       {}
[s]   request    <GET http://www.ANSA.it>
[s]   response   <200 http://www.ANSA.it>
[s]   settings   <scrapy.settings.Settings object at 0x04035EF0>
[s]   spider     <DefaultSpider 'default' at 0x4302fb0>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
...

come si nota ci vengono messi a disposizione alcuni oggetti, tra i quali ‘response’, sul quale è
possibile effettuare query di ogni tipo. Si possono usare sia il metodo css() che il metodo xpath().
Ad es. voglio ottenere il titolo della pagina:

>>> response.css('title')
[<Selector xpath=u'descendant-or-self::title' data=u'<title>ANSA.it</title>'>]
>>> response.xpath('//title')
[<Selector xpath='//title' data=u'<title>ANSA.it</title>'>]

Nel caso di utilizzo del metodo css() è previsto l’utilizzo di una sintassi dedicata.
Il metodo css() comunque ritornerà sempre un oggetto selector che si rifarà alla sintassi
di Xpath:

>>> response.css('title')
[<Selector xpath=u'descendant-or-self::title' data=u'<title>ANSA.it</title>'>]
>>> response.css('title::text')
[<Selector xpath=u'descendant-or-self::title/text()' data=u'ANSA.it'>]
>>> response.css('title::text').extract_first()
u'ANSA.it'

Selector

Un selector altro non è che un’istanza della classe Selector, alla quale viene passata una stringa
o un oggetto TextResponse e, a seconda del dato in ingresso sceglie il tipo di parsing
(XML o HTML) da usare:

>>> from scrapy import Selector
>>> body = "<foo><bar>Some text</bar></foo>"
>>> selector = Selector(text=body)
>>> selector.xpath('//bar[text()]').extract()
[u'<bar>Some text</bar>']
>>> selector.xpath('//bar/text()').extract()
[u'Some text']

Xpath è il linguaggio che permette di selezionare i nodi in un documento XML e può essere utilizzato
anche per l’HTML.
come si nota, si passa una stringa html al selector e con il metodo extract(),
si ottiene una lista di testi che rispondono ai nostri criteri, o con extract_first()
si ottiene il primo della lista.
Attenzione ai path:
nel primo caso :’//bar’ troviamo il tag ed il testo contenuto da esso;
nel secondo caso: ‘//bar/text()’ troviamo solo il testo contenuto da quel tag!
Stesso principio per gli oggetti TextResponse:

>>> from scrapy.http import HtmlResponse
>>> resp = HtmlResponse(url='', body=body)
>>> Selector(response=resp).xpath('//bar[text()]').extract_first()
u'<bar>Some text</bar>'

>>> Selector(response=resp).xpath(‘//bar/text()’).extract_first()
u’Some text’

Xpath

Rispetto all’utilizzo dei Selector CSS, le espressioni Xpath sono più potenti
perchè oltre a navigare attraverso la struttura della pagina web, permettono di
osservare i contenuti dei nodi.
Qui è disponibile una serie di appunti su Xpath.

>>> response.xpath('//title[text()]').extract()
[u'<title>ANSA.it</title>']

Andiamo al sodo:
vogliamo ottenere una lista di tutti i selector che contengono articoli?
Per come è strutturata la pagina che stiamo analizzando, gli articoli sono racchiusi all’interno di tag “article”,
quindi:

>>> response.css('article').extract()
...
>>> response.xpath('//article').extract()
...

ovviamente per ottenere il primo della lista, basterebbe chiamare il metodo extract_first():

>>> response.css('article').extract_first()
...

Vediamo come ottenere i sunti delle singole notizie. Queste sono identificabili ad esempio dal tag

.
Quindi vogliamo filtrate tutti i tag

con attributo class=”pp-abs”:

>>> response.css('p.pp-abs').extract()
...

dove ‘p.pp-abs’ sta appunto per

Con la sintassi xpath (qui una breve guida sulla sintassi) sarebbe:

>>> response.xpath('//p[@class="pp-abs"]').extract()
...

Se volessimo solo i testi e non i tag contenitori:

>>> response.css('p.pp-abs::text').extract()
...
>>> response.xpath('//p[@class="pp-abs"]/text()').extract()
...

Oltre al sunto delle notizie, vogliamo ottenere i link delle stesse?
Per come è strutturata la pagina web (‘analizza sorgente pagina’ da browser…) potrei fare qualcosa del genere:

for r in response.css('article.news'):
    p = r.css('p::text').extract_first()
    href = r.css('a::attr(href)').extract_first()
    if p and href != "javascript:void(0);":
        print "%s\nlink: %s" % (p, href)

La condizione if è dovuta al fatto che non sempre all’interno del tag

è disponibile un un tag

e
quindi il metodo extract_first() ritornerebbe ‘None’ e inoltre molti href punterebbero al valore
“javascript:void(0);” che non vogliamo.

Con xpath invece:

for r in response.xpath('//article[@class="news"]'):
    p = r.xpath('descendant::p/text()').extract_first()
    href = r.xpath('descendant::a/@href').extract_first()
    if p and href != "javascript:void(0);":
        print "%s\nlink: %s" % (p, href)

Vediamo adesso come fare per memorizzare tutti i link delle notizie in anteprima ed aprirle tutte,
per memorizzarne il testo completo. La cosa si complica un po’, ma non troppo.
Per questo tipo di cosa utilizzeremo gli spiders.

link utili:
scrapy: spider
scrapy
xpath: appunti

Categorie:python, scrapy, xpath Tag: , ,

Fantamanager 2.2

20 settembre 2017 Commenti chiusi

Diversamente dalla versione precedente FantaLega 2.1 e dalla versione Light, qui è possibile gestire un campionato di n.squadre, a scontri diretti (con più di 8/10 squadre).
Il giocatore non potrà più appartenere a più squadre ma è prevista un’asta.

Opzioni:
Asta
Costruzione Squadre
Generazione calendario
Modifica Squadre
Consegna formazioni
Modifica Formazioni
Importazione Giocatori e Voti Gazzetta
Modifica Giocatori
Mercato tra squadre
Calcolo punteggio formazioni
Classifica Squadre

Grafica: WX
Django per l’ORM

Utilizzare virtualenv ed installare i requirements:

pip install -r requirements.txt

Creare il database:

python manage.py makemigrations fantalega
python manage.py migrate

lanciare l’applicazione con il comando:

python main.py

Il link di github:
repo su github

Fantalega: gestore Asta

1 settembre 2017 Commenti chiusi

Ieri è terminata ufficialmente la sessione di calciomercato.
Molti fanta-allenatori hanno rischiosamente già effettuato le loro aste,
molti altri (noi), NO.

Ecco un tool parecchio spartano che velocizzerà le annotazioni
squadra-giocatore durante l’asta.

Auction1.0 è un gestore di aste di fantacalcio scritto in python.
Per la grafica sono state utilizzate le librerie wx, mentre per la
gestione del database e l’ORM, è stato utilizzato django.

Requisiti: installare django e wxpython

Su github è presente una prima bozza di repo.

Per prima cosa creare i database con i due comandi:

python manage.py makemigrations auction
python manage.py migrate

dopo queste operazioni che creeranno il database con le relative tabelle, è
possibile avviare la app:

python main.py

per prima cosa importiamo i giocatori utilizzando i soliti file txt in
formato Gazzetta, reperibili qui:

E’ possibile importare i giocatori più e più volte in modo da aggiornare
la lista dei calciatori.
Importati i giocatori, è necessario creare le squadre.

Dopo aver creato le squadre, si comincia l’asta.

Si seleziona il giocatore per squadra reale di appartenenza,
si utilizzano i filtri di ruolo e si assegna un costo d’asta e la squadra
che si aggiudica il giocatore. Durante l’asta è possibile avere un resoconto
delle squadre, con budget rimanente e giocatori ancora da comprare

Dai menu Teams e Players è possibile editare le squadre e i giocatori
per apportare delle modifiche. E’ anche possibile aggiungere giocatori che,
in fase d’asta, non sono ancora stati ufficializzati da Gazzetta e quindi
non presenti nei file.

Terminata l’asta è possibile effettuare scambi tra fanta-allenatori con il menu
“Trades”.

Nota: la casella max trades del pannello edit-team indica le massime operazioni
di mercato concesse durante la fantalega, mutuate dall’applicazione completa.
Queste operazioni (Trades) in fase di asta, sono invece illimitate.

Come ultima operazione, è possibile esportare un file in formato csv (menu Auction).