最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python網(wǎng)絡(luò)爬蟲中的同步與異步示例詳解

 更新時間:2018年02月03日 15:59:22   作者:我為峰2014  
這篇文章主要給大家介紹了關(guān)于Python網(wǎng)絡(luò)爬蟲中同步與異步的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

一、同步與異步

#同步編程(同一時間只能做一件事,做完了才能做下一件事情)
<-a_url-><-b_url-><-c_url->
#異步編程 (可以近似的理解成同一時間有多個事情在做,但有先后)
<-a_url->
 <-b_url->
 <-c_url->
 <-d_url->
 <-e_url->
  <-f_url->
  <-g_url->
  <-h_url->
  <--i_url-->
   <--j_url-->

模板

import asyncio
#函數(shù)名:做現(xiàn)在的任務(wù)時不等待,能繼續(xù)做別的任務(wù)。
async def donow_meantime_dontwait(url):
 response = await requests.get(url)
#函數(shù)名:快速高效的做任務(wù)
async def fast_do_your_thing():
 await asyncio.wait([donow_meantime_dontwait(url) for url in urls])
#下面兩行都是套路,記住就好
loop = asyncio.get_event_loop()
loop.run_until_complete(fast_do_your_thing())

tips:

  • await表達式中的對象必須是awaitable
  • requests不支持非阻塞
  • aiohttp是用于異步請求的庫

代碼

import asyncio
import requests
import time
import aiohttp
urls = ['https://book.douban.com/tag/小說','https://book.douban.com/tag/科幻',
 'https://book.douban.com/tag/漫畫','https://book.douban.com/tag/奇幻',
 'https://book.douban.com/tag/歷史','https://book.douban.com/tag/經(jīng)濟學']
async def requests_meantime_dont_wait(url):
 print(url)
 async with aiohttp.ClientSession() as session:
 async with session.get(url) as resp:
  print(resp.status)
  print("{url} 得到響應(yīng)".format(url=url))
async def fast_requsts(urls):
 start = time.time()
 await asyncio.wait([requests_meantime_dont_wait(url) for url in urls])
 end = time.time()
 print("Complete in {} seconds".format(end - start))
loop = asyncio.get_event_loop()
loop.run_until_complete(fast_requsts(urls))

gevent簡介

gevent是一個python的并發(fā)庫,它為各種并發(fā)和網(wǎng)絡(luò)相關(guān)的任務(wù)提供了整潔的API。

gevent中用到的主要模式是greenlet,它是以C擴展模塊形式接入Python的輕量級協(xié)程。 greenlet全部運行在主程序操作系統(tǒng)進程的內(nèi)部,但它們被協(xié)作式地調(diào)度。

猴子補丁

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

而gevent庫中的猴子補?。╩onkey patch),gevent能夠修改標準庫里面大部分的阻塞式系統(tǒng)調(diào)用。這樣在不改變原有代碼的情況下,將應(yīng)用的阻塞式方法,變成協(xié)程式的(異步)。

代碼

from gevent import monkey
import gevent
import requests
import time


monkey.patch_all()

def req(url):
 print(url)
 resp = requests.get(url)
 print(resp.status_code,url)
def synchronous_times(urls):
 """同步請求運行時間"""
 start = time.time()
 for url in urls:
 req(url)
 end = time.time()
 print('同步執(zhí)行時間 {} s'.format(end-start))
def asynchronous_times(urls):
 """異步請求運行時間"""
 start = time.time()
 gevent.joinall([gevent.spawn(req,url) for url in urls])
 end = time.time()
 print('異步執(zhí)行時間 {} s'.format(end - start))
urls = ['https://book.douban.com/tag/小說','https://book.douban.com/tag/科幻',
 'https://book.douban.com/tag/漫畫','https://book.douban.com/tag/奇幻',
 'https://book.douban.com/tag/歷史','https://book.douban.com/tag/經(jīng)濟學']
synchronous_times(urls)
asynchronous_times(urls)

gevent:異步理論與實戰(zhàn)

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

一個greenlet遇到IO操作時,比如訪問網(wǎng)絡(luò),就自動切換到其他的greenlet,等到IO操作完成,再在適當?shù)臅r候切換回來繼續(xù)執(zhí)行。由于IO操作非常耗時,經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動切換協(xié)程,就保證總有g(shù)reenlet在運行,而不是等待IO。

串行和異步

高并發(fā)的核心是讓一個大的任務(wù)分成一批子任務(wù),并且子任務(wù)會被被系統(tǒng)高效率的調(diào)度,實現(xiàn)同步或者異步。在兩個子任務(wù)之間切換,也就是經(jīng)常說到的上下文切換。

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

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

import gevent
def foo():
 print('Running in foo')
 gevent.sleep(0)
 print('Explicit context switch to foo again')

def bar():
 print('Explicit context to bar')
 gevent.sleep(0)
 print('Implicit context switch back to bar')

gevent.joinall([
 gevent.spawn(foo),
 gevent.spawn(bar)
 ])

運行的順序:

Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar

同步異步的順序問題

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

代碼

import gevent
import random
def task(pid):
 """
 Some non-deterministic task
 """
 gevent.sleep(random.randint(0,2)*0.001)
 print('Task %s done' % pid)
#同步(結(jié)果更像串行)
def synchronous():
 for i in range(1,10):
 task(i)
#異步(結(jié)果更像亂步)
def asynchronous():
 threads = [gevent.spawn(task, i) for i in range(10)]
 gevent.joinall(threads)
print('Synchronous同步:')
synchronous()
print('Asynchronous異步:')
asynchronous()

輸出

Synchronous同步:
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Task 5 done
Task 6 done
Task 7 done
Task 8 done
Task 9 done
Asynchronous異步:
Task 1 done
Task 5 done
Task 6 done
Task 2 done
Task 4 done
Task 7 done
Task 8 done
Task 9 done
Task 0 done
Task 3 done

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

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

實戰(zhàn)

實現(xiàn)gevent到底怎么用,把異步訪問得到的數(shù)據(jù)提取出來。

在有道詞典搜索框輸入“hello”按回車。觀察數(shù)據(jù)請求情況 觀察有道的url構(gòu)建。

分析url規(guī)律

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

解析網(wǎng)頁數(shù)據(jù)

def fetch_word_info(word):
 url = "http://dict.youdao.com/w/eng/{}/".format(word)
 resp = requests.get(url,headers=headers)
 doc = pq(resp.text)
 pros = ''
 for pro in doc.items('.baav .pronounce'):
  pros+=pro.text()
 description = ''
 for li in doc.items('#phrsListTab .trans-container ul li'):
  description +=li.text()
 return {'word':word,'音標':pros,'注釋':description}

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

同步代碼

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey
gevent.monkey.patch_all()
words = ['good','bad','cool',
   'hot','nice','better',
   'head','up','down',
   'right','left','east']
def synchronous():
 start = time.time()
 print('同步開始了')
 for word in words:
  print(fetch_word_info(word))
 end = time.time()
 print("同步運行時間: %s 秒" % str(end - start))

#執(zhí)行同步
synchronous()

異步代碼

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey
gevent.monkey.patch_all()
words = ['good','bad','cool',
   'hot','nice','better',
   'head','up','down',
   'right','left','east']
def asynchronous():
 start = time.time()
 print('異步開始了')
 events = [gevent.spawn(fetch_word_info,word) for word in words]
 wordinfos = gevent.joinall(events)
 for wordinfo in wordinfos:
  #獲取到數(shù)據(jù)get方法
  print(wordinfo.get())
 end = time.time()
 print("異步運行時間: %s 秒"%str(end-start))
#執(zhí)行異步
asynchronous()

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

解決辦法

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

方法1

seqence = list(range(20))
size = 5 #子列表長度
output = [seqence[i:i+size] for i in range(0, len(seqence), size)]
print(output)

方法2

chunks = lambda seq, size: [seq[i: i+size] for i in range(0, len(seq), size)]
print(chunks(seq, 5))

方法3

def chunks(seq,size):
 for i in range(0,len(seq), size):
  yield seq[i:i+size]
prinT(chunks(seq,5))
 for x in chunks(req,5):
   print(x) 

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

動手實現(xiàn)

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey
gevent.monkey.patch_all()
words = ['good','bad','cool',
   'hot','nice','better',
   'head','up','down',
   'right','left','east']
def fetch_word_info(word):
 url = "http://dict.youdao.com/w/eng/{}/".format(word)
 resp = requests.get(url,headers=headers)
 doc = pq(resp.text)
 pros = ''
 for pro in doc.items('.baav .pronounce'):
  pros+=pro.text()
 description = ''
 for li in doc.items('#phrsListTab .trans-container ul li'):
  description +=li.text()
 return {'word':word,'音標':pros,'注釋':description}
def asynchronous(words):
 start = time.time()
 print('異步開始了')
 chunks = lambda seq, size: [seq[i: i + size] for i in range(0, len(seq), size)]
 for subwords in chunks(words,3):
  events = [gevent.spawn(fetch_word_info, word) for word in subwords]
  wordinfos = gevent.joinall(events)
  for wordinfo in wordinfos:
   # 獲取到數(shù)據(jù)get方法
   print(wordinfo.get())
  time.sleep(1)
  end = time.time()
 print("異步運行時間: %s 秒" % str(end - start))
asynchronous(words)

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 如何在python中用os模塊實現(xiàn)批量移動文件

    如何在python中用os模塊實現(xiàn)批量移動文件

    在工作中難免會遇到需要批量整理文件的情況,當需要從一堆文件中將部分文件批量地轉(zhuǎn)移時,如果手工一一轉(zhuǎn)移難免浪費時間,這篇文章主要給大家介紹了關(guān)于如何在python中用os模塊實現(xiàn)批量移動文件的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • Python打包exe時各種異常處理方案總結(jié)

    Python打包exe時各種異常處理方案總結(jié)

    今天教大家用Python打包exe時各種異常處理的方案總結(jié),下文中有非常詳細的介紹,對正在學習python的小伙伴們很有幫助喲,需要的朋友可以參考下
    2021-05-05
  • python求crc32值的方法

    python求crc32值的方法

    這篇文章主要介紹了python求crc32值的方法,包括相關(guān)的步驟及實現(xiàn)方法,需要的朋友可以參考下
    2014-10-10
  • python編寫暴力破解zip文檔程序的實例講解

    python編寫暴力破解zip文檔程序的實例講解

    下面小編就為大家分享一篇python編寫暴力破解zip文檔程序的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-04-04
  • Python讀取ini配置文件傳參的簡單示例

    Python讀取ini配置文件傳參的簡單示例

    這篇文章主要給大家介紹了關(guān)于Python讀取ini配置文件傳參的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • python 使用cx-freeze打包程序的實現(xiàn)

    python 使用cx-freeze打包程序的實現(xiàn)

    這篇文章主要介紹了python 使用cx-freeze打包程序的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • Python利用百度地圖獲取兩地距離(附demo)

    Python利用百度地圖獲取兩地距離(附demo)

    本文主要介紹了Python利用百度地圖獲取兩地距離,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-07-07
  • python中l(wèi)ist循環(huán)語句用法實例

    python中l(wèi)ist循環(huán)語句用法實例

    這篇文章主要介紹了python中l(wèi)ist循環(huán)語句用法,以實例形式詳細介紹了Python針對list的解析,包含各種常見的遍歷操作及原理分析,需要的朋友可以參考下
    2014-11-11
  • pymilvus?offset參數(shù)不生效解決示例

    pymilvus?offset參數(shù)不生效解決示例

    這篇文章主要為大家介紹了pymilvus?offset參數(shù)不生效解決示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • keras中的History對象用法

    keras中的History對象用法

    這篇文章主要介紹了keras中的History對象用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-06-06

最新評論

准格尔旗| 资溪县| 连南| 百色市| 搜索| 剑川县| 绥化市| 三原县| 霸州市| 云浮市| 广东省| 自贡市| 舟曲县| 松桃| 林周县| 崇州市| 广宁县| 桑植县| 日照市| 仙游县| 五大连池市| 漯河市| 东宁县| 宽甸| 从江县| 郴州市| 桑日县| 潍坊市| 岳普湖县| 吴堡县| 阳新县| 元阳县| 明星| 汪清县| 肥城市| 措美县| 搜索| 芮城县| 苍溪县| 棋牌| 龙井市|