日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

【第四章-2】Python爬虫教程(协程,多任务异步协程,aiohttp模块,异步爬虫实战:爬取《西游记》全部章节内容)

發布時間:2023/12/16 python 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【第四章-2】Python爬虫教程(协程,多任务异步协程,aiohttp模块,异步爬虫实战:爬取《西游记》全部章节内容) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本課程共五個章節,課程地址:

【Python爬蟲教程】花9888買的Python爬蟲全套教程2021完整版現分享給大家!(已更新項目)——附贈課程與資料_嗶哩嗶哩_bilibili


第四章

  • 本章內容梗概
  • 多線程
  • 多進程
  • 線程池和進程池
  • 抓取廣州江南果菜批發市場菜價
  • 協程
  • 多任務異步協程?
  • aiohttp模塊?
  • 異步爬蟲實戰:扒光一本電子書
  • 綜合訓練:抓取一部電影
  • 目錄

    第四章

    (六)協程?

    (七)多任務異步協程

    python編寫協程的程序

    官方推薦寫法

    在爬蟲領域的應用

    (八)aiohttp模塊

    安裝

    代碼框架

    例子

    (九)異步爬蟲實戰:扒光一本電子書

    思路

    第一步:同步操作

    第二步:異步操作


    (六)協程?

    協程能夠更加高效的利用CPU

    其實,我們能夠高效的利用多線程來完成爬蟲已經很厲害了。但是,從某種角度講,線程的執行效率真的就無敵了嗎?我們真的充分利用CPU資源了嗎?非也

    比如,我們來看下面這個例子。我們單獨的用一個線程來完成某一個操作,看看它的效率是否真的能把CPU完全利用起來?

    import timedef func():print("我愛黎明")time.sleep(3) # 讓當前的線程處于阻塞狀態,CPU是不為我工作的print("我真的愛黎明")if __name__ == '__main__':func()# 其他能讓線程處于阻塞狀態的語句(一般情況下,當程序處于IO操作的時候,線程都會處于阻塞狀態) # input() 輸入 # requests.get() 發送請求(在網絡請求返回數據之前,程序也是處于阻塞狀態的)

    在該程序中,我們的func()實際在執行的時候至少需要3s的時間來完成操作,中間的3s需要讓我當前的線程處于阻塞狀態,阻塞狀態的線程CPU是不會來執行你的,那么此時CPU很可能會切換到其他程序上去執行。此時,對于你來說,在這3s內CPU并沒有為你工作。那么我們能不能通過某種手段,讓CPU一直為我工作,盡量不要去管其他人?

    我們知道CPU一般拋開執行周期不談,如果一個線程遇到了IO操作,CPU就會自動地切換到其他線程進行執行。那么,如果我想辦法讓我的線程遇到了IO操作就掛起,留下的都是運算操作,那CPU是不是就會長時間的來照顧我

    以此為目的,程序員就發明了一個新的執行過程:當線程中遇到了IO操作的時候,將線程中的任務進行切換,切換成非IO操作,等原來的IO執行完了,再恢復到原來的任務中?

    協程:當程序遇見了IO操作(費時不費力的操作)的時候,可以選擇性的切換到其他任務上(程序完成的,不是操作系統完成的)

    • 在微觀上是一個任務一個任務的進行切換,切換條件一般就是IO操作
    • 在宏觀上,我們能看到的其實是多個任務一起在執行(多任務異步操作)

    上方所講的一切,都是在單線程的條件下


    (七)多任務異步協程

    python編寫協程的程序

    import asyncioasync def func():print("你好啊,我叫賽利亞")if __name__ == '__main__':# print(func()) 此時拿到的是一個協程對象,和生成器差不多,該函數默認是不會這樣執行的g = func() # 此時的函數是異步協程函數,函數執行得到的是一個協程對象asyncio.run(g) # 協程程序運行需要asyncio模塊的支持

    但上面這個代碼是單任務的,下面寫一個多任務的:

    import asyncio import timeasync def func1():print("你好啊,我叫潘金蓮")time.sleep(3)print("你好啊,我叫潘金蓮")async def func2():print("你好啊,我叫王建國")time.sleep(2)print("你好啊,我叫王建國")async def func3():print("你好啊,我叫李雪琴")time.sleep(4)print("你好啊,我叫李雪琴")if __name__ == '__main__':f1 = func1() # 此時的函數是異步協程函數,函數執行得到的是一個協程對象f2 = func2()f3 = func3()# 把三個任務統一放到列表里去tasks = [f1,f2,f3]t1 = time.time() # 執行之前記錄一下時間# 一次性啟動多個任務(協程)asyncio.run(asyncio.wait(tasks)) # 協程程序運行需要asyncio模塊的支持t2 = time.time() # 執行之后記錄一下時間print(t2-t1) 異步操作,但跑出來的是同步效果

    異步效果的:

    await:當該任務被掛起后,CPU會自動切換到其他任務中?

    import asyncio import timeasync def func1():print("你好啊,我叫潘金蓮")# time.sleep(3) # 當程序出現了同步操作的時候,異步就中斷了 還有如requests.get()也會造成阻塞,碰到的時候也要換成對應的異步操作的代碼await asyncio.sleep(3) # 異步操作的代碼print("你好啊,我叫潘金蓮")async def func2():print("你好啊,我叫王建國")# time.sleep(2)await asyncio.sleep(2)print("你好啊,我叫王建國")async def func3():print("你好啊,我叫李雪琴")# time.sleep(4)await asyncio.sleep(4)print("你好啊,我叫李雪琴")if __name__ == '__main__':f1 = func1() # 此時的函數是異步協程函數,函數執行得到的是一個協程對象f2 = func2()f3 = func3()# 把三個任務統一放到列表里去tasks = [ # 協程任務列表f1,f2,f3 # 創建協程任務]t1 = time.time() # 執行之前記錄一下時間# 一次性啟動多個任務(協程)asyncio.run(asyncio.wait(tasks)) # 協程程序運行需要asyncio模塊的支持t2 = time.time() # 執行之后記錄一下時間print(t2-t1)

    官方推薦寫法:

    import asyncio import timeasync def func1():print("你好啊,我叫潘金蓮")await asyncio.sleep(3) # 異步操作的代碼print("你好啊,我叫潘金蓮")async def func2():print("你好啊,我叫王建國")await asyncio.sleep(2)print("你好啊,我叫王建國")async def func3():print("你好啊,我叫李雪琴")await asyncio.sleep(4)print("你好啊,我叫李雪琴")async def main(): # 協程函數# 第一種寫法# f1 = func1()# await f1 # 一般await掛起操作放在協程對象前面# 第二種寫法(推薦)tasks = [ # 組成列表func1(),func2(),func3()]await asyncio.wait(tasks) # 一次性把所有任務都執行if __name__ == '__main__':t1 = time.time()asyncio.run(main()) # run協程對象t2 = time.time()print(t2-t1)

    python3.8之后的版本運行上述代碼時可能會有警告,將代碼稍作修改:

    # py3.8之后需要我們手動將協程對象包裝成task對象tasks = [asyncio.create_task(func1()), # py3.8以后加上asyncio.create_task()asyncio.create_task(func2()),asyncio.create_task(func3())]await asyncio.wait(tasks)

    在爬蟲領域的應用:

    import asyncio# 在爬蟲領域的應用 async def download(url):print("準備開始下載")await asyncio.sleep(2) # 模擬網絡請求 不能寫requests.get()print("下載完成")async def main():urls = ["http://www.baidu.com","http://www.bilibili.com","http://www.163.com"]tasks = []for url in urls:d = download(url) # 協程對象tasks.append(d)await asyncio.wait(tasks)if __name__ == '__main__':asyncio.run(main())

    python3.8之后的版本運行上述代碼時可能會有警告,將代碼稍作修改:

    # 準備異步協程對象列表tasks = []for url in urls:d = asycio.create_task(download(url))tasks.append(d)tasks = [asyncio.create_task(download(url)) for url in urls] # 這么干也行哦~

    多線程和異步一個是程序執行模式,一個是IO模式。協程是單線程,事實上只是節省了CPU切棧的時間


    (八)aiohttp模塊

    requests.get() 是同步的代碼,如何換成異步的操作?? ? 借助?模塊aiohttp(第三方庫,需安裝)

    aiohttp是python的一個非常優秀的第三方異步http請求庫,我們可以用aiohttp來編寫異步爬蟲(協程)?

    安裝:

    pip install aiohttp --trusted-host pypi.tuna.tsinghua.edu.cn

    代碼框架:

    import asyncio import aiohttpurls = ["","" ]# 異步下載 async def aiodownload(url):passasync def main():tasks = [] # 添加下載任務for url in urls:tasks.append(aiodownload(url))await asyncio.wait(tasks) # 等待所有任務下載完成if __name__ == '__main__':asyncio.run(main())

    例子:?

    【唯美壁紙】桌面壁紙唯美小清新_唯美手機壁紙_電腦桌面壁紙高清唯美大全 - 優美圖庫

    import asyncio # 異步協程 import aiohttp # 異步協程的http請求urls = [ # 復制圖片地址"http://kr.shanghai-jiuxin.com/file/2020/1031/191468637cab2f0206f7d1d9b175ac81.jpg","http://kr.shanghai-jiuxin.com/file/2020/1031/563337d07af599a9ea64e620729f367e.jpg","http://kr.shanghai-jiuxin.com/file/2020/1031/774218be86d832f359637ab120eba52d.jpg" ]async def aiodownload(url):# 發送請求 ——> 得到圖片內容 ——> 保存到文件'''s = aiohttp.ClientSession() 等價于 requestsrequests.get() .post()s.get() .post()'''name = url.rsplit("/", 1)[1] # 從右邊切, 切一次. 得到[1]位置的內容# 有了with會自動closeasync with aiohttp.ClientSession() as session: # session對象相當于requests對象async with session.get(url) as resp: # 發送請求,相當于resp = requests.get()# 請求回來了. 寫入文件# 寫入文件可以自己去學習一個模塊aiofileswith open(name, mode="wb") as f: # 創建文件# resp.content.read() 等價于 resp.content 讀取圖片# resp.text() 等價于 resp.text 讀取文本,如頁面源代碼# resp.json() 等價于 resp.json() 讀取jsonf.write(await resp.content.read()) # 讀取內容是異步的. 需要await掛起print(name, "搞定")async def main():tasks = []for url in urls:tasks.append(aiodownload(url))await asyncio.wait(tasks)if __name__ == '__main__':asyncio.run(main())

    運行后能爬下圖片,但是代碼會報錯:?

    將? asyncio.run() 改為 asyncio.get_event_loop().run_until_complete(main()) 即可。具體可參考下面這個帖子:

    【Python自學筆記】asyncio.run()報錯RuntimeError:Event loop is closed的原因以及解決辦法_xiaoqiangclub的博客-CSDN博客_asyncio.run

    if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

    從最終運行的結果中能非常直觀地看到用異步IO完成爬蟲的效率明顯高了很多?


    (九)異步爬蟲實戰:扒光一本電子書

    百度小說

    右鍵 ——> 查看網頁源代碼,如圖:

    說明頁面的數據是通過 ajax 異步操作返回的數據(第二次加載的),故 f12 ,點擊頁面上 “全部章節(共100章)” 后的 “查看全部”

    # 所有章節的內容(名稱、cid) # 同步的方式即可 https://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4306063500"}

    隨便點進某一回的鏈接,如圖:

    # 章節內部的內容 # 需要異步(100個章節即100個任務) https://dushu.baidu.com/api/pc/getChapterContent?data={"book_id":"4306063500","cid":"4306063500|1569782244","need_bookinfo":1}

    思路:

  • 同步操作:訪問getCatolog? 拿到所有章節的cid和名稱
  • 異步操作:訪問getChapterContent? 下載所有的文章內容
  • 第一步:同步操作

    怎么拿cid?

    import requestsdef getCatalog(url):resp = requests.get(url)dic = resp.json()for item in dic['data']['novel']['items']: # item就是對應每一個章節的名稱和cidtitle = item['title']cid = item['cid']print(title,cid)if __name__ == '__main__':b_id = "4306063500"url = 'http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"' + b_id + '"}'getCatalog(url)

    第二步:異步操作

    每一個cid就是一個異步任務?

    如何獲取章節內容?

    代碼運行前,先創建一個文件夾,并把文件夾標記成 Excluded

    # http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4306063500"} => 所有章節的內容(名稱, cid) # http://dushu.baidu.com/api/pc/getChapterContent?data={"book_id":"4306063500","cid":"4306063500|11348571","need_bookinfo":1} => 章節內部的內容import requests import asyncio import aiohttp import aiofiles # 異步的文件讀寫 import json""" 1. 同步操作: 訪問getCatalog 拿到所有章節的cid和名稱 2. 異步操作: 訪問getChapterContent 下載所有的文章內容 """async def aiodownload(cid, b_id, title):data = {"book_id":b_id,"cid":f"{b_id}|{cid}","need_bookinfo":1}data = json.dumps(data) # 將對象變為json字符串url = f"http://dushu.baidu.com/api/pc/getChapterContent?data={data}"async with aiohttp.ClientSession() as session: # 準備好sessionasync with session.get(url) as resp: # session發送請求dic = await resp.json() # 從發送的請求里讀取jsonasync with aiofiles.open(title, mode="w", encoding="utf-8") as f:await f.write(dic['data']['novel']['content']) # 把小說內容寫出async def getCatalog(url):# 同步(此時還沒有其他任務會和該任務一起并行執行,所以完全沒必要用異步)resp = requests.get(url)dic = resp.json()# 得到要的內容之后,再異步tasks = []for item in dic['data']['novel']['items']: # item就是對應每一個章節的名稱和cidtitle = item['title']cid = item['cid']# 準備異步任務tasks.append(aiodownload(cid, b_id, title))await asyncio.wait(tasks)if __name__ == '__main__':b_id = "4306063500" # 百度小說的書籍idurl = 'http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"' + b_id + '"}' # 首頁urlasyncio.get_event_loop().run_until_complete(getCatalog(url))

    運行后,將產生的文件全部移入之前建立好的文件夾里

    怎么一次性選中多個文件?

    左鍵選中一個文件,按住Ctrl+Shift,選中想要范圍內的文件

    ??

    總結

    以上是生活随笔為你收集整理的【第四章-2】Python爬虫教程(协程,多任务异步协程,aiohttp模块,异步爬虫实战:爬取《西游记》全部章节内容)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。