본문 바로가기

카테고리 없음

PY4E: Chapter 15 Worked Example: Twfriends.py

저번에 했던 트위터 실습의 연장에 있는 것으로

한 계정의 친구 목록을 가져오고, 그 친구 중 한명의 목록을 다시 가져와 데이터베이스에 저장한다.

 

PEOPLE과 FOLLOWS테이블이 있는데, PEOPLE은 이제까지 검색된 친구의 목록이며

FOLLOWS는 사람들간의 관계를 나타낸다. 그렇기 때문에 FOLLOWS 테이블은 다(사람) 대 다(사람)간의 관계를 나타낸다.

 

1. 필요한 모듈 임포트

import urllib.request, urllib.parse, urllib.error
import twurl
import json
import sqlite3
import ssl

2. 데이타베이스, 테이블 만들기

TWITTER_URL = 'https://api.twitter.com/1.1/friends/list.json'

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

cur.execute('''CREATE TABLE IF NOT EXISTS People
            (id INTEGER PRIMARY KEY, name TEXT UNIQUE, retrieved INTEGER)''')
cur.execute('''CREATE TABLE IF NOT EXISTS Follows
            (from_id INTEGER, to_id INTEGER, UNIQUE(from_id, to_id))''')

a. 접속해야할 트위터 API URL 설정

b. sqilite와의 접점을 만듦

c. People table 행: id(기본키), name, retrieved

d. Follows table 행: from_id, to_id (이 두쌍이 기본키)

 

3. 인증 무시

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

 

4-1. while 반복문의 시작. quit이 입력되거나, 아무것도 입력되지 않는다면

while True:
    acct = input('Enter a Twitter account, or quit: ')
    if (acct == 'quit'): break
    if (len(acct) < 1):
        cur.execute('SELECT id, name FROM People WHERE retrieved=0 LIMIT 1')
        try:
            (id, acct) = cur.fetchone()
        except:
            print('No unretrieved Twitter accounts found')
            continue

a. 검색하고자 하는 acct(계정) 입력

b. quit입력시 break

c. 입력이 되지 않는다면, People 테이블에서 retrieved=0(검색되지 않은)인 id, name 값 선택

d. fetchone을 통해 그 튜플을 (id, acct)로 할당

 

f. retrieved=0이 없다면, 다시 입력 창으로.

 

4-2. 무엇인가 입력된다면, 아래와 같이 처리합니다. 지금은 입력된 ID가 있냐 없냐, 없으면 추가, 있으면 그대로 쓰고

    else:
        cur.execute('SELECT id FROM People WHERE name = ? LIMIT 1',
                    (acct, ))
        try:
            id = cur.fetchone()[0]
        except:
            cur.execute('''INSERT OR IGNORE INTO People
                        (name, retrieved) VALUES (?, 0)''', (acct, ))
            conn.commit()
            if cur.rowcount != 1:
                print('Error inserting account:', acct)
                continue
            id = cur.lastrowid

a. People 테이블에서 입력된 acct와 같은 name값을 가지는 id값을 선택 

b. 그 id값은 변수 id에 넣고

c. b가 실행되지 않으면, people의 name, retrieved (acct,0)을 넣어 추가한다.

d. cur.rowcount는 위의 삽입동작으로 얼마나 많은 행이 영향을 받았는지 확인하며 그 값이 1아니라면 다시 처음으로 돌아갑니다. 또 그때의 id값은 cur.lastrowid로 커서의 마지막 로우의 id를 확인하고 id에 할당합니다.

 

4-3. 아직 while문은 끝나지 않았습니다. 어쨌든 내가 검색한 Id의 테이블 내에서의 id를 얻은 상태에서 아래를 진행합니다. 아래는 본격적으로 API를 이용하여 작업을 수행하는 과정입니다.

    url = twurl.augment(TWITTER_URL, {'screen_name': acct, 'count': '100'})
    print('Retrieving account', acct)
    try:
        connection = urllib.request.urlopen(url, context=ctx)
    except Exception as err:
        print('Failed to Retrieve', err)
        break

a. twurl.argument. 함수에 url, parameter를 넣어줘 전자서명 url을 가져옵니다.

b. 수집되고 있는 계정 이름을 보여준 뒤,

c.  urllib.request.urlopen을 통해 커넥션을 만듭니다.

d. 그렇지 못하다면, 에러를 프린트하고 break합니다.

 

    data = connection.read().decode()
    headers = dict(connection.getheaders())

    print('Remaining', headers['x-rate-limit-remaining'])

    try:
        js = json.loads(data)
    except:
        print('Unable to parse json')
        print(data)
        break

 

a. 읽어온 값을 decode()하여 data에 넣고, header를 가져옵니다.

b. 헤더로부터 남은 횟수를 가져옵니다.

c. 데이타를 js로 읽어옵니다.

d. 그렇지 못하다면, break합니다.

 

    if 'users' not in js:
        print('Incorrect JSON received')
        print(json.dumps(js, indent=4))
        continue

    cur.execute('UPDATE People SET retrieved=1 WHERE name = ?', (acct, ))

 

a. 읽어온 js데이터 안의 user데이터가 없다면

b. 문구를 출력하고, 화면에 4개 들여쓰기 된 값을 뿌립니다.

c. 다시 while문 처음으로 돌아갑니다.

 

d. People 테이블의 nameacct와 같은 곳을 retrieved1로 수정합니다. 이것은 이 name에 대한 수집이 이뤄졌음을 의미합니다.

 

 

    countnew = 0
    countold = 0
    for u in js['users']:
        friend = u['screen_name']
        print(friend)
        cur.execute('SELECT id FROM People WHERE name = ? LIMIT 1',
                    (friend, ))

a.  countnew의 값을 0, countold의 값은 0으로 놓은 뒤

b. js데이터의 users 태그 u 대해 반복문을 시행합니다.

c. users의 태그 안의 screen_namefriend로 놓고

d. 그 값을 출력합니다.

e. People 테이블에서 friend의 이름과 같은 곳의 id를 선택하고

 

*즉, 기존의 그 값이 있었다면, 그 아이디의 위치가 friend_id가 됩니다. 그리고 countold1이 더해집니다.

만약 새로운 id가 들어온 것이라면 추가를 해주고, countnew1을 더합니다.

try:
            friend_id = cur.fetchone()[0]
            countold = countold + 1
        except:
            cur.execute('''INSERT OR IGNORE INTO People (name, retrieved)
                        VALUES (?, 0)''', (friend, ))
            conn.commit()

a. friend_id의 값에 위에서 selectid 값을 부여합니다.

b. countold에 1을 더한 값을 다시 countold로 놓습니다.

c. 위 작업이 되지 않으면 friendPeople테이블에 없는 것이기 때문에 새로운 People테이블의 nameretrievedfriend0값을 넣어줍니다.

d. 기억하십쇼, 이 작업은 users 모두에 대해 해당합니다. 즉 없으면 없는 만큼, People에 추가됩니다.

 

*아직 except문이다. 

if cur.rowcount != 1:
                print('Error inserting account:', friend)
                continue
friend_id = cur.lastrowid
countnew = countnew + 1

a. except문의 새로운 로우가 People테이블에 추가되면 아래를 확인합니다.

b. 방금 sql 문으로 영향을 받은 로우가 1개가 아니라면

c. 에러 메시지를 뛰우고, for문 처음으로 돌아갑니다.

 

d. 1개만이 추가 되었다면, friend_id는 마지막으로 있던 커서의 위치입니다.

e. 그리고 countnew에 1 더해 countnew로 할당한다.

 

 

    cur.execute('''INSERT OR IGNORE INTO Follows (from_id, to_id)
                    VALUES (?, ?)''', (id, friend_id))
    print('New accounts=', countnew, ' revisited=', countold)
    print('Remaining', headers['x-rate-limit-remaining'])
    conn.commit()

a. 드디어 마지막, Follow테이블은 구성한다.

b. from_idto_id값에 id(우리가 검색한 ID in People table)와 friend_id(그 ID의 친구목록의 친구의 ID in People table)를 넣어준다.

c. New accounts 는 기존에 없어서 새로 People테이블에 추가된 ID이고, revisited는 기존의 People테이블에 있었던 ID의 갯수 이다.