成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Python網絡爬蟲的同步和異步

開發 后端
同步就是讓子任務串行,而異步有點影分身之術,但在任意時間點,真身只有一個,子任務并不是真正的并行,而是充分利用了碎片化的時間,讓程序不要浪費在等待上。這就是異步,效率杠桿的。

一、同步與異步

 

  1. #同步編程(同一時間只能做一件事,做完了才能做下一件事情)  
  2. <-a_url-><-b_url-><-c_url->  
  3. #異步編程 (可以近似的理解成同一時間有多個事情在做,但有先后)  
  4. <-a_url->  
  5.   <-b_url->  
  6.     <-c_url->  
  7.       <-d_url->  
  8.         <-e_url->  
  9.           <-f_url->  
  10.             <-g_url->  
  11.               <-h_url->  
  12.                 <--i_url-->  
  13.                   <--j_url--> 

 

模板

 

  1. import asyncio 
  2.  
  3. #函數名:做現在的任務時不等待,能繼續做別的任務。 
  4.  
  5. async def donow_meantime_dontwait(url): 
  6.  
  7.     response = await requests.get(url) 
  8.  
  9. #函數名:快速高效的做任務 
  10.  
  11. async def fast_do_your_thing(): 
  12.  
  13.     await asyncio.wait([donow_meantime_dontwait(url) for url in urls]) 
  14.  
  15. #下面兩行都是套路,記住就好 
  16.  
  17. loop = asyncio.get_event_loop() 
  18.  
  19. loop.run_until_complete(fast_do_your_thing()) 

tips:

await表達式中的對象必須是awaitable

requests不支持非阻塞

aiohttp是用于異步請求的庫

代碼

 

  1. import asyncio 
  2. import requests 
  3. import time 
  4. import aiohttp 
  5. urls = ['https://book.douban.com/tag/小說','https://book.douban.com/tag/科幻'
  6.         'https://book.douban.com/tag/漫畫','https://book.douban.com/tag/奇幻'
  7.         'https://book.douban.com/tag/歷史','https://book.douban.com/tag/經濟學'
  8. async def requests_meantime_dont_wait(url): 
  9.     print(url) 
  10.     async with aiohttp.ClientSession() as session: 
  11.         async with session.get(url) as resp: 
  12.             print(resp.status) 
  13.             print("{url} 得到響應".format(url=url)) 
  14. async def fast_requsts(urls): 
  15.     start = time.time() 
  16.     await asyncio.wait([requests_meantime_dont_wait(url) for url in urls]) 
  17.     end = time.time() 
  18.     print("Complete in {} seconds".format(end - start)) 
  19. loop = asyncio.get_event_loop() 
  20. loop.run_until_complete(fast_requsts(urls)) 

gevent簡介

gevent是一個python的并發庫,它為各種并發和網絡相關的任務提供了整潔的API。

gevent中用到的主要模式是greenlet,它是以C擴展模塊形式接入Python的輕量級協程。 greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。

猴子補丁

requests庫是阻塞式的,為了將requests同步更改為異步。只有將requests庫阻塞式更改為非阻塞,異步操作才能實現。

而gevent庫中的猴子補丁(monkey patch),gevent能夠修改標準庫里面大部分的阻塞式系統調用。這樣在不改變原有代碼的情況下,將應用的阻塞式方法,變成協程式的(異步)。

代碼

 

  1. from gevent import monkey  
  2. import gevent  
  3. import requests  
  4. import time  
  5.  
  6. monkey.patch_all()  
  7. def req(url):  
  8.     print(url)  
  9.     resp = requests.get(url)  
  10.     print(resp.status_code,url)  
  11.  
  12. def synchronous_times(urls): 
  13.  
  14.     """同步請求運行時間"""  
  15.     start = time.time() 
  16.      for url in urls:  
  17.         req(url)  
  18.     end = time.time()  
  19.     print('同步執行時間 {} s'.format(end-start))  
  20.  
  21. def asynchronous_times(urls):  
  22.     """異步請求運行時間"""  
  23.     start = time.time()  
  24.     gevent.joinall([gevent.spawn(req,url) for url in urls])  
  25.     end = time.time()  
  26.     print('異步執行時間 {} s'.format(end - start))  
  27.  
  28. urls = ['https://book.douban.com/tag/小說','https://book.douban.com/tag/科幻' 
  29.         'https://book.douban.com/tag/漫畫','https://book.douban.com/tag/奇幻' 
  30.         'https://book.douban.com/tag/歷史','https://book.douban.com/tag/經濟學' 
  31.  
  32. synchronous_times(urls)  
  33. asynchronous_times(urls) 

 

gevent:異步理論與實戰

gevent庫中使用的最核心的是Greenlet-一種用C寫的輕量級python模塊。在任意時間,系統只能允許一個Greenlet處于運行狀態

一個greenlet遇到IO操作時,比如訪問網絡,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。由于IO操作非常耗時,經常使程序處于等待狀態,有了gevent為我們自動切換協程,就保證總有greenlet在運行,而不是等待IO。

串行和異步

高并發的核心是讓一個大的任務分成一批子任務,并且子任務會被被系統高效率的調度,實現同步或者異步。在兩個子任務之間切換,也就是經常說到的上下文切換。

同步就是讓子任務串行,而異步有點影分身之術,但在任意時間點,真身只有一個,子任務并不是真正的并行,而是充分利用了碎片化的時間,讓程序不要浪費在等待上。這就是異步,效率杠桿的。

gevent中的上下文切換是通過yield實現。在這個例子中,我們會有兩個子任務,互相利用對方等待的時間做自己的事情。這里我們使用gevent.sleep(0)代表程序會在這里停0秒。

 

  1. import gevent  
  2. def foo():  
  3.     print('Running in foo' 
  4.     gevent.sleep(0)  
  5.     print('Explicit context switch to foo again' 
  6.  
  7. def bar():  
  8.     print('Explicit context to bar' 
  9.     gevent.sleep(0)  
  10.     print('Implicit context switch back to bar' 
  11.  
  12. gevent.joinall([  
  13.     gevent.spawn(foo),  
  14.     gevent.spawn(bar) 
  15.  
  16.     ]) 

 

運行的順序:

 

  1. Running in foo  
  2. Explicit context to bar  
  3. Explicit context switch to foo again  
  4. Implicit context switch back to bar 

 

同步異步的順序問題

同步運行就是串行,123456...,但是異步的順序是隨機的任意的(根據子任務消耗的時間而定)

代碼

 

  1. import gevent  
  2. import random  
  3. def task(pid):  
  4.     "" 
  5.     Some non-deterministic task  
  6.     "" 
  7.     gevent.sleep(random.randint(0,2)*0.001)  
  8.     print('Task %s done' % pid)  
  9.  
  10.  
  11. #同步(結果更像串行)  
  12. def synchronous():  
  13.     for i in range(1,10):  
  14.         task(i)  
  15.  
  16.  
  17. #異步(結果更像亂步)  
  18. def asynchronous():  
  19.     threads = [gevent.spawn(task, i) for i in range(10)]  
  20.     gevent.joinall(threads)  
  21.  
  22.  
  23. print('Synchronous同步:' 
  24. synchronous()  
  25.  
  26.  
  27. print('Asynchronous異步:'
  28.  
  29. asynchronous() 

 

輸出

Synchronous同步:

 

  1. Task 1 done  
  2. Task 2 done  
  3. Task 3 done 
  4. Task 4 done  
  5. Task 5 done  
  6. Task 6 done  
  7. Task 7 done  
  8. Task 8 done  
  9. Task 9 done 

 

Asynchronous異步:

 

  1. Task 1 done  
  2. Task 5 done  
  3. Task 6 done  
  4. Task 2 done  
  5. Task 4 done  
  6. Task 7 done  
  7. Task 8 done  
  8. Task 9 done  
  9. Task 0 done  
  10. Task 3 done 

 

同步案例中所有的任務都是按照順序執行,這導致主程序是阻塞式的(阻塞會暫停主程序的執行)。

gevent.spawn會對傳入的任務(子任務集合)進行進行調度,gevent.joinall方法會阻塞當前程序,除非所有的greenlet都執行完畢,程序才會結束。

實戰

實現gevent到底怎么用,把異步訪問得到的數據提取出來。

在有道詞典搜索框輸入“hello”按回車。觀察數據請求情況 觀察有道的url構建。

分析url規律

 

  1. #url構建只需要傳入word即可 
  2.  
  3. url = "http://dict.youdao.com/w/eng/{}/".format(word) 

 

解析網頁數據

 

  1. def fetch_word_info(word):  
  2.     url = "http://dict.youdao.com/w/eng/{}/".format(word)  
  3.  
  4.     resp = requests.get(url,headers=headers)  
  5.     doc = pq(resp.text)  
  6.     pros = ''  
  7.     for pro in doc.items('.baav .pronounce'):  
  8.         pros+=pro.text()  
  9.  
  10.     description = ''  
  11.     for li in doc.items('#phrsListTab .trans-container ul li'):  
  12.         description +=li.text()  
  13.  
  14.     return {'word':word,'音標':pros,'注釋':description} 

 

因為requests庫在任何時候只允許有一個訪問結束完全結束后,才能進行下一次訪問。無法通過正規途徑拓展成異步,因此這里使用了monkey補丁

同步代碼

 

  1. import requests  
  2. from pyquery import PyQuery as pq  
  3. import gevent  
  4. import time  
  5. import gevent.monkey  
  6. gevent.monkey.patch_all() 
  7.  
  8. words = ['good','bad','cool' 
  9.          'hot','nice','better' 
  10.          'head','up','down' 
  11.          'right','left','east' 
  12.  
  13. def synchronous():  
  14.     start = time.time()  
  15.     print('同步開始了' 
  16.     for word in words:  
  17.         print(fetch_word_info(word))  
  18.     end = time.time()  
  19.     print("同步運行時間: %s 秒" % str(end - start)) 
  20.  
  21.  
  22. #執行同步  
  23. synchronous() 

 

異步代碼

 

  1. import requests  
  2. from pyquery import PyQuery as pq  
  3. import gevent  
  4. import time  
  5. import gevent.monkey  
  6. gevent.monkey.patch_all()  
  7.  
  8. words = ['good','bad','cool' 
  9.          'hot','nice','better' 
  10.          'head','up','down' 
  11.          'right','left','east' 
  12.  
  13. def asynchronous():  
  14.     start = time.time()  
  15.     print('異步開始了' 
  16.     events = [gevent.spawn(fetch_word_info,word) for word in words]  
  17.     wordinfos = gevent.joinall(events)  
  18.     for wordinfo in wordinfos:  
  19.         #獲取到數據get方法  
  20.         print(wordinfo.get())  
  21.     end = time.time()  
  22.     print("異步運行時間: %s 秒"%str(end-start))  
  23.  
  24. #執行異步  
  25. asynchronous() 

 

我們可以對待爬網站實時異步訪問,速度會大大提高。我們現在是爬取12個詞語的信息,也就是說一瞬間我們對網站訪問了12次,這還沒啥問題,假如爬10000+個詞語,使用gevent的話,那幾秒鐘之內就給網站一股腦的發請求,說不定網站就把爬蟲封了。

解決辦法

將列表等分為若干個子列表,分批爬取。舉例我們有一個數字列表(0-19),要均勻的等分為4份,也就是子列表有5個數。下面是我在stackoverflow查找到的列表等分方案:

方法1

 

  1. seqence = list(range(20)) 
  2.  
  3. size = 5 #子列表長度 
  4.  
  5. output = [seqence[i:i+sizefor i in range(0, len(seqence), size)] 
  6.  
  7. print(output

 

方法2

 

  1. chunks = lambda seq, size: [seq[i: i+sizefor i in range(0, len(seq), size)] 
  2.  
  3. print(chunks(seq, 5)) 

 

方法3

 

  1. def chunks(seq,size):  
  2.     for i in range(0,len(seq), size):  
  3.         yield seq[i:i+size 
  4. prinT(chunks(seq,5))  
  5.     for  x  in chunks(req,5):  
  6.          print(x)  

 

數據量不大的情況下,選哪一種方法都可以。如果特別大,建議使用方法3.

動手實現

 

  1. import requests  
  2. from pyquery import PyQuery as pq  
  3. import gevent  
  4. import time  
  5. import gevent.monkey  
  6. gevent.monkey.patch_all()
  7.  
  8. words = ['good','bad','cool' 
  9.          'hot','nice','better' 
  10.          'head','up','down' 
  11.          'right','left','east' 
  12.  
  13. def fetch_word_info(word):   
  14.     url = "http://dict.youdao.com/w/eng/{}/".format(word)  
  15.  
  16.     resp = requests.get(url,headers=headers)  
  17.     doc = pq(resp.text) 
  18.  
  19.  
  20.     pros = ''  
  21.     for pro in doc.items('.baav .pronounce'):  
  22.         pros+=pro.text()  
  23.  
  24.     description = ''  
  25.     for li in doc.items('#phrsListTab .trans-container ul li'):  
  26.         description +=li.text()  
  27.  
  28.     return {'word':word,'音標':pros,'注釋':description}  
  29.  
  30.  
  31. def asynchronous(words):  
  32.     start = time.time()  
  33.     print('異步開始了'  
  34.  
  35.     chunks = lambda seq, size: [seq[i: i + sizefor i in range(0, len(seq), size)] 
  36.  
  37.  
  38.     for subwords in chunks(words,3):  
  39.         events = [gevent.spawn(fetch_word_info, word) for word in subwords]   
  40.         wordinfos = gevent.joinall(events)  
  41.         for wordinfo in wordinfos:  
  42.             # 獲取到數據get方法  
  43.             print(wordinfo.get()) 
  44.  
  45.         time.sleep(1)   
  46.         end = time.time()  
  47.     print("異步運行時間: %s 秒" % str(end - start))  
  48.  
  49. asynchronous(words)  

 

責任編輯:龐桂玉 來源: Python愛好者社區
相關推薦

2024-07-26 21:55:39

RustRESTfulAPI

2012-03-01 20:32:29

iOS

2023-09-07 08:15:58

場景同步異步

2021-04-02 11:05:57

Python同步異步

2021-03-11 11:32:40

Python同步異步

2020-09-25 18:10:06

Python 開發編程語言

2009-08-21 10:28:21

C#異步方法C#同步方法

2023-08-30 08:43:42

asyncioaiohttp

2018-09-27 12:38:46

Python同步異步

2009-10-20 16:48:30

C#委托

2023-12-29 22:41:12

同步架構業務

2024-03-08 12:17:39

網絡爬蟲Python開發

2019-06-11 09:06:22

網絡爬蟲工具

2022-06-13 06:20:42

setStatereact18

2018-11-30 09:30:46

aiohttp爬蟲Python

2023-03-13 17:18:09

OkHttp同步異步

2024-07-02 11:32:38

2024-11-22 16:06:21

2017-06-20 09:07:22

uvloopPython網絡框架

2024-11-27 06:31:02

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av在线伊人| 激情小视频 | 日韩激情免费 | 深夜福利影院 | 日韩精品视频在线播放 | 欧美一级毛片免费观看 | 国产乱码久久久 | 久久天天综合 | 欧美视频在线播放 | 欧美精品一区二区三区四区 | www.夜夜骑.com | 拍真实国产伦偷精品 | 婷婷在线免费 | 国产区视频在线观看 | 免费成人高清在线视频 | 精品欧美色视频网站在线观看 | h视频在线免费看 | 亚洲国产一区在线 | 日韩精品二区 | 在线观看国产视频 | 国产精品久久久久久久久免费相片 | 久久久91 | 国产在线看片 | www.日本精品 | 久久久这里只有17精品 | 免费在线观看成人 | av片免费 | 奇米四色在线观看 | 欧美日韩不卡合集视频 | 91色综合| 91麻豆产精品久久久久久 | 极品电影院 | 99在线资源 | 精品少妇一区二区三区日产乱码 | 一级黄色片网址 | 91.xxx.高清在线 | 国产高清区 | 欧美日韩免费一区二区三区 | 一区二区三区四区毛片 | 一级日韩| www.日日夜夜 |