Prima versione pseudo funzionante, ma ci sono ancora
Leonardo Robol [2009-10-26 22:14]
Prima versione pseudo funzionante, ma ci sono ancora
un sacco di variabili globali che fungono da parametri
diff --git a/spidy.py b/spidy.py
index 2b8feab..d24725b 100644
--- a/spidy.py
+++ b/spidy.py
@@ -6,12 +6,24 @@ import random
import urllib2
import mutex
import threading
-import re
+import re, time
default_page = "http://poisson.phc.unipi.it"
-def get_links(url):
- content = get_page(url)
+__author__ = "Leonardo Robol <leo@robol.it>"
+
+mtx_url_dict = mutex.mutex()
+
+N = 10000
+url_dict = {}
+url_counter = range(N)
+
+max_steps = 5
+
+def get_links(page):
+ """Restituisce una lista con i link
+ presenti nella pagina data, in forma canonica"""
+ content = get_page(page.url)
if(content == -1):
return -1
@@ -20,7 +32,7 @@ def get_links(url):
for link in links:
# Espando il link in modo da (speriamo!)
# garantire l'unicità
- ret.append(expand_url(url, link))
+ ret.append(expand_url(page.url, link))
return ret
@@ -35,7 +47,8 @@ def expand_url(parent, url):
"""
## Controllo che l'url non cominci con un punto
- ## nel qual caso cerchiamo di rimediare subito
+ ## nel qual caso cerchiamo di rimediare subito,
+ ## ma non cadiamo nel tranello di ignorare i ..
if url[0] == ".":
if len(url) == 1:
url = parent
@@ -43,7 +56,21 @@ def expand_url(parent, url):
else:
if(url[1] != "."):
url = url[1:]
-
+
+ ## Se all'inizio dell'url c'è uno slash non ci serve tutto
+ ## il parent, ma solo la prima parte
+ if url.startswith("/"):
+ parent = re.search(".+//[^/]*", parent).group(0)
+ else:
+ # in caso contrario dobbiamo assicurarci di troncare
+ # l'ultima parte dell'url dopo il /, a meno che non
+ # finisca senza estensione (in quel caso aggiungiamo un /)
+ if re.search("\.[^/]*$", parent):
+ parent = re.sub("[^/]*$", "", parent)
+ else:
+ parent += "/"
+
+
## Controlliamo prima di tutto se nell'url c'è un
## protocollo
@@ -53,6 +80,8 @@ def expand_url(parent, url):
return url
def get_page(url):
+ """Cerca di scaricare l'url dato e restituisce
+ -1 se non ce la fa, il contenuto altrimenti"""
try:
req = urllib2.urlopen(url)
except:
@@ -60,21 +89,170 @@ def get_page(url):
return req.read()
+class Page():
+ """Una pagina web. Questa classe, quando viene istanziata,
+ controlla se una pagina con quel nome è già presente (una
+ pagina è unica!) e se lo è restituisce lo stesso oggetto,
+ altrimenti ne crea uno nuovo con un nuovo ID"""
+
+ def __repr__(self):
+ return "<Page object: %s>" % self.url
+
+ def __init__(self, url=""):
+
+ if(url != ""):
+ mtx_url_dict.lock(self.__new_page, url)
+ mtx_url_dict.unlock()
+ else:
+ mtx_url_dict.lock(self.__get_page, 0)
+ mtx_url_dict.unlock()
+
+ def __get_page(self, num):
+
+ if(len(url_counter) == 0):
+ self.exhausted = True
+ return
+
+ page_found = False
+
+ while(not page_found):
+
+ for url in url_dict:
+ page = Page(url)
+ if not page.analyzed:
+ page_found = True
+ self.url = url
+ break
+
+ if not page_found:
+ time.sleep(1)
+
+
+
+ self.ID = page.ID
+ self.analyzed = page.analyzed
+ self.exhausted = False
+ url_dict[url].analyzed = True
+
+ def __new_page(self, url):
+ # Questo ci serve per tenere il
+ # conto di tutti gli url
+ global url_dict
+ global url_counter
+
+ self.exhausted = False
+ self.analyzed = False
+ self.url = url
+
+ if(url_dict.has_key(url)):
+ # Preservo i parametri che esistono già!
+ self.ID = url_dict[url].ID
+ self.analyzed = url_dict[url].analyzed
+
+ else:
+ try:
+ self.ID = url_counter.pop()
+ except IndexError:
+ self.exhausted = True
+
+
+ url_dict[url] = self
+ url_dict[url].links = []
+
+ def add_link(self, page):
+
+ if(page.exhausted):
+ return -1
+ print " => Adding link to %s" % page.url
+ mtx_url_dict.lock(self.__add_link, page.ID)
+ mtx_url_dict.unlock()
+ return 0
+
+ def __add_link(self, ID):
+ url_dict[self.url].links.append(ID)
+
+ def links(self):
+ return url_dict[self.url].links
+
+
+
+
class Crawler(threading.Thread):
+ """Partendo da startpage, segue tutti i link registrando
+ i vari collegamenti fra le pagine. Una volta raggiunto il
+ limite di pagine da controllare termina"""
def __init__(self, startpage=default_page):
threading.Thread.__init__(self)
- start_page = start_page
+ self.start_page = startpage
- def run():
+ def run(self):
+
+ step_counter = 0
# Capiamo che pagina ci serve
- page = start_page
+ page = Page(self.start_page)
+
+ while(not page.exhausted):
+
+ if(step_counter > max_steps):
+ page = Page(self.start_page)
+ step_counter = 0
+ else:
+ page = Page()
+ step_counter += 1
+
+ if page.exhausted:
+ break
+
+ # Come prima cosa devo fare il parsing dei
+ # link che ci sono nella pagina
+ # Diamo una mixata per simulare meglio
+ # il caso.. dato che tanto è probabile che
+ # alcuni link rimarranno non visitati!
+ links = get_links(page)
+ random.shuffle(links)
+
+ ## A questo punto io che mi occupo della pagina devo
+ ## aggiungere tutti i link alla pagina
+
+ if not links == -1:
+ for l in links:
+ lpage = Page(l)
+
+ if not lpage.exhausted:
+ page.add_link(lpage)
+ else:
+ break
+
- # Come prima cosa devo fare il parsing dei
- # link che ci sono nella pagina
+
- links = get_links(page)
+if __name__ == "__main__":
+
+ concurrency = 25
+
+ threads = []
+ for i in range(0, concurrency):
+ threads.append(Crawler())
+ threads[i].start()
+
+
+ for i in range(0, concurrency):
+ threads[i].join()
+
+ ## A questo punto mi devo preoccupare di salvare
+ ## la matrice in un formato soddisfacente
+
+ out = open("ji.txt", 'w')
+
+ for page in url_dict:
+ for link in url_dict[page].links:
+ out.write(page + "\t" + str(url_dict[page].ID) + "\t" + str(link) + "\n")
+
+
+
+
diff --git a/test/test_1.py b/test/test_1.py
index 238f68a..6a3aef9 100644
--- a/test/test_1.py
+++ b/test/test_1.py
@@ -2,6 +2,29 @@ import sys
sys.path.append("../")
import spidy
+def confirm(test):
+
+ print " => Test %s superato" % test
+
+
print " => Provo ad ottenere i link di www.robol.it"
-print spidy.get_links("http://poisson.phc.unipi.it/~robol")
+page = spidy.Page("http://www.robol.it")
+if len(spidy.get_links(page)) > 0:
+ confirm("links_1")
+
+print " => Provo ad effettuare il test sulla pagina"
+page = spidy.Page("http://www.robol.it")
+page.add_link("http://ciccio.org")
+page = spidy.Page("http://www.robol.it")
+page.exhausted
+if len(page.links()) == 1:
+ confirm("page_1")
+print " => Provo ad ottenere una pagina non analizzata"
+page = spidy.Page()
+page.exhausted
+print " => Ho ottenuto %s" % page.url
+page = spidy.Page()
+page.exhausted
+print " => Ho ottenuto %s" % page.url
+confirm("page_2")