Python分别用单线程,多线程,异步协程爬取一部小说,最快仅需要5s


本文运用了三种方式爬取一整部小说,分别运用了单线程爬取,多线程爬取和异步协程爬取。
小说网址:`

http://www.doupo321.com/doupocangqiong/`

网页很简单,也不用过多分析,内容都在网页源代码中,就是一个多级链接爬虫,步骤就是先爬取到网页下的子链接,然后通过子链接爬取到每章小说内容。
因为这个网页的源代码都很规整,所有我们用xpath来匹配,当然你熟悉正则或者bs4也可以用bs4来匹配。然后我们就开始写代码吧。

单线程爬取

# @Time:2022/1/1312:04
# @Author:中意灬
# @File:斗破2.py
# @ps:tutu qqnum:2117472285
import time
import requests
from lxml import etree
def download(url,title):#下载内容
    resp=requests.get(url)
    resp.encoding='utf-8'
    html=resp.text
    tree=etree.HTML(html)
    body = tree.xpath("/html/body/div/div/div[4]/p/text()")
    body = '\n'.join(body)
    with open(f'斗破2/{title}.txt',mode='w',encoding='utf-8')as f:
        f.write(body)
def geturl(url):#获取子链接
    resp=requests.get(url)
    resp.encoding='utf-8'
    html=resp.text
    tree=etree.HTML(html)
    lis=tree.xpath("/html/body/div[1]/div[2]/div[1]/div[3]/div[2]/ul/li")
    for li in lis:
        href=li.xpath("./a/@href")[0].strip('//')
        href="http://"+href
        title=li.xpath("./a/text()")[0]
        download(href,title)
if __name__ == '__main__':
    url="http://www.doupo321.com/doupocangqiong/"
    t1=time.time()
    geturl(url)
    t2=time.time()
    print("耗时:",t2-t1)

运行结果:
在这里插入图片描述

在这里插入图片描述

多线程爬取

# @Time:2022/1/1311:42
# @Author:中意灬
# @File:斗破1.py
# @ps:tutu qqnum:2117472285
import time
import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor
def download(url,title):
    resp=requests.get(url)
    resp.encoding='utf-8'
    html=resp.text
    tree=etree.HTML(html)
    body = tree.xpath("/html/body/div/div/div[4]/p/text()")
    body = '\n'.join(body)
    with open(f'斗破1/{title}.txt',mode='w',encoding='utf-8')as f:
        f.write(body)
def geturl(url):
    resp = requests.get(url)
    resp.encoding = 'utf-8'
    html = resp.text
    tree = etree.HTML(html)
    lis = tree.xpath("/html/body/div[1]/div[2]/div[1]/div[3]/div[2]/ul/li")
    return lis

if __name__ == '__main__':
    url="http://www.doupo321.com/doupocangqiong/"
    t1=time.time()
    lis=geturl(url)
    with ThreadPoolExecutor(1000)as t:#创建线程池,有1000个线程
        for li in lis:
            href = li.xpath("./a/@href")[0].strip('//')
            href = "http://" + href
            title = li.xpath("./a/text()")[0]
            t.submit(download,url=href,title=title)
    t2=time.time()
    print("耗时:",t2-t1)

运行结果:
在这里插入图片描述

在这里插入图片描述

异步协程爬取

# @Time:2022/1/1310:30
# @Author:中意灬
# @File:斗破.py
# @ps:tutu qqnum:2117472285
import requests
import aiohttp
import asyncio
import aiofiles
from lxml import etree
import time
async def download(url,title,session):
        async with session.get(url) as resp:#resp=requst.get()
            html= await resp.text()
            tree=etree.HTML(html)
            body=tree.xpath("/html/body/div/div/div[4]/p/text()")
            body='\n'.join(body)
        async with aiofiles.open(f'斗破/{title}.txt',mode='w',encoding='utf-8')as f:#保存下载内容
                await f.write(body)

async def geturl(url):
    resp=requests.get(url)
    resp.encoding='utf-8'
    html=resp.text
    tree=etree.HTML(html)
    lis=tree.xpath("/html/body/div[1]/div[2]/div[1]/div[3]/div[2]/ul/li")
    tasks=[]
    async with aiohttp.ClientSession() as session:#request
        for li in lis:
            href=li.xpath("./a/@href")[0].strip('//')
            href="http://"+href
            title=li.xpath("./a/text()")[0]
    # 插入异步操作
            tasks.append(asyncio.create_task(download(href,title,session)))
        await asyncio.wait(tasks)
if __name__ == '__main__':
    url="http://www.doupo321.com/doupocangqiong/"
    t1=time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(geturl(url))
    t2=time.time()
    print("耗时:",t2-t1)

运行结果:
在这里插入图片描述

在这里插入图片描述
因为没有进行排序,所以爬取出来的章节都是乱序的,大家可以写爬虫的时候里面自己设置一下标题,这样爬取出来的顺序就可能是有序的了。
我们可以看出,用多线程,仅仅5秒就扒完了一部1600多章的小说,但是多线程会对系统的开销较大;如果用异步协程,爬取速度会稍微慢些,需要大概20多秒,但是对系统开销较小,建议大家采用异步协程的方式,但是用单线程去爬取会慢很多,扒完一部小说耗时需要9分多钟,不是很推荐。

### YOLO项目中使用ThreadPoolExecutor实现多线程 在YOLO项目中,`ThreadPoolExecutor`可以用于加速图像预处理、推理以及后处理阶段的任务。通过合理配置线程池大小并利用Python标准库中的`concurrent.futures.ThreadPoolExecutor`模块,能够有效提升模型预测效率。 #### 配置线程池大小 对于大多数应用场景而言,并不是线程数量越多越好。过多的线程可能导致上下文切换开销增加,从而降低整体性能[^3]。因此,在初始化`ThreadPoolExecutor`实例时应谨慎设定最大工作线程数目: ```python from concurrent.futures import ThreadPoolExecutor, as_completed max_workers = min(32, (os.cpu_count() or 1) + 4) # 合理估算最优线程数 executor = ThreadPoolExecutor(max_workers=max_workers) ``` 这段代码片段展示了如何基于CPU核心数动态调整线程池规模,既不会浪费资源也不会因过度分配而影响程序运行效率。 #### 实现多线程任务提交与结果获取 当需要对一批图片执行检测操作时,可以通过向线程池提交异步任务的方式来充分利用计算资源。下面是一个简单的例子说明如何批量加载图片并将它们送入YOLO模型进行推断: ```python import os from PIL import Image from yolov5.models.experimental import attempt_load from yolov5.utils.general import non_max_suppression, scale_coords from yolov5.utils.torch_utils import select_device from yolov5.utils.datasets import letterbox def process_image(image_path): img0 = cv2.imread(image_path) # BGR img = letterbox(img0, new_shape=640)[0] # Convert img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416 img = np.ascontiguousarray(img) return image_path, img, img0 device = select_device('') model = attempt_load('yolov5s.pt', map_location=device) # 加载模型权重文件 image_paths = ["path/to/image1.jpg", "path/to/image2.jpg"] # 图片路径列表 futures_to_img = {executor.submit(process_image, path): path for path in image_paths} for future in as_completed(futures_to_img): try: image_path, img, im0 = future.result() # 将单张图片转换成batch形式输入给网络 img_tensor = torch.from_numpy(img).to(device) img_tensor = img_tensor.float() / 255.0 # 归一化 if img_tensor.ndimension() == 3: img_tensor = img_tensor.unsqueeze(0) pred = model(img_tensor)[0] det = non_max_suppression(pred, conf_thres=0.25, iou_thres=0.45)[0] if len(det): # Rescale boxes from img_size to im0 size det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round() # 打印识别结果 for *xyxy, conf, cls in reversed(det): label = f'{names[int(cls)]} {conf:.2f}' plot_one_box(xyxy, im0, label=label, color=colors(int(cls)), line_thickness=3) save_path = 'inference/output/' + os.path.basename(image_path) cv2.imwrite(save_path, im0) except Exception as exc: print('%r generated an exception: %s' % (future, exc)) ``` 上述代码实现了如下功能: - 定义了一个辅助函数`process_image()`负责读取和预处理每一张待测图片; - 创建了字典`futures_to_img`用来存储由`submit()`返回的对象及其对应的原始图片路径之间的映射关系; - 利用`as_completed()`迭代器监控所有已提交任务的状态变化情况,一旦某个任务完成则立即取出其结果继续后续流程; 此方式不提高了I/O密集型任务(如磁盘访问)的速度,同时也使得GPU资源得到了更充分的应用。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值