【python】爬蟲篇:python使用psycopg2批量插入資料(三)
本人菜雞,有什麼錯誤,還望大家批評指出,最近在更新python的爬蟲系列,○( ^皿^)っHiahiahia…
該系列暫時總共有3篇文章,連線如下
【python】爬蟲篇:python連線postgresql(一):https://blog.csdn.net/lsr40/article/details/83311860
【python】爬蟲篇:python對於html頁面的解析(二):https://blog.csdn.net/lsr40/article/details/83380938
【python】爬蟲篇:python使用psycopg2批量插入資料(三):https://blog.csdn.net/lsr40/article/details/83537974
根據前兩篇的思路,我們已經將html頁面解析成功,接下來就是insert插入資料庫,總所周知1W條資料一條一條插入,肯定是比一次性插入1W條來得慢的,所以最好要考慮到批量插入資料庫,既可以緩解資料庫的壓力,又能加快速度。
psycopg2的文件:http://initd.org/psycopg/docs/
# 插入資料庫的方法 def insertManyRow(strings): # 這裡就不需要遍歷了,因為executemany接受 # for index in range(len(rows)): try: conn_2 = psycopg2.connect(database="資料庫", user="使用者名稱", password="密碼", host="ip", port="埠號") cur2 = conn_2.cursor() sql2 = "INSERT INTO test(欄位1,欄位2,欄位3,欄位4,欄位5) VALUES(%s,%s,%s,%s,%s)" cur2.executemany(sql2, strings) conn_2.commit() conn_2.close() except Exception as e: print("執行sql時出錯:%s" % (e)) conn_word_2.rollback() conn_2.close()
關於這個strings可以傳入什麼樣的引數型別?見如下程式碼:
# 第一種:strings可以是 strings = ({'num': 0, 'text': 'Zero'}, {'num': 1, 'text': 'Item One'}, {'num': 2, 'text': 'Item Two'}, {'num': 3, 'text': 'Three'}) cur.executemany("INSERT INTO test VALUES (%(num)d, %(text)s)", rows) # 第二種:strings可以是,我測試的時候是第二種速度更快,但是應該沒有快多少 strings = [[0,'zero'],[1,'item one'],[3,'item two']] sql2 = "INSERT INTO test(num,text) VALUES(%s,%s)" cur.executemany(sql,strings)
理論上來說,插入就批量插入就好了,不要搞什麼多執行緒,但是如果是多執行緒獲取資料的話,看起來也只能每個執行緒各自往資料庫中插入(下一篇文章會使用其他技術來解決這個問題),但是請注意資料庫連線有限,在使用的時候注意看下資料庫連線被佔了多少,是否有釋放連線!
因此來總結下這整套方案用的東西和缺點:
流程:
1、先從資料庫查出資料
2、將每條資料通過urllib和python多執行緒的方式請求到頁面
3、通過bs4或者xpath或者其他的html頁面解析的方式去解析到我想要的東西
4、將我想要的東西一批一批的傳入批量插入資料庫的程式碼中,然後執行批量插入
每個點會遇到的問題:
1、從資料庫,我一次性查出200W條資料,python報了個記憶體溢位的錯誤,我一直不太清楚python的記憶體機制,其實java我也沒有非常清晰,希望以後會有機會多學學!可以寫個迴圈,(在sql裡面做排序,然後limit偏移量疊加)就像做java的分頁功能一樣,每次處理一頁的資料,處理完再從資料庫查詢下一頁(這種方式在該系列的第一篇文章中有提到,用offset和limit實現)。當然我認真搜尋了下,發現了executemany方法,也就是說,我可以查詢全部資料,但是並不用一次性全部加載出來,分批載入就可以(這裡我沒實際測試過會不會記憶體溢位,但是我想應該是不會溢位,畢竟不是一次性將全部資料取出),每次呼叫executemany,返回的結果相當於有一個遊標往下偏移,就是不會查出重複資料,這樣也是能夠滿足我的需求的。
executemany的API:http://initd.org/psycopg/docs/cursor.html?highlight=fetchmany#cursor.fetchmany,
程式碼如下:
if __name__ == '__main__':
conn = psycopg2.connect(database="資料庫", user="使用者名稱", password="密碼",
host="ip",
port="埠")
cur = conn.cursor()
# 查詢條件
sql = "查詢全量資料的sql"
cur.execute(sql)
# 手動給定迴圈次數5次
for i in range(5):
# 每次查詢1000條資料
rows = cur.fetchmany(size=1000)
print('第{i}次拉取到資料'.format(i=i+1))
for row in rows:
print(row)
conn.close()
2、這步就考慮用多執行緒還是多程序,每個執行緒(程序)的資料從哪來,必須不能重複;有時候在請求頁面的時候會假死(卡住),相應的api是否有timeout時間,例如我用的urllib.request.urlopen(url,timeout=5)就有這樣的引數;還有一點,是否會被封號,多久解封,我在做的這個是,今天被封,得等到明天才能再用,所以得有多個ip或者偽裝自己的方式,並且要做爬取該文章的時候是否已經被封了,如果被封了可以發警報或者停止繼續爬等等,避免浪費資源。
3、解析的方式很多,在能接受消耗資源的情況下,哪種更快?是否有更好的方式來獲取(該系列第二篇文章有介紹)?解析出來的資料如何拼接,傳遞到下一個方法(我會在下一篇文章來談談這個點)是這個階段要考慮的問題。
4、批量插入資料庫,該篇文章提供了一種方法,就是解析完之後把資料List加到一個外層的List列表中(List巢狀,類似如上程式碼strings的第二種方式),傳入批量插入的程式碼中執行executemany,然後我看到除了executemany之外還有一個execute_batch,並且可以設定page_size(預設值是100),這是一次能插入100條資料,然後插入多個批次固定條數的sql,我自己沒有測試過,因為我每個批次資料量不大,但是看stackoverflow上,有這麼一段話。
大概的意思就是說在他的測試中execute_batch的效能是executemany的兩倍,並且可以調整每次插入的資料條數(page_size)。
連結在這裡:https://stackoverflow.com/questions/2271787/psycopg2-postgresql-python-fastest-way-to-bulk-insert