Advertisement
Guest User

Untitled

a guest
Dec 31st, 2016
274
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.27 KB | None | 0 0
  1. class Indexer:
  2.  
  3.  def __init__(self):
  4.    
  5.   self.conn = psycopg2.connect("dbname='postgres' user='postgres' password='120789' host='localhost'")
  6.  
  7.   self.cur = self.conn.cursor()
  8.   self.cur.execute("set search_path to 'rambler_search'")
  9.  
  10.   self.grab = Grab()
  11.  
  12.  def __del__(self):
  13.   self.conn.close()
  14.  
  15.  def dbcommit(self):
  16.   self.conn.commit()
  17.  # Начинаем со списка страниц для индексации
  18.   # опускаемся в глубину на 2 уровня
  19.  def crawl(self, pages, depth=2):
  20.  
  21.   rambler_news_href_pattern = re.compile(r'(^http:\/\/news\.rambler\.ru\/[\d]+)')
  22.  
  23.   for i in range(depth):
  24.    newpages={}
  25.  
  26.    for page in pages:
  27.  
  28.     try:
  29.       self.grab.go(page)
  30.  
  31.     except:
  32.       print "Could not open %s" % page
  33.       continue
  34.  
  35.     try:
  36.       article_text = '' # текст статьи
  37.       for paragraph in self.grab.tree.xpath("//p[contains(@class, 'b-article__paragraph')]"):
  38.       article_text += paragraph.text_content()
  39.  
  40.       self.addtoindex(page, article_text) # записываем в базу текст статьи с индексацией
  41.  
  42.       links = self.grab.tree.xpath("//a")
  43.       for link in links:
  44.  
  45.      if ('href' in link.attrib):
  46.        url = urljoin(page, link.attrib['href']).split('#')[0]# делаем полную ссылку и удаляем часть за # если она есть
  47.  
  48.        match = rambler_news_href_pattern.findall(url)
  49.        if match:
  50.         url = match[0]
  51.  
  52.       if url[0:4] == 'http' and not self.isindexed(url):
  53.        newpages[url] = 1
  54.  
  55.       linkText = link.text_content() # текст ссылки
  56.       self.addlinkref(page, url, linkText) # записываем в базу текст ссылки с индексацией
  57.  
  58.       self.dbcommit()
  59.  
  60.     except Exception, e:
  61.       print "Could not parse page %s" % page, e
  62.  
  63.    pages = newpages
  64. def getwords(html):
  65.     words = []
  66.     for split_str in html.split():
  67.         t = re.split("[\s;:\-_*\".,?!()'&#«»]", split_str)
  68.         words += [a.lower() for a in t if a != '' and len(a) > 3]
  69.     return words
  70.         def addtoindex(self, url, text):
  71.  
  72.   if self.isindexed(url): return # если уже индексирована - пропускаем
  73.   print 'Indexing %s' % url
  74.  
  75.   # Получаем слова из текста
  76.   words = getwords(text)
  77.  
  78.   # Получаем id url'а
  79.   url_id = self.getentryid('url_list', 'url', url)
  80.  
  81.   # Связываем слова с этим урлом
  82.   for i, word in enumerate(words): # пронумеруем слова
  83.     word_id = self.getentryid('word_list', 'word', word)
  84.     self.cur.execute("insert into word_location(url_id, word_id, location) values (%d, %d, %d)" % (url_id, word_id, i))
  85.     self.dbcommit()
  86.         # Узнаем id записи в БД, если нет
  87.         # иначе записываем и возвращаем новый
  88.  def getentryid(self, table, field, value, createnew=True):
  89.  
  90.   self.cur.execute("select id from %s where %s = '%s'" % (table, field, value))
  91.   cur = self.cur.fetchone()
  92.  
  93.   if not cur:
  94.    # print (table, field, value)
  95.    self.cur.execute("insert into %s (%s) values ('%s') returning %s.id" % (table, field, value, table))
  96.    cur = self.cur.fetchone()
  97.    self.dbcommit()
  98.  
  99.    return cur[0]
  100.  
  101.   else:
  102.     return cur[0]
  103.  
  104.        # Возвращаем True, если посещали страницу
  105.  def isindexed(self, url):
  106.   self.cur.execute("select id from url_list where url = '%s'" % url)
  107.   u = self.cur.fetchone()
  108.  
  109.   if u:
  110.    # Проверяем, что паук посещал страницу
  111.    self.cur.execute("select count(1) from word_location where url_id = %d" % u[0])
  112.    v = self.cur.fetchone()
  113.  
  114.    if v[0]:
  115.     return True
  116.  
  117.   return False
  118.  def addlinkref(self, urlFrom, urlTo, linkText):
  119.  
  120.   words = getwords(linkText)
  121.   from_id = self.getentryid('url_list', 'url', urlFrom)
  122.   to_id = self.getentryid('url_list', 'url', urlTo)
  123.  
  124.   if from_id == to_id: return
  125.  
  126.   self.cur.execute("insert into link(from_id, to_id) values (%d, %d) returning link.id" % (from_id, to_id))
  127.   link_id = self.cur.fetchone()[0]
  128.  
  129.   for word in words:
  130.  
  131.     word_id = self.getentryid('word_list', 'word', word)
  132.     self.cur.execute("insert into link_words(link_id, word_id) values (%d, %d)" % (link_id, word_id))
  133.  def get_match_rows(self, query):
  134.  
  135.   select_query_add = []
  136.   join_query_add = []
  137.   where_query_add = []
  138.  
  139.   main_search_query = """
  140.     SELECT wl0.url_id, %s
  141.     FROM word_location wl0
  142.     %s
  143.     WHERE %s
  144.    """
  145.  
  146.   query_words = getwords(query)
  147.  
  148.   query_word_ids = []
  149.   for query_word in query_words:
  150.  
  151.    self.cur.execute("select id from word_list where word = '%s'" % query_word)
  152.    query_word_id = self.cur.fetchone()
  153.  
  154.    if query_word_id:
  155.     query_word_ids.append(query_word_id[0])
  156.  
  157.   if query_word_ids:
  158.    for position, query_word_id in enumerate(query_word_ids):
  159.  
  160.     if position:
  161.      join_query_add.append('JOIN word_location wl%d ON wl%d.url_id = wl%d.url_id' % (position, position-1, position))
  162.  
  163.     select_query_add.append('wl%d.location' % position)
  164.     where_query_add.append('wl%d.word_id = %d' % (position, query_word_id))
  165.  
  166.    main_search_query = main_search_query % (', '.join(select_query_add), ' '.join(join_query_add), ' and '.join(where_query_add))
  167.  
  168.    self.cur.execute(main_search_query)
  169.    search_results = self.cur.fetchall()
  170.    
  171.    return search_results, query_word_ids
  172.  # Функция, которая взвешивает результаты
  173.  def get_scored_list(self, rows, word_ids):
  174.  
  175.   total_scores = {row[0]: 0 for row in rows}
  176.  
  177.   if rows:
  178.    # Список весовых функций
  179.    weight_functions = [
  180.       ]
  181.  
  182.    for (weight, scores) in weight_functions:
  183.      for url in total_scores:
  184.     total_scores[url] += weight * scores[url]
  185.  
  186.   return total_scores
  187.  
  188.  # Возвращает полный урл по id
  189.  def get_url_name(self, url_id):
  190.  
  191.   self.cur.execute("select url from url_list where id = %d" % url_id)
  192.  
  193.   return self.cur.fetchone()[0]
  194.  
  195.  # Основная функция поиска
  196.  def search(self, search_sentence):
  197.  
  198.   search_results, word_ids = self.get_match_rows(search_sentence)
  199.   scores = self.get_scored_list(search_results, word_ids)
  200.  
  201.   ranked_scores = [(score, url) for (url, score) in scores.items()]
  202.   ranked_scores.sort()
  203.   ranked_scores.reverse()
  204.  
  205.   for (score, url_id) in ranked_scores[0:10]:
  206.     print '%f\t%s' % (score, self.get_url_name(url_id))
  207.  
  208.   return word_ids, [r[1] for r in ranked_scores[0:10]]
  209.  def normalize_scores(self, scores, smallIsBetter=0):
  210.  
  211.   vsmall = 0.00001 # Avoid division by zero errors
  212.  
  213.   if smallIsBetter:
  214.    minscore = min(scores.values())
  215.    return {u: float(minscore)/max(vsmall, l) for (u,l) in scores.items()}
  216.  
  217.   else:
  218.    maxscore = max(scores.values())
  219.    if maxscore == 0: maxscore = vsmall
  220.    return {u: float(c)/maxscore for (u,c) in scores.items()}
  221.  
  222.   return scores
  223. def frequency_score(self, rows):
  224.  
  225.   counts = {row[0]:0 for row in rows}
  226.   for row in rows:
  227.    counts[row[0]] += 1
  228.  
  229.   return self.normalize_scores(counts)
  230. def location_score(self, rows):
  231.  
  232.   locations = {}
  233.   for row in rows:
  234.    loc = sum(row[1:])
  235.    if locations.has_key(row[0]):
  236.     if loc < locations[row[0]]:
  237.      locations[row[0]] = loc
  238.    else:
  239.     locations[row[0]] = loc
  240.  
  241.   return self.normalizescores(locations, smallIsBetter=1)
  242. def distance_score(self, rows):
  243.  
  244.   mindistance = {}
  245.  
  246.   # Если только 1 слово, любой документ выигрывает
  247.   if len(rows[0]) <= 2:
  248.    return {row[0]: 1.0 for row in rows}
  249.  
  250.   mindistance = {}
  251.  
  252.   for row in rows:
  253.    dist = sum([abs(row[i]-row[i-1]) for i in xrange(2, len(row))])
  254.  
  255.    if mindistance.has_key(row[0]):
  256.     if dist < mindistance[row[0]]:
  257.      mindistance[row[0]] = dist
  258.    else:
  259.     mindistance[row[0]] = dist
  260.  
  261.   return self.normalize_scores(mindistance, smallIsBetter=1)
  262. def inbound_link_score(self, rows):
  263.  
  264.   unique_urls = {row[0]: 1 for row in rows}
  265.   inbound_count = {}
  266.  
  267.   for url_id in unique_urls:
  268.    self.cur.execute('select count(*) from link where to_id = %d' % url_id)
  269.    inbound_count[url_id] = self.cur.fetchone()[0]
  270.  
  271.   return self.normalize_scores(inbound_count)
  272.  # Вычисляем PageRank страниц
  273.  def calculate_page_rank(self, iterations=20):
  274.  
  275.   START_PR = 1
  276.   self.cur.execute('update url_list set page_rank = %d' % START_PR)
  277.   self.db_commit()
  278.  
  279.   for i in range(iterations):
  280.    print "Iteration %d" % (i)
  281.    self.cur.execute('select id from url_list')
  282.    for url_id,  in self.cur.fetchall():
  283.  
  284.     # Конечный PR зависит от START_PR и этого базового pr
  285.     pr = 0.15
  286.  
  287.     self.cur.execute('select distinct from_id from link where to_id = %d' % url_id)
  288.     # Перебираем все страницы, ссылающиеся на данную
  289.     for linker, in self.cur.fetchall():
  290.  
  291.      # Получаем PR ссылающейся страницы
  292.      self.cur.execute('select page_rank from url_list where id = %d' % linker)
  293.      linking_pr = self.cur.fetchone()[0]
  294.  
  295.      # Получаем общее количество ссылок на ссылающейся странице
  296.      self.cur.execute('select count(*) from link where from_id = %d' % linker)
  297.      linking_count = self.cur.fetchone()[0]
  298.      pr += 0.85 * (linking_pr/linking_count)
  299.  
  300.      self.cur.execute('update url_list set page_rank = %f where id = %d' % (pr, url_id))
  301.     self.db_commit()
  302. def page_rank_score(self, rows):
  303.  
  304.   page_ranks = {row[0]: 0 for row in rows}
  305.  
  306.   for row in rows:
  307.    self.cur.execute('select page_rank from url_list where id = %d' % row[0])
  308.    page_ranks[row[0]] = self.cur.fetchone()[0]
  309.  
  310.   return self.normalize_scores(page_ranks)
  311.  def link_text_score(self, rows, word_ids):
  312.  
  313.   link_scores = {row[0]: 0 for row in rows}
  314.  
  315.   word_ids_string = ','.join(map(str, word_ids))
  316.   url_ids_string =  ','.join(set([str(row[0]) for row in rows]))
  317.  
  318.   self.cur.execute(''' select sum(u_l.page_rank), l.to_id
  319.        from link l
  320.        join link_words l_w on l.id = l_w.link_id
  321.        join url_list u_l on u_l.id = l.from_id
  322.        where word_id IN (%s) and l.to_id IN (%s)
  323.        group by l.to_id''' % (word_ids_string, url_ids_string))
  324.  
  325.   for sum_pr_url_from, url_id in self.cur.fetchall():
  326.    link_scores['url_id'] = sum_pr_url_from
  327.  
  328.   return self.normalize_scores(link_scores
  329.  def set_strength(self, from_id, to_id, table, strength):
  330.  
  331.   self.cur.execute('select id from %s where from_id = %d and to_id = %d' % (table, from_id, to_id))
  332.   res = self.cur.fetchone()
  333.  
  334.   if res == None:
  335.    self.cur.execute('insert into %s (from_id, to_id, strength) values (%d, %d, %f)' % (table, from_id, to_id, strength))
  336.   else:
  337.    row_id = res[0]
  338.    self.cur.execute('update %s set strength = %f where id = %d' % (table, strength, row_id))
  339.  
  340.   self.db_commit()
  341.  def generate_hidden_node(self, word_ids, urls):
  342.  
  343.                 # Для простоты ограничимся 2-х словными фразами
  344.   if len(word_ids) > 3:
  345.    return None
  346.  
  347.   # Проверить, создавали ли мы уже узел для данного набора слов
  348.   sorted_words = map(str, word_ids)
  349.   sorted_words.sort()
  350.   create_key = '_'.join(sorted_words)
  351.   self.cur.execute("select count(id) from hidden_node where create_key = '%s'" % create_key)
  352.   count_hidden_id = self.cur.fetchone()
  353.  
  354.   # Если нет, создадим сейчас
  355.   if not count_hidden_id:
  356.    self.cur.execute("insert into hidden_node (create_key) values ('%s')  returning hidden_node.id" % create_key)
  357.    hidden_id = self.cur.fetchone()[0]
  358.  
  359.    # Задать веса по умолчанию
  360.    for word_id in word_ids:
  361.     self.set_strength(word_id, hidden_id, 'word_hidden', 1.0/len(word_ids))
  362.  
  363.    for url_id in urls:
  364.     self.set_strength(hidden_id, url_id, 'hidden_url', 0.1)
  365.  
  366.    self.db_commit()
  367.  def get_all_hidden_ids(self, word_ids, url_ids):
  368.  
  369.   l1 = {}
  370.   for word_id in word_ids:
  371.  
  372.    self.cur.execute('select to_id from word_hidden where from_id = %d' % word_id)
  373.    cur = self.cur.fetchall()
  374.  
  375.    for row in cur:
  376.     l1[row[0]] = 1
  377.  
  378.   for url_id in url_ids:
  379.  
  380.    cur = self.cur.execute('select from_id from hidden_url where to_id = %d' % url_id)
  381.    cur = self.cur.fetchall()
  382.    
  383.    for row in cur:
  384.  
  385.     l1[row[0]] = 1
  386.  
  387.   return l1.keys()
  388. def setup_network(self, word_ids, url_ids):
  389.  
  390.   # списки значений
  391.   self.word_ids = word_ids
  392.   self.hidden_ids = self.get_all_hidden_ids(word_ids, url_ids)
  393.   self.url_ids = url_ids
  394.  
  395.   # выходные сигналы узлов
  396.   self.hidden_word_output = [1.0]*len(self.word_ids)
  397.   self.hidden_layer_output = [1.0]*len(self.hidden_ids)
  398.   self.hidden_url_output = [1.0]*len(self.url_ids)
  399.  
  400.   # создаем матрицу весов
  401.   self.word_layer_weights = [
  402.      [self.get_strength(word_id, hidden_id, 0) for hidden_id in self.hidden_ids] for word_id in self.word_ids
  403.        ]
  404.  
  405.   self.layer_url_weights = [
  406.      [self.get_strength(hidden_id, url_id, 1) for url_id in self.url_ids] for hidden_id in self.hidden_ids
  407.      ]
  408. def feed_forward(self):
  409.  
  410.   # единственные входные сигналы – слова из запроса
  411.   for i in xrange(len(self.word_ids)):
  412.    self.hidden_word_output[i] = 1.0
  413.  
  414.   # возбуждение скрытых узлов
  415.   for j in xrange(len(self.hidden_ids)):
  416.    sum = 0.0
  417.    for i in xrange(len(self.word_ids)):
  418.     sum = sum + self.hidden_word_output[i] * self.word_layer_weights[i][j]
  419.    self.hidden_layer_output[j] = tanh(sum)
  420.  
  421.   # возбуждение выходных узлов
  422.   for k in xrange(len(self.url_ids)):
  423.    sum = 0.0
  424.    for j in range(len(self.hidden_ids)):
  425.     sum = sum + self.hidden_layer_output[j] * self.layer_url_weights[j][k]
  426.    self.hidden_url_output[k] = tanh(sum)
  427.  
  428.   return self.hidden_url_output[:]
  429. def get_result(self, word_ids, url_ids):
  430.  
  431.   self.setup_network(word_ids, url_ids)
  432.   return self.feed_forward()
  433.  def back_propagate(self, targets, N=0.5):
  434.  
  435.   # вычислить поправки для выходного слоя
  436.   output_deltas = [0.0] * len(self.urlids)
  437.   for k in xrange(len(self.urlids)):
  438.    error = targets[k] - self.hidden_url_output[k]
  439.    output_deltas[k] = dtanh(self.hidden_url_output[k]) * error
  440.  
  441.   # вычислить поправки для скрытого слоя
  442.   hidden_deltas = [0.0] * len(self.hiddenids)
  443.   for j in xrange(len(self.hiddenids)):
  444.    error = 0.0
  445.    for k in xrange(len(self.urlids)):
  446.     error = error + output_deltas[k]*self.layer_url_weights[j][k]
  447.    hidden_deltas[j] = dtanh(self.hidden_layer_output[j]) * error
  448.  
  449.   # обновить веса связеи между узлами скрытого и выходного слоя
  450.   for j in xrange(len(self.hiddenids)):
  451.    for k in xrange(len(self.urlids)):
  452.     change = output_deltas[k]*self.hidden_layer_output[j]
  453.     self.layer_url_weights[j][k] = self.layer_url_weights[j][k] + N*change
  454.  
  455.   # обновить веса связей между узлами входного и скрытого слоя
  456.   for i in xrange(len(self.wordids)):
  457.    for j in xrange(len(self.hiddenids)):
  458.     change = hidden_deltas[j]*self.hidden_word_output[i]
  459.     self.word_layer_weights[i][j] = self.word_layer_weights[i][j] + N*change
  460. def train_query(self, word_ids, url_ids, selected_url_id):
  461.  
  462.   # сгенерировать скрытыи узел, если необходимо
  463.   self.generate_hidden_node(word_ids, url_ids)
  464.  
  465.   self.setup_network(word_ids, url_ids)      
  466.   self.feed_forward()
  467.   targets = [0.0]*len(urlids)
  468.   targets[urlids.index(selected_url_id)] = 1.0
  469.   error = self.back_propagate(targets)
  470.   self.update_database()
  471. def update_database(self):
  472.  
  473.   for i in xrange(len(self.word_ids)):
  474.    for j in xrange(len(self.hidden_ids)):
  475.     self.set_strength(self.word_ids[i], self. hidden_ids[j], 0, self.word_layer_weights[i][j])
  476.  
  477.   for j in xrange(len(self.hidden_ids)):
  478.    for k in xrange(len(self.url_ids)):
  479.     self.set_strength(self.hidden_ids[j], self.url_ids[k], 1, self.layer_url_weights[j][k])
  480. def neiron_net_score(self, rows, word_ids):
  481.  
  482.   # Get unique URL IDs as an ordered list
  483.   url_ids = [row[0] for row in rows]
  484.  
  485.   nn_res = self.neiron_net.get_result(word_ids, url_ids)
  486.   scores = {url_ids[i]: nn_res[i] for i in xrange(len(url_ids))}
  487.   return self.normalize_scores(scores)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement