day6--高性能的异步爬虫

目的

在爬虫中使用异步实现高性能的数据爬取操作

异步爬虫的方法

  1. 多线程,多进程(不建议)

    • 好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行
    • 弊端:无法无限制的开启多线程或者多进程
  2. 线程池,进程池(适当使用)

    • 好处:可以降低系统对进程或者线程创建和销毁的一个频率,从而很好的降低系统的开销
    • 弊端:池中线程或者进程的数量是有上限。
    • 实例:爬出梨视频数据
from multiprocessing import Pool
import requests
from lxml import etree


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

#对下述url发起请求解析出视频详情页的url和视频名称
url = 'https://www.pearvideo.com/category_5'
page_text = requests.get(url=url,headers=headers).text

tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@id="listvideoListUl"]/li')
urls_list = []   #视频url地址和名称
for li in li_list:
    detai_url = 'https://www.pearvideo.com/' + li.xpath('./div/a/@href')[0]
    name = li.xpath('./div/a/div[2]/text()')[0]
    contId =li.xpath('./div/a/@href')[0]
    contId = contId.split('_')[1]

    #发起请求获得视频的地址
    new_url = 'https://www.pearvideo.com/videoStatus.jsp'
    param = {
        'contId':contId
    }
    headers = {
        'Referer':detai_url
    }
    response = requests.get(url=new_url,headers=headers,params=param)
    response = response.json()
    video_url = response['videoInfo']['videos']['srcUrl']
    dic = {
        'name':name,
        'url':video_url
    }
    urls_list.append(dic)

#对视频链接发起请求获取视频的二进制数据,然后将视频进行返回
def get_video_data(dic):
    url = dic['url']
    print(url)
    data = requests.get(url=url,headers=headers).content
    video_path = dic['name'] + '.mp4'
    with open(video_path,'wb') as f:
        f.write(data)

#使用线程池对视频数据进行请求

if __name__  == '__main__':
    pool = Pool(4)
    pool.map(get_video_data,urls_list)
  1. 单线程+异步协程(推荐)
  • event_Loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。

  • coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用,我们可以使用 async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。

  • task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。

  • future:代表将来执行或还没有执行的任务,实际上和task没有本质区别

  • async:定义一个协程

  • await:用来挂起阻塞方法的执行。

import asyncio

async def request(url):
    print('正在请求:',url)
    print('请求成功',url)
    return url

#async修饰的函数,调用之后返回的一个协程对象
c = request('www.baidu.com')

# #创建一个时间循环对象
# loop = asyncio.get_event_loop()
# #将协程对象注册到loop中,然后启动loop
# loop.run_until_complete(c)

# #task的使用
# loop = asyncio.get_event_loop()
# #基于loop创建一个task对象
# task = loop.create_task(c)
# print(task)  #打印协程状态
# #将task注册到循环事件中
# loop.run_until_complete(task)
# print(task)

#future的使用
# loop = asyncio.get_event_loop()
# task = asyncio.ensure_future(c)
# print(task)
# loop.run_until_complete(task)
# print(task)


#假设该函数为一个回调函数
def callback_func(task):
    #result返回的就是任务对象中封装的协程对象对应函数的返回值
    print(task.result())

#绑定回调
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(c)
#将回调函数绑定到任务对象中
task.add_done_callback(callback_func)
loop.run_until_complete(task)

多任务协程

import asyncio
import time


async def request(url):
    print('正在下载',url)
    #在异步协程中如果出现同步模块相关的代码,那么就无法实现异步
    # time.sleep(2) 属于同步代码
    #当在saycio中遇到阻塞操作必须进行手动挂起
    await asyncio.sleep(2)
    print('下载完成',url)


start = time.time()
urls = [
    'www.baidu.com',
    'www.sougou.com',
    'www.bandoujia.com'
]

#任务列表:多放多个任务对象
stasks = []
for url in urls:
    c = request(url)
    task = asyncio.ensure_future(c)
    stasks.append(task)
    
loop = asyncio.get_event_loop()
#需要将任务列表封装到wait中
loop.run_until_complete(asyncio.wait(stasks))
print(time.time()-start)

单线程异步爬虫练习


##############服务端代码#############
from flask import Flask
import time

app = Flask(__name__)


@app.route('/bobo')
def index_bobo():
    time.sleep(2)
    return 'hello bobo'

@app.route('/jay')
def index_jay():
    time.sleep(2)
    return 'hello jay'

@app.route('/tom')
def index_tom():
    time.sleep(2)
    return 'hello tom'


if __name__  == '__main__':
    app.run(threaded=True)



#####################运行代码######################
import requests
import asyncio
import time
import aiohttp

start = time.time()

urls = [
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/jay',
    'http://127.0.0.1:5000/tom'
]

async def get_page(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            #test()返回字符串形式的响应数据
            #read()返回二进制形式的响应数据
            #json()返回json形式的响应数据
            #注意:获取响应数据操作之前一定要使用await进行手动挂起
            page_text = await response.text()
            print(page_text)


# async def get_page(url):
#     print('正在下载',url)
#     # response = requests.get(url=url)   #request.get是基于同步的,必须使用基于异步的网络请求模块进行指定url发送
#     #aiohttp:基于异步网络请求的模块
#     print('下载完成',response.text)

tasks = []

for url in urls:
    c = get_page(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
endtime = time.time()

print('总耗时:',endtime-start)

###############################结果#########################
hello jay
hello bobo
hello tom
总耗时: 2.011115074157715

Process finished with exit code 0



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值