提升requests模塊的爬取效率


一、提升requests模塊的爬取效率

  1、多線程和多進程(不建議使用)

  2、線程池或進程池(適當使用)

  3、單線程+異步協程(爬蟲推薦使用)

二、單線程。爬取某視頻到本地

import re
import time
import random
import requests
from lxml import etree

start_time = time.time()

url = "https://www.pearvideo.com/category_3"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}

ex = 'srcUrl="(.*?)",vdoUrl=srcUrl'

def request_video(url):
    """
    向視頻鏈接發送請求
    """
    return requests.get(url=url, headers=headers).content

def save_video(content):
    """
    將視頻的二進制數據保存到本地
    """
    video_name = str(random.randint(100, 999)) + ".mp4"
    with open(video_name, 'wb') as f:
        f.write(content)

        
# 獲取首頁源碼
page_text = requests.get(url=url, headers=headers).text

tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@class="listvideo-list clearfix"]/li')

video_url_list = list()
for li in li_list:
    detail_url = "https://www.pearvideo.com/" + li.xpath('./div/a/@href')[0]
    
    # 獲取該視頻頁面的源碼
    detail_page_text = requests.get(url=detail_url, headers=headers).text
    
    # 正則匹配視頻的URL
    video_url = re.findall(ex, detail_page_text, re.S)[0]
    video_url_list.append(video_url)
    
    content = request_video(video_url)
    save_video(content)


print("執行耗時: ", time.time() - start_time)

 

三、線程池或進程池。爬取某視頻到本地

import re
import time
import random
import requests
from lxml import etree
from multiprocessing.dummy import Pool


start_time = time.time()

url = "https://www.pearvideo.com/category_3"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}

ex = 'srcUrl="(.*?)",vdoUrl=srcUrl'

def request_video(url):
    """
    向視頻鏈接發送請求
    """
    return requests.get(url=url, headers=headers).content

def save_video(content):
    """
    將視頻的二進制數據保存到本地
    """
    video_name = str(random.randint(100, 999)) + ".mp4"
    with open(video_name, 'wb') as f:
        f.write(content)

        
# 獲取首頁源碼
page_text = requests.get(url=url, headers=headers).text

tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@class="listvideo-list clearfix"]/li')

video_url_list = list()
for li in li_list:
    detail_url = "https://www.pearvideo.com/" + li.xpath('./div/a/@href')[0]
    
    # 獲取該視頻頁面的源碼
    detail_page_text = requests.get(url=detail_url, headers=headers).text
    
    # 正則匹配視頻的URL
    video_url = re.findall(ex, detail_page_text, re.S)[0]
    video_url_list.append(video_url)
    
pool = Pool(4)
    
#使用線程池將視頻的二進制數據下載下來
content_list = pool.map(request_video, video_url_list)
    
# 使用線程池將視頻的二進制數據保存到本地
pool.map(save_video, content_list)    

print("執行耗時: ", time.time() - start_time)

四、單線程+異步協程。

  1、單線程  

import time

start_time = time.time()

def request(url):
    print("正在下載", url)
    time.sleep(2)
    print("下載完成", url)

url_list = [
    "https://www.baidu.com",
    "https://www.taobao.com",
    "https://www.jd.com"
]

for url in url_list:
    request(url)

print("執行耗時:", time.time() - start_time)

  2、進程池或線程池

import time
from multiprocessing.dummy import Pool

start_time = time.time()

def request(url):
    print("正在下載", url)
    time.sleep(2)
    print("下載完成", url)

url_list = [
    "https://www.baidu.com",
    "https://www.taobao.com",
    "https://www.jd.com"
]

pool = Pool(3)

pool.map(request, url_list)

print("執行耗時:", time.time() - start_time)

  3、協程

   a、協程相關的概念

  - event_loop:事件循環,相當於一個無限循環,我們可以把一些函數注冊到這個事件循環上,當滿足某些條件的時候,函數就會被循環執行。程序是按照設定的順序從頭執行到尾,運行的次數也是完全按照設定。當在編寫異步程序時,必然其中有部分程序的運行耗時是比較久的,需要先讓出當前程序的控制權,讓其在背后運行,讓另一部分的程序先運行起來。當背后運行的程序完成后,也需要及時通知主程序已經完成任務可以進行下一步操作,但這個過程所需的時間是不確定的,需要主程序不斷的監聽狀態,一旦收到了任務完成的消息,就開始進行下一步。loop就是這個持續不斷的監視器。

  - coroutine:中文翻譯叫協程,在 Python 中常指代為協程對象類型,我們可以將協程對象注冊到事件循環中,它會被事件循環調用。我們可以使用 async 關鍵字來定義一個方法,這個方法在調用時不會立即被執行,而是返回一個協程對象。

  - task:任務,它是對協程對象的進一步封裝,包含了任務的各個狀態。

  - future:代表將來執行或還沒有執行的任務,實際上和 task 沒有本質區別。

  - 另外我們還需要了解 async/await 關鍵字,它是從 Python 3.5 才出現的,專門用於定義協程。其中,async 定義一個協程,await 用來使掛起阻塞方法的執行。

  b、創建一個協程對象,只需要在函數名前加上一個async關鍵字即可 

import time

async def request():
    print("正在下載")
    time.sleep(2)
    print("下載完成")

c = request()

  c、基本使用

import time
import asyncio


async def request():
    print("正在下載")
    time.sleep(2)
    print("下載完成")

# 創建一個協程對象
c = request()

# 創建一個事件循環對象
loop = asyncio.get_event_loop()


# 1. 將協程對象注冊到事件循環中
# 2. 執行事件循環
loop.run_until_complete(c)

  d、task的使用

import time
import asyncio


async def request():
    print("下載成功~~")

# 創建一個協程對象
c = request()

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

# 通過事件循環對象創建一個任務, 並將協程對象封裝進這個任務里面 //作用:可以監聽它的狀態
task = loop.create_task(c)
print(task)

# 1. 將協程對象注冊到事件循環中
# 2. 執行事件循環
loop.run_until_complete(task)
print(task)

  e、future的使用

import time
import asyncio


async def request():
    print("下載成功~~")

# 創建一個協程對象
c = request()

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

# 通過future創建任務,就不再依附於loop對象,通過asyncio模塊來創建
future = asyncio.ensure_future(c)
print(future)

# 1. 將協程對象注冊到事件循環中
# 2. 執行事件循環
loop.run_until_complete(future)
print(future)

  f、回調函數

import time
import asyncio

def call_back(res):
    # 通過res.result()就可以接收到requests發送請求返回的響應數據
    print("在這里進行數據解析", res.result())
    

async def request():
    print("下載成功~~")
    return "永靈大神"

# 創建一個協程對象
c = request()

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

# 通過future創建任務,就不再依附於loop對象,通過asyncio模塊來創建
future = asyncio.ensure_future(c)

# 通過future定義一個回調函數,這樣的話,協程對象的返回值就可以傳遞給我們的回調函數
future.add_done_callback(call_back)


# 1. 將協程對象注冊到事件循環中
# 2. 執行事件循環
loop.run_until_complete(future)

  g、回調函數(真正發送請求)

import time
import requests
import asyncio

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}

def call_back(res):
    # 通過res.result()就可以接收到requests發送請求返回的響應數據
    page_text = res.result()
    print("在這里進行數據解析", page_text)
    

async def request():
    return requests.get(url="https://www.baidu.com", headers=headers).text

# 創建一個協程對象
c = request()

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

# 通過future創建任務,就不再依附於loop對象,通過asyncio模塊來創建
future = asyncio.ensure_future(c)

# 通過future定義一個回調函數,這樣的話,協程對象的返回值就可以傳遞給我們的回調函數
future.add_done_callback(call_back)


# 1. 將協程對象注冊到事件循環中
# 2. 執行事件循環
loop.run_until_complete(future)

 五、多任務異步協程

  1、多任務異步協程基本使用  

import time
import asyncio

start_time = time.time()

# 特殊函數
async def request(url):
    print("正在下載: ", url)
    time.sleep(2)  # time模塊為非異步的模塊,在整個異步的代碼中,如果出現了非異步的模塊,則會讓整個異步失去效果
    print("下載完成: ", url)
    
url_list = [
    "https://www.baidu.com",
    "https://www.taobao.com",
    "https://www.jd.com"
]

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

task_list = list()
for url in url_list:
    c = request(url)
    
    # 創建一個任務對象
    task = asyncio.ensure_future(c)
    task_list.append(task)
    
# 如果傳過來的是一個列表的話,需要再加上一層封裝, 使用asyncio.wait()方法進行封裝
loop.run_until_complete(asyncio.wait(task_list))
    
print("執行耗時:", time.time() - start_time)

  2、多任務異步協程(解決方案)

import time
import asyncio

start_time = time.time()

# 特殊函數
async def request(url):
    print("正在下載: ", url)
#     time.sleep(2)  # time模塊為非異步的模塊,在整個異步的代碼中,如果出現了非異步的模塊,則會讓整個異步失去效果
    await asyncio.sleep(2)
    print("下載完成: ", url)
    
url_list = [
    "https://www.baidu.com",
    "https://www.taobao.com",
    "https://www.jd.com"
]

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

task_list = list()
for url in url_list:
    c = request(url)
    
    # 創建一個任務對象
    task = asyncio.ensure_future(c)
    task_list.append(task)
    
# 如果傳過來的是一個列表的話,需要再加上一層封裝, 使用asyncio.wait()方法進行封裝
loop.run_until_complete(asyncio.wait(task_list))
    
print("執行耗時:", time.time() - start_time)

  3、將多任務異步協程應用到爬蟲中

import time
import asyncio
import requests

start_time = time.time()

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}

# 特殊函數
async def request(url):
    print("正在下載: ", url)
    # requests模塊為非異步的請求模塊,如果使用它,將會使整個異步失去效果
    page_text = requests.get(url=url, headers=headers).text
    print("返回的數據", page_text)
    print("下載完成: ", url)
    
url_list = [
    "http://127.0.0.1:5000/tiger",
    "http://127.0.0.1:5000/tom",
    "http://127.0.0.1:5000/jay"
]

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

task_list = list()
for url in url_list:
    c = request(url)
    
    # 創建一個任務對象
    task = asyncio.ensure_future(c)
    task_list.append(task)
    
# 如果傳過來的是一個列表的話,需要再加上一層封裝, 使用asyncio.wait()方法進行封裝
loop.run_until_complete(asyncio.wait(task_list))
    
print("執行耗時:", time.time() - start_time)

  4、將多任務異步協程應用到爬蟲中(解決方案)

import time
import asyncio
import aiohttp
import requests

start_time = time.time()

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}

# 特殊函數
async def request(url):
    print("正在下載: ", url)
    # 此session非requests.Session, 這個session支持異步的網絡並發請求
    
    async with aiohttp.ClientSession() as session:
        async with await session.get(url=url) as response:
            page_text = await response.text()  # text()頁面源碼 read()二進制數據 json()
            print("返回的數據", page_text)
            
    print("下載完成: ", url)

url_list = [
    "http://127.0.0.1:5000/tiger",
    "http://127.0.0.1:5000/tom",
    "http://127.0.0.1:5000/jay"
]

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

task_list = list()
for url in url_list:
    c = request(url)
    
    # 創建一個任務對象
    task = asyncio.ensure_future(c)
    task_list.append(task)
    
# 如果傳過來的是一個列表的話,需要再加上一層封裝, 使用asyncio.wait()方法進行封裝
loop.run_until_complete(asyncio.wait(task_list))
    
print("執行耗時:", time.time() - start_time)

  5、如何實現數據解析(任務的綁定回調機制)

import time
import asyncio
import aiohttp
import requests

start_time = time.time()

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}

def call_back(task):
    print("在這里進行數據解析")
    print("I am call_back")
    print(task.result())

# 特殊函數
async def request(url):
    # 此session非requests.Session, 這個session支持異步的網絡並發請求
    
    async with aiohttp.ClientSession() as session:
        async with await session.get(url=url) as response:
            page_text = await response.text()  # text()頁面源碼 read()二進制數據 json()
            print("返回的數據", page_text)
            return page_text

url_list = [
    "http://127.0.0.1:5000/tiger",
    "http://127.0.0.1:5000/tom",
    "http://127.0.0.1:5000/jay"
]

# 創建一個事件循環對象
loop = asyncio.get_event_loop()

task_list = list()
for url in url_list:
    c = request(url)
    
    # 創建一個任務對象
    task = asyncio.ensure_future(c)
    task.add_done_callback(call_back)
    task_list.append(task)
    
# 如果傳過來的是一個列表的話,需要再加上一層封裝, 使用asyncio.wait()方法進行封裝
loop.run_until_complete(asyncio.wait(task_list))
    
print("執行耗時:", time.time() - start_time)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM