티스토리 뷰

이제 검색엔진을 만들어보고 한 페이지의 페이지랭크에 대해 해보겠습니다!

페이지랭크에 대한 알고리즘을 실행하고 웹 브라우저에 시각화를 하는 것이죠!

 

페이지랭크 알고리즘의 특징은 어떤 페이지가 가장 최고의 링크를 가지는지 알아내는 것입니다.이

 

일단 먼저 프로세스를 보겠습니다.

페이지 랭크 전체 프로세스

spider.py로 하나의 URL을 DB에 집어넣습니다. 그다음 검색되지 않은 페이지를 찾습니다. 또 고른 페이지를 검색하고 해당 페이지를 파싱하게 됩니다. 그리고 검색되지 않은 페이지들을 DB에 저장하게 되는 것이죠.

 

페이지랭크 알고리즘은 옛날 순위를 통해 새로운 순위를 계산하고 옛날 순위를 새로운 순위로 교체합니다.

그래서 다대다 테이블을 생성해 페이지를 가리키는 용도로 사용합니다.

이번 포스팅에서는 spider.py의 역할과 코드를 보겠습니다.

 

먼저 페이지 랭크 - 스파이더링에 대해 살펴보겠습니다.

여기서는 Beautiful Soup 코드로 파싱을 한다고 하네요!

http://www.py4e.com/code3/pagerank.zip < 여기서 다운받으시면 bs4.zip 파일에 Beautiful Soup 코드가 들어가 있네요.

 

spider.py 코드입니다.

import sqlite3
import urllib.error
import ssl
from urllib.parse import urljoin
from urllib.parse import urlparse
from urllib.request import urlopen
from bs4 import BeautifulSoup

# Ignore SSL certificate errors
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

conn = sqlite3.connect('spider.sqlite')
cur = conn.cursor()

cur.execute('''CREATE TABLE IF NOT EXISTS Pages
    (id INTEGER PRIMARY KEY, url TEXT UNIQUE, html TEXT,
     error INTEGER, old_rank REAL, new_rank REAL)''')

cur.execute('''CREATE TABLE IF NOT EXISTS Links
    (from_id INTEGER, to_id INTEGER)''')

cur.execute('''CREATE TABLE IF NOT EXISTS Webs (url TEXT UNIQUE)''')

# Check to see if we are already in progress...
cur.execute('SELECT id,url FROM Pages WHERE html is NULL and error is NULL ORDER BY RANDOM() LIMIT 1')
row = cur.fetchone()
if row is not None:
    print("Restarting existing crawl.  Remove spider.sqlite to start a fresh crawl.")
else :
    starturl = input('Enter web url or enter: ')
    if ( len(starturl) < 1 ) : starturl = 'http://www.dr-chuck.com/'
    if ( starturl.endswith('/') ) : starturl = starturl[:-1]
    web = starturl
    if ( starturl.endswith('.htm') or starturl.endswith('.html') ) :
        pos = starturl.rfind('/')
        web = starturl[:pos]

    if ( len(web) > 1 ) :
        cur.execute('INSERT OR IGNORE INTO Webs (url) VALUES ( ? )', ( web, ) )
        cur.execute('INSERT OR IGNORE INTO Pages (url, html, new_rank) VALUES ( ?, NULL, 1.0 )', ( starturl, ) )
        conn.commit()

# Get the current webs
#우리가 처음에 연결하고자 하는 랭크에만 링크를 연결합니다.
cur.execute('''SELECT url FROM Webs''')
webs = list()
for row in cur:
    webs.append(str(row[0]))

#우리가 갈 수 있는 적법한 곳이 몇 개인지 나타냅니다.
print(webs)

many = 0
while True:
    #몇 개의 페이지를 돌 것인지 물어봅니다.
    if ( many < 1 ) :
        sval = input('How many pages:')
        if ( len(sval) < 1 ) : break
        many = int(sval)
    many = many - 1

    cur.execute('SELECT id,url FROM Pages WHERE html is NULL and error is NULL ORDER BY RANDOM() LIMIT 1')
    try:
        row = cur.fetchone()
        # print row
        # 현재 가리키고 있는 페이지의 fromid와 url을 얻어옵니다.
        fromid = row[0]
        url = row[1]
    except:
        print('No unretrieved HTML pages found')
        many = 0
        break

    print(fromid, url, end=' ')

    # If we are retrieving this page, there should be no links from it
    # 모든 링크를 없앱니다. 왜냐하면 이 링크들은 검색되지 않았으니까요!
    # Links는 페이지 간의 관계를 표현합니다.
    cur.execute('DELETE from Links WHERE from_id=?', (fromid, ) )
    try:
        #url을 열어 읽습니다.
        document = urlopen(url, context=ctx)

        html = document.read()
        if document.getcode() != 200 :
            print("Error on page: ",document.getcode())
            cur.execute('UPDATE Pages SET error=? WHERE url=?', (document.getcode(), url) )

        if 'text/html' != document.info().get_content_type() :
            print("Ignore non text/html page")
            cur.execute('DELETE FROM Pages WHERE url=?', ( url, ) )
            cur.execute('UPDATE Pages SET error=0 WHERE url=?', (url, ) )
            conn.commit()
            continue

        print('('+str(len(html))+')', end=' ')

        soup = BeautifulSoup(html, "html.parser")
    except KeyboardInterrupt:
        print('')
        print('Program interrupted by user...')
        break
    except:
        print("Unable to retrieve or parse page")
        cur.execute('UPDATE Pages SET error=-1 WHERE url=?', (url, ) )
        conn.commit()
        continue

    cur.execute('INSERT OR IGNORE INTO Pages (url, html, new_rank) VALUES ( ?, NULL, 1.0 )', ( url, ) )
    cur.execute('UPDATE Pages SET html=? WHERE url=?', (memoryview(html), url ) )
    conn.commit()

    # Retrieve all of the anchor tags
    tags = soup('a')
    count = 0
    for tag in tags:
        href = tag.get('href', None)
        if ( href is None ) : continue
        # Resolve relative references like href="/contact"
        # 필요한 URL 얻기
        up = urlparse(href)
        if ( len(up.scheme) < 1 ) :
            href = urljoin(url, href)
        ipos = href.find('#')
        if ( ipos > 1 ) : href = href[:ipos]
        if ( href.endswith('.png') or href.endswith('.jpg') or href.endswith('.gif') ) : continue
        if ( href.endswith('/') ) : href = href[:-1]
        # print href
        if ( len(href) < 1 ) : continue

		# Check if the URL is in any of the webs
        found = False
        for web in webs:
            if ( href.startswith(web) ) :
                found = True
                break
        if not found : continue

        cur.execute('INSERT OR IGNORE INTO Pages (url, html, new_rank) VALUES ( ?, NULL, 1.0 )', ( href, ) )
        count = count + 1
        conn.commit()

        cur.execute('SELECT id FROM Pages WHERE url=? LIMIT 1', ( href, ))
        try:
            row = cur.fetchone()
            toid = row[0]
        except:
            print('Could not retrieve id')
            continue
        # print fromid, toid
        cur.execute('INSERT OR IGNORE INTO Links (from_id, to_id) VALUES ( ?, ? )', ( fromid, toid ) )


    print(count)

cur.close()

 

spider.py 코드를 실행시켜볼게요.

처음이니깐 www.dr-chuck.com을 입력하면 8769 문자열이 있고 4개의 링크가 있다고 하네요.

DB를 열어보면

Webs 테이블에는 한개의 URL 우리가 볼 수 있는 URL 입니다.

 

Webs 테이블

Pages 테이블에는 5개의 다른 URL이 있습니다. 

Pages 테이블

Links 테이블에는 페이지1이 페이지 2에 페이지1이 페이지3에 연결되어있죠

Links 테이블

 

댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31