<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          送書 | 兩百四十多萬字,六百章的小說秒爬完

          共 12930字,需瀏覽 26分鐘

           ·

          2021-09-03 22:15


          大家好!啃書君。

          相信很多人喜歡在空閑的時間里看小說,甚至有小部分人為了追小說而熬夜看,那么問題來了,喜歡看小說的小伙伴在評論區(qū)告訴我們?yōu)槭裁聪矚g看小說,今天我們手把手教你使用異步協(xié)程20秒爬完兩百四十多萬字,六百章的小說,讓你一次看個夠。

          在爬取之前我們先來簡單了解一下什么是同步,什么是異步協(xié)程?

          同步與異步

          同步

          同步是有序,為了完成某個任務(wù),在執(zhí)行的過程中,按照順序一步一步執(zhí)行下去,直到任務(wù)完成。

          爬蟲是IO密集型任務(wù),我們使用requests請求庫來爬取某個站點時,網(wǎng)絡(luò)順暢無阻塞的時候,正常情況如下圖所示:

          但在網(wǎng)絡(luò)請求返回數(shù)據(jù)之前,程序是處于阻塞狀態(tài)的,程序在等待某個操作完成期間,自身無法繼續(xù)干別的事情,如下圖所示:

          當(dāng)然阻塞可以發(fā)生在站點響應(yīng)后的執(zhí)行程序那里,執(zhí)行程序可能是下載程序,大家都知道下載是需要時間的。

          當(dāng)站點沒響應(yīng)或者程序卡在下載程序的時候,CPU一直在等待而不去執(zhí)行其他程序,那么就白白浪費(fèi)了CPU的資源,導(dǎo)致我們的爬蟲效率很低。

          異步

          異步是一種比多線程高效得多的并發(fā)模型,是無序的,為了完成某個任務(wù),在執(zhí)行的過程中,不同程序單元之間過程中無需通信協(xié)調(diào),也能完成任務(wù)的方式,也就是說不相關(guān)的程序單元之間可以是異步的。如下圖所示:

          當(dāng)請求程序發(fā)送網(wǎng)絡(luò)請求1并收到某個站點的響應(yīng)后,開始執(zhí)行程序中的下載程序,由于下載需要時間或者其他原因使處于阻塞狀態(tài),請求程序和下載程序是不相關(guān)的程序單元,所以請求程序發(fā)送下一個網(wǎng)絡(luò)請求,也就是異步。

          • 微觀上異步協(xié)程是一個任務(wù)一個任務(wù)的進(jìn)行切換,切換條件一般就是IO操作;
          • 宏觀上異步協(xié)程是多個任務(wù)一起在執(zhí)行;

          注意:上面我們所講的一切都是在單線程的條件下實現(xiàn)。

          請求庫

          我們發(fā)送網(wǎng)絡(luò)請求一定要用到請求庫,Python從多個HTTP客戶端中,最常用的請求庫莫過于requests、aiohttp、httpx。

          在不借助其他第三方庫的情況下,requests只能發(fā)送同步請求;aiohttp只能發(fā)送異步請求;httpx既能發(fā)送同步請求,又能發(fā)送異步請求。

          接下來我們將簡單講解這三個庫。

          requests庫

          相信大家對requests庫不陌生吧,requests庫簡單、易用,是python爬蟲使用最多的庫。

          在命令行中運(yùn)行如下代碼,即可完成requests庫的安裝:

          pip install requests

          使用requests發(fā)送網(wǎng)絡(luò)請求非常簡單,

          在本例中,我們使用get網(wǎng)絡(luò)請求來獲取百度首頁的源代碼,具體代碼如下:

          import requests
          headers={
              'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
          }
          response=requests.get('https://baidu.com',headers=headers)
          response.encoding='utf-8'
          print(response.text)

          運(yùn)行部分結(jié)果如下圖:

          首先我們導(dǎo)入requests庫,創(chuàng)建請求頭,請求頭中包含了User-Agent字段信息,也就是瀏覽器標(biāo)識信息,如果不加這個,網(wǎng)站就可能禁止抓取,然后調(diào)用get()方法發(fā)送get請求,傳入的參數(shù)為URL鏈接和請求頭,這樣簡單的網(wǎng)絡(luò)請求就完成了。

          這里我們返回打印輸出的是百度的源代碼,大家可以根據(jù)需求返回輸出其他類型的數(shù)據(jù)。

          需要注意的是:

          百度源代碼的head部分的編碼為:utf-8,如下圖所示:

          我們利用requests庫的方法來查看默認(rèn)的編碼類型是什么,具體代碼如下所示:

          import requests
          url = 'https://www.baidu.com'
          response = requests.get(url)
          print(response.encoding)  

          運(yùn)行結(jié)果為:ISO-8859-1

          由于默認(rèn)的編碼類型不同,所以需要更改輸出的編碼類型,更改方式也很簡單,只需要在返回數(shù)據(jù)前根據(jù)head部分的編碼來添加以下代碼即可:

          response.encoding='編碼類型'

          除了使用get()方法實現(xiàn)get請求外,還可以使用post()、put()、delete()等方法來發(fā)送其他網(wǎng)絡(luò)請求,在這里就不一一演示了,關(guān)于更多的requests網(wǎng)絡(luò)請求庫用法可以到官方參考文檔進(jìn)行查看,我們今天主要講解可以發(fā)送異步請求的aiohttp庫和httpx庫。

          asyncio模塊

          在講解異步請求aiohttp庫和httpx庫請求前,我們需要先了解一下協(xié)程。

          協(xié)程是一種比線程更加輕量級的存在,讓單線程跑出了并發(fā)的效果,對計算資源的利用率高,開銷小的系統(tǒng)調(diào)度機(jī)制。

          Python中實現(xiàn)協(xié)程的模塊有很多,我們主要來講解asyncio模塊,從asyncio模塊中直接獲取一個EventLoop的引用,把需要執(zhí)行的協(xié)程放在EventLoop中執(zhí)行,這就實現(xiàn)了異步協(xié)程。

          協(xié)程通過async語法進(jìn)行聲明為異步協(xié)程方法,await語法進(jìn)行聲明為異步協(xié)程可等待對象,是編寫asyncio應(yīng)用的推薦方式,具體示例代碼如下:

          import asyncio
          import time
          async def function1():
              print('I am Superman?。?!')
              await asyncio.sleep(3)
              print('function1')

          async def function2():
              print('I am Batman!?。?)
              await asyncio.sleep(2)
              print('function2')

          async def function3():
              print('I am iron man?。?!')
              await asyncio.sleep(4)
              print('function3')
              
          async def Main():
              tasks=[
                  asyncio.create_task(function1()),
                  asyncio.create_task(function2()),
                  asyncio.create_task(function3()),
              ]
              await asyncio.wait(tasks)
              
          if __name__ == '__main__':
              t1=time.time()
              asyncio.run(Main())
              t2=time.time()
              print(t2-t1)

          運(yùn)行結(jié)果為:

          I am Superman?。?!
          I am Batman!??!
          I am iron man!?。?br>function2
          function1
          function3
          4.0091118812561035

          首先我們用了async來聲明三個功能差不多的方法,分別為function1,function2,function3,在方法中使用了await聲明為可等待對象,并使用asyncio.sleep()方法使函數(shù)休眠一段時間。

          再使用async來聲明Main()方法,通過調(diào)用asyncio.create_task()方法將方法封裝成一個任務(wù),并把這些任務(wù)存放在列表tasks中,這些任務(wù)會被自動調(diào)度執(zhí)行;

          最后通過asyncio.run()運(yùn)行協(xié)程程序。

          注意:當(dāng)協(xié)程程序出現(xiàn)了同步操作的時候,異步協(xié)程就中斷了。

          例如把上面的示例代碼中的await asyncio.sleep()換成time.time(),運(yùn)行結(jié)果為:

          I am Superman?。。?br>function1
          I am Batman?。?!
          function2
          I am iron man?。。?br>function3
          9.014737844467163

          所以在協(xié)程程序中,盡量不使用同步操作。

          好了,asyncio模塊我們講解到這里,想要了解更多的可以進(jìn)入asyncio官方文檔進(jìn)行查看。

          aiohttp庫

          aiohttp是基于asyncio實現(xiàn)的HTTP框架,用于HTTP服務(wù)器和客戶端。安裝方法如下:

          pip install aiohttp

          aiohttp只能發(fā)送異步請求,示例代碼如下所示:

          import aiohttp
          import asyncio
          headers={
              'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
          }
          async def Main():
              async with aiohttp.ClientSession() as session:
                  async with session.get('https://www.baidu.com',headers=headers) as response:
                      html = await response.text()
                      print(html)
          loop=asyncio.get_event_loop()
          loop.run_until_complete(Main())

          運(yùn)行結(jié)果和前面介紹的requests網(wǎng)絡(luò)請求一樣,如下圖所示:

          大家可以對比requests網(wǎng)絡(luò)請求發(fā)現(xiàn),其實aiohttp.ClientSession() as session相當(dāng)于將requests賦給session,也就是說session相當(dāng)于requests,而發(fā)送網(wǎng)絡(luò)請求、傳入的參數(shù)、返回響應(yīng)內(nèi)容都和requests請求庫大同小異,只是aiohttp請求庫需要用async和await進(jìn)行聲明,然后調(diào)用asyncio.get_event_loop()方法進(jìn)入事件循環(huán),再調(diào)用loop.run_until_complete(Main())方法運(yùn)行事件循環(huán),直到Main方法運(yùn)行結(jié)束。

          注意:在調(diào)用Main()方法時,不能使用下面這條語句:

          asyncio.run(Main())

          雖然會得到想要的響應(yīng),但是會報錯:RuntimeError: Event loop is closed錯誤。

          我們還可以在返回的內(nèi)容中指定解碼方式或編碼方式,例如:

          await response.text(encoding='utf-8')

          或者選擇不編碼,讀取圖像:

          await resp.read()

          好了aiohttp請求庫我們學(xué)到這里,想要了解更多的可以到pypi官網(wǎng)進(jìn)行學(xué)習(xí)。

          httpx請求庫

          在前面我們簡單地講解了requests請求庫和aiohttp請求庫,requests只能發(fā)送同步請求,aiohttp只能發(fā)送異步請求,而httpx請求庫既可以發(fā)送同步請求,又可以發(fā)送異步請求,而且比上面兩個效率更高。

          安裝方法如下:

          pip install httpx

          httpx請求庫——同步請求

          使用httpx發(fā)送同步網(wǎng)絡(luò)請求也很簡單,與requests代碼重合度99%,只需要把requests改成httpx即可正常運(yùn)行。

          具體示例代碼如下:

          import httpx
          headers={
              'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
          }
          response=httpx.get('https://www.baidu.com',headers=headers)
          print(response.text)

          運(yùn)行結(jié)果如下圖所示:

          注意:httpx使用的默認(rèn)utf-8進(jìn)行編碼來解碼響應(yīng)。

          httpx請求庫——同步請求高級用法

          當(dāng)發(fā)送請求時,httpx必須為每個請求建立一個新連接(連接不會被重用),隨著對主機(jī)的 請求數(shù)量增加,網(wǎng)絡(luò)請求的效率就是變得很低。

          這時我們可以用Client實例來使用HTTP連接池,這樣當(dāng)我們主機(jī)發(fā)送多個請求時,Client將重用底層的TCP連接,而不是為重新創(chuàng)建每個請求。

          with模塊用法如下:

          with httpx.Client() as client:    ...

          我們把Client作為上下文管理器,并使用with塊,當(dāng)執(zhí)行完with語句時,程序會自動清理連接。

          當(dāng)然我們可以使用.close()顯式關(guān)閉連接池,用法如下:

          client = httpx.Client()
          try:
              ...
          finally:
              client.close()

          為了我們的代碼更簡潔,我們推薦使用with塊寫法,具體示例代碼如下:

          import httpx
          headers={
              'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
          }
          with httpx.Client(headers=headers)as client:
              response=client.get('https://www.baidu.com')
              print(response.text)

          其中httpx.Client()as client相當(dāng)于把httpx的功能傳遞給client,也就是說示例中的client相當(dāng)于httpx,接著我們就可以使用client來調(diào)用get請求。

          注意:我們傳遞的參數(shù)可以放在httpx.Client()里面,也可以傳遞到get()方法里面。

          httpx請求庫——異步請求

          要發(fā)送異步請求時,我們需要調(diào)用AsyncClient,具體示例代碼如下:

          import httpx
          import asyncio
          headers={
              'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
          }
          async def function():
              async with httpx.AsyncClient()as client:
                  response=await client.get('https://www.baidu.com',headers=headers)
                  print(response.text)
          if __name__ == '__main__':
              loop = asyncio.get_event_loop()
              loop.run_until_complete(function())

          運(yùn)行結(jié)果為:

          首先我們導(dǎo)入了httpx庫和asyncio模塊,使用async來聲明function()方法并用來聲明with塊的客戶端打開和關(guān)閉,用await來聲明異步協(xié)程可等待對象response。接著我們調(diào)用asyncio.get_event_loop()方法進(jìn)入事件循環(huán),再調(diào)用loop.run_until_complete(function())方法運(yùn)行事件循環(huán),直到function運(yùn)行結(jié)束。

          好了,httpx請求庫講解到這里,想要了解更多的可以到httpx官方文檔進(jìn)行學(xué)習(xí),接下來我們正式開始爬取小說。

          實戰(zhàn)演練

          接下來我們將使用requests請求庫同步和httpx請求庫的異步,兩者結(jié)合爬取17k小說網(wǎng)里面的百萬字小說,利用XPath來做相應(yīng)的信息提取。

          Xpath小技巧

          在使用Xpath之前,我們先來介紹使用Xpath的小技巧。

          技巧一:快速獲取與內(nèi)容匹配的Xpath范圍。

          我們可以將鼠標(biāo)移動到我們想要獲取到內(nèi)容div的位置并右擊選擇copy,如下圖所示:

          這樣我們就可以成功獲取到內(nèi)容匹配的Xpath范圍了。

          技巧二:快速獲取Xpath范圍匹配的內(nèi)容。

          當(dāng)我們寫好Xpath匹配的范圍后,可以通過Chrome瀏覽器的小插件Xpath Helper,該插件的安裝方式很簡單,在瀏覽器應(yīng)用商店中搜索Xpath Helper,點擊添加即可,如下圖所示:

          使用方法也很簡單,如下圖所示:

          首先我們點擊剛剛添加的插件,然后把已經(jīng)寫好的Xpath范圍寫到上圖2的方框里面,接著Xpath匹配的內(nèi)容將出現(xiàn)在上圖3方框里面,接著被匹配內(nèi)容的背景色全部變成了金色,那么我們匹配內(nèi)容就一目了然了。

          這樣我們就不需要每寫一個Xpath范圍就運(yùn)行一次程序查看匹配內(nèi)容,大大提高了我們效率。

          獲取小說章節(jié)名和鏈接

          首先我們選取爬取的目標(biāo)小說,并打開開發(fā)者工具,如下圖所示:

          我們通過上圖可以發(fā)現(xiàn),<div class="Main List"存放著我們所有小說章節(jié)名,點擊該章節(jié)就可以跳轉(zhuǎn)到對應(yīng)的章節(jié)頁面,所以可以使用Xpath來通過這個div來獲取到我們想要的章節(jié)名和URL鏈接。

          由于我們獲取的章節(jié)名和URL鏈接的網(wǎng)絡(luò)請求只有一個,直接使用requests請求庫發(fā)送同步請求,主要代碼如下所示:

          async def get_link(url):
              response=requests.get(url)
              response.encoding='utf-8'
              Xpath=parsel.Selector(response.text)
              dd=Xpath.xpath('/html/body/div[5]')
              for a in dd:
                  #獲取每章節(jié)的url鏈接
                  links=a.xpath('./dl/dd/a/@href').extract()
                  linklist=['https://www.17k.com'+link for link in links]
                  #獲取每章節(jié)的名字
                  names=a.xpath('./dl/dd/a/span/text()').extract()
                  namelist=[name.replace('\n','').replace('\t',''for name in names]
                  #將名字和url鏈接合并成一個元組
                  name_link_list=zip(namelist,linklist)

          首先我們用async聲明定義的get_text()方法使用requests庫發(fā)送get請求并把解碼方式改成'utf-8',接著使用parsel.Selector()方法將文本構(gòu)成Xpath解析對象,最后我們將獲取到的URL鏈接和章節(jié)名合并成一個元組。

          獲取到URL鏈接和章節(jié)名后,需要構(gòu)造一個task任務(wù)列表來作為異步協(xié)程的可等待對象,具體代碼如下所示:

          task=[]
          for name,link in name_link_list:
           task.append(get_text(name,link))
          await asyncio.wait(task)

          我們創(chuàng)建了一個空列表,用來存放get_text()方法,并使用await調(diào)用asyncio.wait()方法保存創(chuàng)建的task任務(wù)。

          獲取每章節(jié)的小說內(nèi)容

          由于需要發(fā)送很多個章節(jié)的網(wǎng)絡(luò)請求,所以我們采用httpx請求庫來發(fā)送異步請求。

          主要代碼如下所示:

          async def get_text(name,link):
              async with httpx.AsyncClient() as client:
                  response=await client.get(link)
                  html=etree.HTML(response.text)
                  text=html.xpath('//*[@id="readArea"]/div[1]/div[2]/p/text()')
                  await save_data(name,text)

          首先我們將上一步的獲取到的章節(jié)名和URL鏈接傳遞到用async聲明定義的get_text()方法,使用with塊調(diào)用httpx.AsyncClient()方法,并使用await來聲明client.get()是可等待對象,然后使用etree模塊來構(gòu)造一個XPath解析對象并自動修正HTML文本,將獲取到的小說內(nèi)容和章節(jié)名傳入到自定義方法save_data中。

          保存小說內(nèi)容到text文本中

          好了,我們已經(jīng)把章節(jié)名和小說內(nèi)容獲取下來了,接下來就要把內(nèi)容保存在text文本中,具體代碼如下所示:

          async def save_data(name,text):
              f=open(f'小說/{name}.txt','w',encoding='utf-8')
              for texts in text:
                  f.write(texts)
                  f.write('\n')
                  print(f'正在爬取{name}')

          老規(guī)矩,首先用async來聲明save_data()協(xié)程方法save_data(),然后使用open()方法,將text文本文件打開并調(diào)用write()方法把小說內(nèi)容寫入文本中。

          最后調(diào)用asyncio.get_event_loop()方法進(jìn)入事件循環(huán),再調(diào)用loop.run_until_complete(get_link())方法運(yùn)行事件循環(huán),直到function運(yùn)行結(jié)束。具體代碼如下所示:

          url='https://www.17k.com/list/2536069.html'
          loop = asyncio.get_event_loop()
          loop.run_until_complete(get_link(url))

          結(jié)果展示

          好了,異步爬蟲爬取小說就講解到這里了,感謝觀看?。?!



          送書

          又到了每周三的送書時刻,今天給大家?guī)淼氖恰?span style="outline: 0px;font-family: Arial, "microsoft yahei";font-size: 16px;font-weight: 700;letter-spacing: 0.544px;">Python Django Web從入門到項目實戰(zhàn)(視頻版)》

          Python 的 Django 框架是目前流行的一款重量級網(wǎng)站開發(fā)框架,具備簡單易學(xué)、搭建快速、功能強(qiáng)大等特點。

          本書從簡單的 HTML、CSS、JavaScript 開始介紹,再到 Django 的基礎(chǔ)知識,融入了大量的代碼案例、重點提示、圖片展示,做到了手把手教授。

          本書基于 Django 3.0.7 版本、Python 3.8.5 版本、Rest Framework 3.11.1 版本、Vue.js 2.6.10 版本、數(shù)據(jù)庫 MySQL 8.0 版本進(jìn)行講解。

          本書還提供了一個商業(yè)級別的項目案例,采用目前主流的前后端分離開發(fā)技術(shù),以便讀者可以體驗正式項目的開發(fā)過程。

          熟練掌握本書內(nèi)容后,讀者將達(dá)到中級 Web 項目開發(fā)工程師的技術(shù)水平。



          公眾號回復(fù):送書 ,參與抽獎(共3本)

          點擊下方回復(fù):送書  即可!




          大家如果有什么建議,歡迎掃一掃二維碼私聊小編~
          回復(fù):加群 可加入Python技術(shù)交流群


          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天色人人 | 综合AV| 可以看美女操逼视频的网站 | 国产精品嫩草影院久久久 | 亚洲中文字幕无码视频在线观看 |