下面是关于“python协程gevent案例 爬取斗鱼图片过程解析”的完整攻略。
1. 什么是协程
协程是一种轻量级线程,Python的协程是基于生成器的协程。协程与线程的区别在于,线程是抢占式多任务,需要操作系统进行上下文切换,而协程是非抢占式多任务,通过协程程序员来控制何时上下文切换。
Python的协程一般使用yield关键字来实现,使用yield来挂起一个协程,使用next()函数继续一个挂起的协程。
2. 什么是gevent
gevent是一个基于协程的Python网络库,gevent封装了Python标准库中的socket库和subprocess库,使得在使用socket和subprocess时可以简化代码,避免出现多线程和多进程。
gevent使用Greenlet来实现协程,Greenlet是一个协程库,基于C实现。gevent使用Greenlet来实现协程,避免使用Python标准库的yield关键字,因此代码更加容易理解。
3. 爬取斗鱼图片过程解析
爬取斗鱼图片的过程可以分为以下几步:
- 获取斗鱼直播网站的页面。
- 解析页面,获取直播间地址。
- 进入直播间,获取直播间里的图片。
- 下载图片。
这个任务需要使用协程来完成,因为我们需要同时进行页面请求和图片下载,而这两个操作可能会有相互等待的状况。
下面是具体实现的代码:
import os
import gevent
import requests
from lxml import etree
from gevent import monkey
# 打补丁,让gevent能够在代码中发挥作用
monkey.patch_all()
def get_page(url):
"""
获取斗鱼房间列表页面
"""
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
}
response = requests.get(url, timeout=10, headers=headers)
if response.status_code == 200:
return response.text
except Exception as e:
print('failed to load page: %s' % url)
return None
def parse_page(page_source):
"""
解析页面,获取直播间地址
"""
try:
html_obj = etree.HTML(page_source)
room_list = html_obj.xpath('//div[@class="ListContent"]/ul[@class="clearfix"]/li')
room_list = list(filter(lambda x: x.xpath('descendant::a[contains(@class, "Title")]'), room_list))
room_urls = [room.xpath('descendant::a[contains(@class, "Title")]/@href')[0] for room in room_list]
print('found %s room urls' % len(room_urls))
return room_urls
except Exception as e:
print('failed to parse page')
return []
def download_image(image_url, image_path):
"""
下载图片
"""
try:
response = requests.get(image_url, timeout=10)
if response.status_code == 200:
with open(image_path, 'wb') as f:
f.write(response.content)
print('downloaded image: %s' % image_path)
except Exception as e:
print('failed to download image: %s' % image_url)
def download_room_images(room_url, img_dir):
"""
下载一个直播间内的所有图片
"""
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
}
response = requests.get(room_url, timeout=10, headers=headers)
if response.status_code == 200:
html_obj = etree.HTML(response.text)
images = html_obj.xpath('//div[@class="layout-Module-container"]/div/div[@class="swiper-wrapper"]/div[@class="swiper-slide"]/a/div/img/@data-original')
for image in images:
image_path = os.path.join(img_dir, image.split('/')[-1])
gevent.spawn(download_image, image, image_path)
except Exception as e:
print('failed to download images in room %s' % room_url)
def download_images(base_url):
"""
下载每个直播间内的图片
"""
page_source = get_page(base_url)
if page_source:
room_urls = parse_page(page_source)
if room_urls:
img_dir = os.path.join(os.getcwd(), 'images')
os.makedirs(img_dir, exist_ok=True)
for room_url in room_urls:
gevent.spawn(download_room_images, room_url, img_dir)
# 测试下载斗鱼直播的图片
if __name__ == "__main__":
download_images('https://www.douyu.com/g_qqcp') # 以QQ飞车直播房间为例
gevent.joinall()
这个代码会下载斗鱼直播网站下面的QQ飞车直播房间的图片。在代码中,我们使用了gevent来实现协程,使用Greenlet来挂起和恢复协程的执行,从而实现了异步爬取和下载。
我们通过gevent.spawn和gevent.joinall来创建和管理多个协程,多个协程可以在想象象的资源(例如CPU,网络IO)中自由切换,因此可以大大提高爬取效率。
4. 示例说明
在上述代码中,有两个例子可以展现协程和gevent的用法:
例子一
def download_image(image_url, image_path):
"""
下载图片
"""
try:
response = requests.get(image_url, timeout=10)
if response.status_code == 200:
with open(image_path, 'wb') as f:
f.write(response.content)
print('downloaded image: %s' % image_path)
except Exception as e:
print('failed to download image: %s' % image_url)
这个函数下载单张图片。我们通过gevent.spawn来创建一个协程来下载这张图片,调用这个函数的线程不会被阻塞,可以立即返回,而协程会在下载完成之后自动恢复执行。
例子二
for room_url in room_urls:
gevent.spawn(download_room_images, room_url, img_dir)
这段代码会下载每个直播间内的图片,我们使用gevent.spawn来创建从位置下载每个直播间的协程,这样当我们使用gevent.joinall()来等待所有协程执行完成时,这些协程会被异步执行,从而大大提高爬虫的效率。
这就是一个完整的“python协程gevent案例 爬取斗鱼图片过程解析”的攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:python协程gevent案例 爬取斗鱼图片过程解析 - Python技术站