1. 程式人生 > >利用協程asyncio爬取搜狗美女圖片(二)——實戰

利用協程asyncio爬取搜狗美女圖片(二)——實戰

上節我們詳細的介紹了asyncio庫的應用(連結https://blog.csdn.net/MG1723054/article/details/81778460),本節我們將其應用到實戰之中。主要還是以分析ajax爬取搜狗美女圖片(連結https://blog.csdn.net/MG1723054/article/details/81735834

直接貼出程式碼,我們在程式碼裡面詳細說明每一行的程式碼含義。

我們以爬取前25個網頁,首先我們再次將之前的沒有新增協程的程式碼放上

# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a NJUer.
"""
import requests
import time 
from urllib.parse import urlencode  #網址編碼
import json  #匯入json庫
urls=[]
def image_json (url) :###請求庫,利用requests請求構造的連結,然後轉化為json格式,然後得到圖######片的標題和圖片連結。
      response=requests.get(url,headers={'User-Agent':'Mozilla/5.0'})
      data=json.loads(response.text)['all_items']
      for m in range(len(data)) :
           items={
                       'image_url':data[m]['thumbUrl'],'title':data[m]['title']
                       }
      
           
           yield items
      
def  image_download(item):###下載圖片
      resource=requests.get(item['image_url'])
      item['title']=item['title'].replace('|','_')
      item['title']=item['title'].replace('/','_')###改名,因為有些圖片中有些字元不符合jpg
###圖片命名規範
      file='C:\\Users\\FangWei\\Desktop\\網路爬蟲\\爬取酷狗美女圖片\\'+item['title'][0:20]+'.jpg'
      with open (file,'wb') as f:
            f.write(resource.content)###將圖片下載,放到指定資料夾
def get_image (offest) :   ###get_image函式主要是構造需要的ajax連結
    base_url='http://pic.sogou.com/pics/channel/getAllRecomPicByTag.jsp?'
    data={'category':'美女',
                  'tag':'全部',
                  'start':str(offest*15),
                  'len':'15',}
    url=base_url+urlencode(data)  ###利用urlencode將字典拼接為一個網址連結
    return url  
def main(offest):
      infor=get_image(offest)  #mian函式內部呼叫get_image函式
    #time.sleep(1)
      for item in  image_json (infor):
           image_download(item)          
if __name__=='__main__' :
      start=time.time()
      for x in range(1,26):   #設定爬取變數,設定30,根據上面分析表示可以爬取30*15張圖片         
          offest=x
          main(offest)           #呼叫主函式main()
      end=time.time()
      times=end-start
      print(times)
          
            
            
            

執行時間為: 

可以看到,爬取完這些網頁所消耗的時間還是比較多的

下面,我們將該程式修改,使其變為單執行緒協程併發,以此來提高效率。

# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a NJUer.
"""
import requests
import time ,json
from urllib.parse import urlencode 
import asyncio,aiohttp
urls=[]
def image_json (url) :
      response=requests.get(url,headers={'User-Agent':'Mozilla/5.0'})
      data=json.loads(response.text)['all_items']
      for m in range(len(data)) :
           items={
                       'image_url':data[m]['thumbUrl'],'title':data[m]['title']
                       }
      
           
           yield items ##利用生成器,與return類似,但是yield可節省記憶體,實際上yield也可作協程
      
async def  image_download(item):
      item['title']=item['title'].replace('|','_')
      item['title']=item['title'].replace('/','_')###資料命名處理,在爬取中發現有的字元不符合jpg命名規範
      file='C:\\Users\\FangWei\\Desktop\\網路爬蟲\\爬取搜狗美女圖\\'+item['title'][0:20]+'.jpg'####命名檔名
      async with aiohttp.ClientSession() as session:
          async with session.get(item['image_url']) as resp:###aiohttp模組中ClientSession方法,這兩句方法最穩妥,也有session=aiohttp.ClientSession(),resp=session.get(item['image_url']),但是可能會報錯,如果不報錯,可以使用這種方法,報錯就使用上面的程式碼
          
              #print(resp.status)
              imgcode=await resp.read()####讀取二進位制檔案,這與requests庫不同,requests讀######取二進位制的方法是content
      with open(file,'wb')as f:
          f.write(imgcode)   ####將二進位制檔案寫入檔案
def get_image (offest) :
    base_url='http://pic.sogou.com/pics/channel/getAllRecomPicByTag.jsp?'
    data={'category':'美女',
                  'tag':'全部',
                  'start':str(offest*15),
                  'len':'15',}
    url=base_url+urlencode(data)
    return url  
def main(offest):
      infor=get_image(offest)
    #time.sleep(1)
      #for item in  image_json (infor):
           #image_download(item)    
      tasks=[asyncio.ensure_future(image_download(item)) for item in image_json (infor) ]     ###開啟協程多工佇列,該語句是列表推導式,列表的簡寫,與上面兩句等效,但是該句是利用協程,多個佇列一起進行
      loop=asyncio.get_event_loop()  
      loop.run_until_complete(asyncio.wait(tasks))###將任務註冊到事件迴圈,並啟動任務
if __name__=='__main__' :
      start=time.time()
      for x in range(1,26):
          
          offest=x
          main(offest)
      end=time.time()
      times=end-start
      print(times)
          
            
            
            
          
      

上面的程式碼執行結束,執行時間為:

我們可以明顯的看到,通過協程併發我們執行時間縮短了一半多,所以我們在實際爬取過程中可以適當的使用協程。

 原創不易,如需轉載,請註明出處和作者,謝謝。