1. 程式人生 > >【python】爬蟲篇:python使用psycopg2批量插入資料(三)

【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

 

總結:到此,結構如下

然鵝我還是想要插入資料庫執行緒只有一條。。。。也就是多個執行緒是否能夠把資料放入一個統一的地方,然後單獨啟動一條執行緒,把這個地方的資料批量插入資料庫?

欲知後事如何,且聽下回分解!

好了標準結尾,菜雞一隻,如果有更好的思路或者不明白的問題可以給我評論,但求輕噴~