標籤:isp ica 並且 分享 json spider 命名 friend lap
14.6 用資料庫爬取Twitter
在本節中,我們將建立一個簡單的爬蟲程式。它將仔細搜尋Twitter帳號,並建立一個帳號資料庫。注意:在運行這個程式時要非常小心。如果你抓取太多的資料或者長時間運行這個程式,最終可能造成Twitter帳號被關閉。
任何爬蟲程式都存在一個問題,即它需要能夠關閉和重啟很多次數,並且你不想丟失你至今為止擷取的資料。你不想每次重啟都重頭擷取所有資料,所以我們要儲存已獲得的資料,這樣我們的程式可以備份,並且從它停止的地方重新開始。
我們將從擷取某人的Twitter好友和他們的狀態開始,迴圈搜尋好友名單,將擷取的每個好友添加到資料庫。當我們處理完一個人的Twitter好友,記錄到我們的資料庫,然後擷取每個好友的好友。我們一遍又一遍地這麼做,挑選未訪問的人,擷取他們的好友名單並將我們還未記錄的好友添加到我們的列表,以便將來的訪問。
我們同時追蹤資料庫中一個特定好友出現的次數,從而擷取他的受歡迎程度。
通過儲存我們的已知帳號列表,和是否擷取該帳號,以及該帳號的受歡迎度至電腦的硬碟,我們可以隨時停止和重啟我們程式。
這個程式有一點複雜。它是基於本書中先前使用Twitter API 練習代碼。
我們的Twitter爬蟲應用程式的源碼如下:
1 import urllib.request 2 import twurl 3 import json 4 import sqlite3 5 6 TWITTER_URL = ‘https://api.twitter.com/1.1/friends/list.json‘ 7 8 conn = sqlite3.connect(‘spider.sqlite3‘) 9 cur = conn.cursor()10 11 cur.execute(‘‘‘12 CREATE TABLE IF NOT EXISTS Twiter13 (name TEXT, retrieved INTEGER, friends INTERGER)‘‘‘)14 15 while True:16 acct = input(‘Enter a Twitter account:, or quit:‘)17 if (acct == ‘quit‘) : break18 if (len(acct) < 1):19 cur.execute(‘SELECT name FROM Twitter WHERE retrieved = 0 LIMIT 1‘)20 try:21 acct = cur.fetchone()[0]22 except:23 print(‘No unretrieved Twitter accounts found‘)24 continue25 url = twurl.augment(TWITTER_URL,26 {‘screen_name‘:acct, ‘count‘:20})27 print(‘Retrieving‘, url)28 connection = urllib.request.urlopen(url)29 data = connection.read()30 headers = connection.info().dict31 js = json.loads(data)32 33 cur.execute(‘UPDATE Twitter SET retrieved=1 WHERE name = ?‘, (acct,))34 35 countnew = 036 countold = 037 for u in js[‘users‘]:38 friend = u[‘screen_name‘]39 print(friend)40 cur.execute(‘SELECT friends FROM Twitter WHERE name = ? LIMIT 1‘,41 (friend,))42 try:43 count = cur.fetchone()[0]44 cur.execute(‘UPDATE Twitter SET friends = ? WHERE name = ?‘,45 (count+1, friend))46 countold = countold + 147 except:48 cut.execute(‘‘‘INSERT INTO Twitter (name, retrieved, friends49 VALUES(?, 0, 1)‘‘‘, (friend,))50 countnew = countnew + 151 print(‘New accounts =‘, countnew, ‘ revisited‘, countold)52 conn.commit()53 54 cur.close()
View Code
我們的資料庫儲存在spider.sqlite3的檔案中。它有一個命名為Twitter的表,表中的每一行都有三個列:帳號名稱(name),我們是否從這個帳號擷取過好友(retrieved),以及這個帳號有多少次被加為好友(friends)。
在程式的主迴圈中,我們提示使用者輸入Twitter帳號名或者輸入“quit”退出程式。如果使用者輸入一個Twitter帳號,我們擷取這個使用者的所有好友清單及其狀態,然後把資料庫中還未存在的好友添加的資料庫中。如果這個好友已經存在,我們在好友數量欄位中加1。
如果使用者按了斷行符號鍵,我們在資料庫中尋找我們還未擷取過的下一個Twitter帳號,然後擷取這個帳號的的好友和狀態。把他們添加到資料庫或者更新他們的好友數量。
一旦我們擷取了好友名單和狀態,我們遍曆返回的JSON中的所有使用者項目並擷取每個使用者的呢稱。然後我們用SELECT語句來查看我們是否已經在資料庫中儲存了這個暱稱,如果有的話,是否擷取其好友資訊。
1 countnew = 0 2 countold = 0 3 for u in js[‘users‘] : 4 friend = u[‘screen_name‘] 5 print friend 6 cur.execute(‘SELECT friends FROM Twitter WHERE name = ? LIMIT 1‘, 7 (friend, ) ) 8 try: 9 count = cur.fetchone()[0]10 cur.execute(‘UPDATE Twitter SET friends = ? WHERE name = ?‘,11 (count+1, friend) )12 countold = countold + 113 except:14 cur.execute(‘‘‘INSERT INTO Twitter (name, retrieved, friends)15 VALUES ( ?, 0, 1 )‘‘‘, ( friend, ) )16 countnew = countnew + 117 print ‘New accounts=‘,countnew,‘ revisited=‘,countold18 conn.commit()
View Code
只要遊標執行SELECT語句,我們必定會擷取多行。我們可以用for語句來迴圈處理,但是因為我們用(LIMIT 1)限制只擷取一行,所以可以使用fetchone()方法返回查詢操作結果的第一行(也只有一行)。因為fetcheone()是以元組的方式返回行(即使只有一個欄位),我們使用索引[0]來提取元組的第一個值,擷取當前好友數量並儲存的變數count中。
如果提取成功,我們使用帶WHERE子句的SQL UPDATE語句給對應好友帳號的好友數量加1.要注意的是在SQL中有兩個預留位置(?),而且execute()方法的第二個參數是二元元組,其中包含了用於替換SQL中問號的值。
try模組中的代碼可能會因為未查詢到匹配 WHERE name=? 的記錄而失敗。所以在except模組中我們使用SQL INSERT語句將好友暱稱添加到表中,並指出我們還未擷取這個暱稱的好友,並且將他的好友數量設定為零。
當這個程式第一次運行時,我們輸入一個Twitter帳號,程式運行資訊如下:
Enter a Twitter account, or quit: drchuck
Retrieving http://api.twitter.com/1.1/friends ...
New accounts= 20 revisited= 0
Enter a Twitter account, or quit: quit
因為這是我們第一次運行程式,資料庫是空的,所以我們在spider.sqlite3檔案中建立資料庫,並添加一個名叫Twitter的表。然後我們擷取一些好友並將他們都添加到資料庫。
此時,我們可能會寫一個簡單的資料庫複寫程式,來看看我們的spider.sqlite3檔案中到底有什麼:
import sqlite3conn = sqlite3.connect(‘spider.sqlite3‘)cur = conn.cursor()cur.execute(‘SELECT * FROM Twitter‘)count = 0for row in cur :print rowcount = count + 1print count, ‘rows.‘cur.close()
View Code
這個程式開啟資料庫並查詢Twitter表中的所有行的列資訊,然後迴圈列印每一行。如果我們在第一執行前面的Twitter爬蟲軟體後運行這個程式,它的輸出如下所示:
(u‘opencontent‘, 0, 1)
(u‘lhawthorn‘, 0, 1)
(u‘steve_coppin‘, 0, 1)
(u‘davidkocher‘, 0, 1)
(u‘hrheingold‘, 0, 1)
...
20 rows.
我們看到每一行對應一個暱稱,這些暱稱我們都未進行爬取,並且在資料庫中它們都有一個好友。
現在資料庫反映出我們第一個Twitter帳號(drchuck)的好友爬取情況。我們可以再次運行程式,並且無需輸入帳號,只要按一下斷行符號,就可讓它擷取後面未處理的帳號資訊。程式的運行結果如下:
Enter a Twitter account, or quit:
Retrieving http://api.twitter.com/1.1/friends ...
New accounts= 18 revisited= 2
Enter a Twitter account, or quit:
Retrieving http://api.twitter.com/1.1/friends ...
New accounts= 17 revisited= 3
Enter a Twitter account, or quit: quit
因為我們按的是斷行符號鍵(我們未指定Twitter account),程式將執行以下代碼:
if ( len(acct) < 1 ) :cur.execute(‘SELECT name FROM Twitter WHERE retrieved = 0 LIMIT 1‘)try:acct = cur.fetchone()[0]except:print ‘No unretrieved twitter accounts found‘continue
View Code
我們使用SQL SELECT 語句來擷取資料庫中未處理的值為零的第一個使用者名稱。同時用try/except代碼塊中的fetchone[0]的方式,抽取擷取的暱稱或者輸出錯誤資訊,並再次尋找。
如果我們成功擷取了一個未處理的使用者暱稱,我們通過以下代碼擷取他們的資料:
url = twurl.augment(TWITTER_URL, {‘screen_name‘: acct, ‘count‘: ‘20‘} )print ‘Retrieving‘, urlconnection = urllib.urlopen(url)data = connection.read()js = json.loads(data)cur.execute(‘UPDATE Twitter SET retrieved=1 WHERE name = ?‘, (acct, ) )
View Code
一旦我們成功擷取資料,我們就用UPDATE語句語句將是否擷取列的值設定為1,指示這個帳號我們已經完成擷取工作。這樣可以防止重複擷取,並保持程式前行處理網路上的Twitter 好友。如果我們運行好友程式並輸入兩次斷行符號擷取下一個未訪問的好友的好友,然後運行複製程式,它將給出以下輸出:
(u‘opencontent‘, 1, 1)
(u‘lhawthorn‘, 1, 1)
(u‘steve_coppin‘, 0, 1)
(u‘davidkocher‘, 0, 1)
(u‘hrheingold‘, 0, 1)
...
(u‘cnxorg‘, 0, 2)
(u‘knoop‘, 0, 1)
(u‘kthanos‘, 0, 2)
(u‘LectureTools‘, 0, 1)
...
55 rows.
我們可以看到,我們正確的記錄了已經訪問的lhawthorn和opncontent兩位好友。同時cnxorg和kthanos已經有了兩個追隨者。因為現在我們擷取了三個人(drchuck,opencontent和lhawthon)的好友,我們表中有55行已擷取的好友。
每次我們運行這個程式並按斷行符號,它將挑選下一個未處理的帳號(例如 下個帳號將會是steve_coppin),擷取並標識steve_coppin每個好友,並將他們添加到資料庫中,如果他們已經存在,則更新他們的好友數量。
因為這個程式的資料儲存在硬碟上的資料庫中,所以爬取活動可以被任意中止並重新啟動而不會遺失資料。
註:文章原文為Dr. Charles Severance 的 《Python for Informatics》。此節中的代碼未改寫,並且用2.7版本調測時報認證錯誤,估計是oauth安全認證的問題。
Python for Infomatics 第14章 資料庫和SQL應用四(譯)