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
Filename
spidy.py
test/test_1.py
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")
ViewGit