現在主要生物醫學資料庫如ensembl, nubi都有開放API接口,可以直接用程式去提出一堆的請求,不用像傳統的網頁查找那樣,省掉很多麻煩,只要稍微瞭解一下http原理,各種程式語言如R的httr(中文教學)或 是Python的requests,使用起來都算方便,
這裡遇到一個小問題,應該說想要更好的解決它:如何加速查找速度,假如我有上千個想要查找的資料?
舉例來說,當你想要找一大串4000個基因變異、相關基因功能,雖然不需要在網頁上點選4000次(可能花好幾天吧),幾行程式碼就可以解決,但當要丟4000次請求,來取得資料時,假設1次請求,要花5秒種取得資料,那麼4000次,還是要花6個小時,那要怎麼改善這個問題呢?因為我們很貪心的想要花更少時間(假如網路頻寬很大的話)
一種最直覺的方式,便是把要查找的variant列表,切成多份,然後同時跑,最後再把資料整合起來。
另外,因抱持者想要探索看看新方法,便嘗試使用Python中的Queue和Thread,用多線程的方式來解決這個問題。
我基本思路是將我要查找的Rsid(Variant的資訊),用iterator的方式來吐出每次要query的id,而Queue object可以把我處理好跟我所開啟thread間資訊同步的問題。
這邊是最後使用的代碼
#這邊是我想要查找的基因變異variant的ID test = RSID.iteritems() #我希望開的線程數量 concurrent = 10 #因為資料是以json檔傳回,最後我都要塞回這個test_list中 test_list = {} #使用queue的重點,是要解決共享變數不能被thread同時讀取的問題,他可以有效解決thread間在取用共享變數的問題 que = queue.Queue() for i in range(concurrent): que.put(next(test)[1]) def doWork(*args): tmp = args[0] while tmp.qsize() > 0: rsid = tmp.get() test_list[rsid]=rsid #這邊是ensembl Rest API的接口 server = "http://grch37.rest.ensembl.org" url_1 = "/variation/human/" url_2 = "?pops=1" ext = url_1+rsid+url_2 r = requests.get(server+ext, headers={ "Content-Type" : "application/json"}) if not r.ok: r.raise_for_status() sys.exit() decoded = r.json() test_list[rsid] = decoded print("\t[Info] Queue size={0}".format(que.qsize())) # 這邊是線程開啟和運行的地方 for i in range(concurrent): thread_name='Thd'+i t = threading.Thread(target=doWork, name=thread_name, args=(que,)) t.daemon = True t.start() td = datetime.datetime.now() - st print("\t[Info] Spending time={0}!".format(td))
P.S:後記,這種方式其實頗危險的,假如所使用的服務管理者,會特別管理高頻要資源的IP那麼就要注意,可能從此ip就被封了,所以最好可以掛載proxy在進行query,另一方面,時間允許下,就用單線慢慢抓吧!甚至必須去測試對方有沒有限制每秒能query的數量,就要去調適最佳化的參數!
閱讀參考:
Python Queue 模塊
what is the fastest way to send 100,000 HTTP requests in Python?
using iterators and generators in multi-threaded applications
iterables/iterators/generators
How many threads is too many?
Python模塊學習 – threading 多線程控制和處理