lolamontes69

searchengine.py for Collective Intelligence ch4

Jun 16th, 2013
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.25 KB | None | 0 0
  1. from pysqlite2 import dbapi2 as sqlite
  2. import urllib2
  3. from bs4 import *
  4. from urlparse import urljoin
  5. import re
  6. import nn
  7. mynet=nn.searchnet('nn.db')
  8.  
  9. # create a list of words to ignore
  10. ignorewords=set(['the','of','to','and','a','in','is','it'])
  11.  
  12. class crawler:
  13.  
  14.     # initialize with name of database
  15.     def __init__(self,dbname):
  16.         self.con=sqlite.connect(dbname)
  17.  
  18.     def __del__(self):
  19.         self.con.close()
  20.     def dbcommit(self):
  21.         self.con.commit()
  22.  
  23.     # auxiliary func for getting an entry id and adding it if not present
  24.     def getentryid(self,table,field,value,createnew=True):
  25.         cur=self.con.execute(
  26.         "select rowid from %s where %s='%s'" % (table,field,value))
  27.         res=cur.fetchone()
  28.         if res==None:
  29.             cur=self.con.execute(
  30.             "insert into %s (%s) values ('%s')" % (table,field,value))
  31.             return cur.lastrowid
  32.         else:
  33.             return res[0]
  34.  
  35.     # index an individual page
  36.     def addtoindex(self,url,soup):
  37.         if self.isindexed(url): return
  38.         print 'Indexing '+url
  39.  
  40.         # Get individual words
  41.         text=self.gettextonly(soup)
  42.         words=self.separatewords(text)
  43.  
  44.         # get url id
  45.         urlid=self.getentryid('urllist','url',url)
  46.  
  47.         # link each word to this url
  48.         for i in range(len(words)):
  49.             word=words[i]
  50.             if word in ignorewords: continue
  51.             wordid=self.getentryid('wordlist','word',word)
  52.             self.con.execute("insert into wordlocation(urlid,wordid,location) \
  53.                values (%d,%d,%d)" % (urlid,wordid,i))
  54.  
  55.  
  56.     # extract text (no tags) from html page
  57.     def gettextonly(self,soup):
  58.         v=soup.string
  59.         if v==None:
  60.             c=soup.contents
  61.             resulttext=''
  62.             for t in c:
  63.                 subtext=self.gettextonly(t)
  64.                 resulttext+=subtext+'\n'
  65.             return resulttext
  66.         else:
  67.             return v.strip()
  68.  
  69.     # Separate words by any non-whitespace character
  70.     def separatewords(self,text):
  71.         splitter=re.compile('\\W*')
  72.         return [s.lower() for s in splitter.split(text) if s!='']
  73.  
  74.     # return true if already indexed
  75.     def isindexed(self,url):
  76.         u=self.con.execute \
  77.         ("select rowid from urllist where url='%s'" % url).fetchone()
  78.         if u!=None:
  79.             # check if this is actually crawled
  80.             v=self.con.execute(
  81.             'select * from wordlocation where urlid=%d' % u[0]).fetchone()
  82.             if v!=None:
  83.                 return True
  84.         return False
  85.  
  86.     # add a link between two pages
  87.     def addlinkref(self,urlFrom,urlTo,linkText):
  88.         words=self.separatewords(linkText)
  89.         fromid=self.getentryid('urllist','url',urlFrom)
  90.         toid=self.getentryid('urllist','url',urlTo)
  91.         if fromid==toid: return
  92.         cur=self.con.execute("insert into link(fromid,toid) values (%d,%d)" % (fromid,toid))
  93.         linkid=cur.lastrowid
  94.         for word in words:
  95.             if word in ignorewords: continue
  96.             wordid=self.getentryid('wordlist','word',word)
  97.             self.con.execute("insert into linkwords(linkid,wordid) values (%d,%d)" % (linkid,wordid))
  98.  
  99.     # Starting with a list of pages, do a breadth first search
  100.     # to the given depth, indexing as we go
  101.     def crawl(self,pages,depth=2):
  102.         for i in range(depth):
  103.             newpages=set()
  104.             for page in pages:
  105.                 try:
  106.                     c=urllib2.urlopen(page)
  107.                 except:
  108.                     print 'Couldn\'t open %s' % page
  109.                     continue
  110.                 soup=BeautifulSoup(c.read())
  111.                 self.addtoindex(page,soup)
  112.  
  113.                 links=soup('a')
  114.                 for link in links:
  115.                     if ('href' in dict(link.attrs)):
  116.                         url=urljoin(page,link['href'])
  117.                         if url.find("'")!=-1: continue
  118.                         url=url.split('#')[0] #remove location position
  119.                         if url[0:4]=='http' and not self.isindexed(url):
  120.                             newpages.add(url)
  121.                         linkText=self.gettextonly(link)
  122.                         self.addlinkref(page,url,linkText)
  123.  
  124.                 self.dbcommit()
  125.             pages=newpages
  126.  
  127.     # create the database tables
  128.     def createindextables(self):
  129.         self.con.execute('create table urllist(url)')
  130.         self.con.execute('create table wordlist(word)')
  131.         self.con.execute('create table wordlocation(urlid,wordid,location)')
  132.         self.con.execute('create table link(fromid integer,toid integer)')
  133.         self.con.execute('create table linkwords(wordid,linkid)')
  134.         self.con.execute('create index wordidx on wordlist(word)')
  135.         self.con.execute('create index urlidx on urllist(url)')
  136.         self.con.execute('create index wordurlidx on wordlocation(wordid)')
  137.         self.con.execute('create index urltoidx on link(toid)')
  138.         self.con.execute('create index urlfromidx on link(fromid)')
  139.         self.dbcommit( )
  140.  
  141.     def calculatepagerank(self,iterations=20):
  142.         #  clear out the current PageRank tables
  143.         self.con.execute('drop table if exists pagerank')
  144.         self.con.execute('create table pagerank(urlid primary key,score)')
  145.  
  146.         # Initialize every url with a PageRank of 1
  147.         self.con.execute('insert into pagerank select rowid, 1.0 from urllist')
  148.         self.dbcommit()
  149.  
  150.         for i in range(iterations):
  151.             print "Iteration %d" % (i)
  152.             for (urlid,) in self.con.execute('select rowid from urllist'):
  153.                 pr=0.15
  154.  
  155.                 # Loop through all the pages that link to this one
  156.                 for (linker,) in self.con.execute('select distinct fromid from link where toid=%d' % urlid):
  157.                     # Get the PageRank of the linker
  158.                     linkingpr=self.con.execute('select score from pagerank where urlid=%d' % linker).fetchone()[0]
  159.  
  160.                     # Get the total number of links from the linker
  161.                     linkingcount=self.con.execute('select count(*) from link where fromid=%d' % linker).fetchone()[0]
  162.                     pr+=0.85*(linkingpr/linkingcount)
  163.                 self.con.execute('update pagerank set score=%f where urlid=%d' % (pr,urlid))
  164.             self.dbcommit()
  165.  
  166. class searcher:
  167.     def __init__(self,dbname):
  168.         self.con=sqlite.connect(dbname)
  169.  
  170.     def __del__(self):
  171.         self.con.close()
  172.  
  173.     def getmatchrows(self,q):
  174.         # strings to build the query
  175.         fieldlist='w0.urlid'
  176.         tablelist=''
  177.         clauselist=''
  178.         wordids=[]
  179.  
  180.         # split the words by spaces
  181.         words=q.split(' ')
  182.         tablenumber=0
  183.  
  184.         for word in words:
  185.             # get the word id
  186.             wordrow=self.con.execute("select rowid from wordlist where word='%s'" % word).fetchone()
  187.             if wordrow!=None:
  188.                 wordid=wordrow[0]
  189.                 wordids.append(wordid)
  190.                 if tablenumber>0:
  191.                     tablelist+=','
  192.                     clauselist+=' and '
  193.                     clauselist+='w%d.urlid=w%d.urlid and ' % (tablenumber-1,tablenumber)
  194.                 fieldlist+=',w%d.location' % tablenumber
  195.                 tablelist+='wordlocation w%d' % tablenumber
  196.                 clauselist+='w%d.wordid=%d' % (tablenumber,wordid)
  197.                 tablenumber+=1
  198.  
  199.         try:
  200.             # create the query string
  201.             fullquery='select %s from %s where %s' % (fieldlist,tablelist,clauselist)
  202.             cur=self.con.execute(fullquery)
  203.             rows=[row for row in cur]
  204.             if len(rows)==0: return "Nothing", "Nothing"
  205.             return rows,wordids
  206.            
  207.         except:
  208.             a = b = "Nothing"
  209.         return a,b
  210.  
  211.     def getscoredlist(self,rows,wordids):
  212.         totalscores=dict([(row[0],0) for row in rows])
  213.  
  214.         # This is where you'll late put the scoring functions
  215.         # Frequency scoring activated
  216.         weights=[(1.0,self.locationscore(rows)),(1.0,self.linktextscore(rows,wordids)),(1.0,self.pagerankscore(rows))]
  217.  
  218.         for (weight,scores) in weights:
  219.             for url in totalscores:
  220.                 totalscores[url]+=weight*scores[url]
  221.  
  222.         return totalscores
  223.  
  224.     def geturlname(self,id):
  225.         return self.con.execute("select url from urllist where rowid=%d" % id).fetchone()[0]
  226.  
  227.     def query(self,q):
  228.         rows,wordids=self.getmatchrows(q.lower())
  229.         if rows == "Nothing":
  230.             return ['%s Not in database' %q]
  231.         scores=self.getscoredlist(rows,wordids)
  232.         rankedscores=sorted([(score,url) for (url,score) in scores.items()],reverse=1)
  233.         for (score,urlid) in rankedscores[0:10]:
  234.             print '%f\t%s' % (score,self.geturlname(urlid))
  235.         return wordids,[r[1] for r in rankedscores[0:10]]
  236.  
  237.     def normalizescores(self,scores,smallIsBetter=0):
  238.         vsmall=0.00001 # Avoid division by zero errors
  239.         if smallIsBetter:
  240.             minscore=min(scores.values())
  241.             return dict([(u,float(minscore)/max(vsmall,l)) for (u,l) in scores.items()])
  242.         else:
  243.             maxscore=max(scores.values())
  244.             if maxscore==0: maxscore=vsmall
  245.             return dict([(u,float(c)/maxscore) for (u,c) in scores.items()])
  246.  
  247.     def frequencyscore(self,rows):
  248.         counts=dict([(row[0],0) for row in rows])
  249.         for row in rows: counts[row[0]]+=1
  250.         return self.normalizescores(counts)
  251.  
  252.  
  253.     def locationscore(self,rows):
  254.         locations=dict([(row[0],1000000) for row in rows])
  255.         for row in rows:
  256.             loc=sum(row[1:])
  257.             if loc<locations[row[0]]: locations[row[0]]=loc
  258.  
  259.         return self.normalizescores(locations,smallIsBetter=1)
  260.  
  261.     def distancescore(self,rows):
  262.         # If there's only one word, everyone wins!
  263.         if len(rows[0])<=2: return dict([(row[0],1.0) for row in rows])
  264.  
  265.         # Initialize the dictionary with large values
  266.         mindistance=dict([(row[0],1000000) for row in rows])
  267.  
  268.         for row in rows:
  269.             dist=sum([abs(row[i]-row[i-1]) for i in range(2, len(row))])
  270.             if dist<mindistance[row[0]]: mindistance[row[0]]=dist
  271.         return self.normalizescores(mindistance,smallIsBetter=1)
  272.  
  273.     def inboundlinkscore(self,rows):
  274.         uniqueurls=set([row[0] for row in rows])
  275.         inboundcount=dict([(u,self.con.execute('select count(*) from link where toid=%d' % u).fetchone()[0]) for u in uniqueurls])
  276.         return self.normalizescores(inboundcount)
  277.  
  278.     def pagelengthscore(self,rows,smallisbetter=0):
  279.         uniqueurls=set([row[0] for row in rows])
  280.         wordscount=dict([(u,self.con.execute('select count(*) from wordlocation where urlid=%d' % u).fetchone()[0]) for u in uniqueurls])
  281.         return self.normalizescores(wordscount,smallisbetter)
  282.  
  283.     def wordfreqscore(self,rows,wordids):
  284.         uniqueurls=set([row[0] for row in rows])
  285.         wordscount=dict([(u,self.con.execute('select count(*) from wordlocation where urlid=%d' % u).fetchone()[0]) for u in uniqueurls])
  286.         wordscount2 = {}
  287.         for word in wordids:
  288.             wordscount1=dict([(u,self.con.execute('select count(*) from wordlocation where urlid=%d and wordid=%d' % (u, word)).fetchone()[0]) for u in uniqueurls])
  289.             for url in wordscount1:
  290.                 if url not in wordscount2:
  291.                     wordscount2[url]=wordscount1[url]
  292.                 else:
  293.                     wordscount2[url] = wordscount2[url]+wordscount1[url]
  294.         for url in wordscount:
  295.             wordscount[url] = (wordscount2[url]/float(wordscount[url]))*100.0
  296.        
  297.         return self.normalizescores(wordscount)
  298.  
  299.     def pagerankscore(self,rows):
  300.         pageranks=dict([(row[0],self.con.execute('select score from pagerank where urlid=%d' % row[0]).fetchone()[0]) for row in rows])
  301.         maxrank=max(pageranks.values())
  302.         if maxrank <= 0: maxrank = 0.00001
  303.         normalizedscores=dict([(u,float(l)/maxrank) for (u,l) in pageranks.items()])
  304.         return normalizedscores
  305.    
  306.     def linktextscore(self,rows,wordids):
  307.         linkscores=dict([(row[0],0) for row in rows])
  308.         for wordid in wordids:
  309.             cur=self.con.execute('select link.fromid,link.toid from linkwords,link where wordid=%d and linkwords.linkid=link.rowid' % wordid)
  310.             for (fromid,toid) in cur:
  311.                 if toid in linkscores:
  312.                     pr=self.con.execute('select score from pagerank where urlid=%d' % fromid).fetchone()[0]
  313.                     linkscores[toid]+=pr
  314.         maxscore=max(linkscores.values())
  315.         if maxscore <= 0: maxscore = 0.00001
  316.         normalizedscores=dict([(u,float(l)/maxscore) for (u,l) in linkscores.items()])
  317.         return normalizedscores
  318.  
  319.  
  320.     def nnscore(self,rows,wordids):
  321.         # Get unique URL IDs as an ordered list
  322.         urlids=[urlid for urlid in set([row[0] for row in rows])]
  323.         nnres=mynet.getresult(wordids,urlids)
  324.         scores=dict([(urlids[i],nnres[i]) for i in range(len(urlids))])
  325.         return self.normalizescores(scores)
  326.  
  327. """ linktextscore() and pagerankscore() corrected for ZeroDivisionError's
  328.    'X not in database' response added to getmatchrows() and query()
  329.    q=q.lower() added to query() because of case sensitivity
  330.    pagelengthscore() and wordfreqscore() methods added
  331. """
Advertisement
Add Comment
Please, Sign In to add comment