Python 爬虫线程

免责声明:自本文章发布起, 本文章仅供参考,不得转载,不得复制等操作。浏览本文章的当事人如涉及到任何违反国家法律法规造成的一切后果由浏览本文章的当事人自行承担与本文章博客主无关。以及由于浏览本文章的当事人转载,复制等操作涉及到任何违反国家法律法规引起的纠纷和造成的一切后果由浏览本文章的当事人自行承担与本文章博客主无关。

1. 基础知识

爬取请求过程中遇到阻塞, 采用高性能异步爬虫爬取数据.

异步爬虫方式:

  1. 多线程, 多进程.
    1.1 好处: 可以为每一个阻塞操作开启单独的线程或者进程, 实现异步进行.
    1.2 弊端: 不可以无限制的开启多进程或者多线程, 开启过多系统的开销大, 也会降低爬取速度.
  2. 线程池, 进程池. (适当使用)
    2.1 好处: 降低了系统对线程或者进程创建和销毁的频率, 降低系统的开销.
    2.2 弊端: 池中的线程或者进程的数量有上限.
  3. 单线程 + 异步协程. (推荐)

协程相关知识点:

  1. event_loop: 事件循环, 相当子一个无限循环, 可以把一些函数注册到这个事件循环上, 当满足某些条件的时候, 函数就会被循环执行.
  2. coroutine: 协程对象, 我们可以将协程对象注册到事件循环中, 它会被事件循环调用. 可以使用 async 关键字来定义一个方法, 这个方法在调用时不会立即被执行, 而是返回一个协程对象.
  3. task: 任务, 它是对协程对象的进一步封装, 包含了任务的各个状态.
  4. future: 代表将来执行或还没有执行的任务, 实际上和 task 没有本质区别. async 定义一个协程.
  5. await: 用来挂起阻塞方法的执行.

2. 线程基本使用

单线程串行方式, 未使用线程池.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# 导包
import time


# 发送请求
def get_page(page):
    print("开始加载: %d" % page)
    time.sleep(2)
    print("加载完成: %d" % page)


if __name__ == '__main__':
    # pages
    pages = [1, 2, 3, 4]

    # 开始时间
    start_time = time.time()

    # 开始加载
    for p in pages:
        get_page(p)

    # 结束时间
    end_time = time.time()

    # 总时间
    print("总时间: %d s" % (end_time - start_time))

在这里插入图片描述

使用线程池方式执行

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# 导包
from multiprocessing.dummy import Pool
import time


# 发送请求
def pool_page(page):
    print("开始加载: %d" % page)
    time.sleep(2)
    print("加载完成: %d" % page)


if __name__ == '__main__':

    # 对象
    pages = 4

    # 计算运行时间
    start_time = time.time()

    # 实例化线程对象
    pool = Pool(4)
    # 将 list 的每一个元素传递给 pool_page(page) 处理
    pool_map = pool.map(pool_page, range(1, pages + 1))
    # 关闭线程
    pool.close()

    # 总时间
    print("总时间: %d s" % (time.time() - start_time))

在这里插入图片描述

3. 协程基本使用

基础使用

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import asyncio


# async 修饰的函数
async def request_url(url):
    print("正在请求 " + url)
    print("成功请求 " + url)


if __name__ == '__main__':
    # 得到协程对象
    coroutine = request_url("www.baidu.com")

    # 创建一个事件循环对象
    loop = asyncio.get_event_loop()

    # 将协议对象注册到 loop 中, 然后启动 loop
    loop.run_until_complete(coroutine)

在这里插入图片描述

task 的基本使用.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import asyncio


# async 修饰的函数
async def request_url(url):
    print("正在请求 " + url)
    print("成功请求 " + url)


if __name__ == '__main__':
    # 得到协程对象
    coroutine = request_url("www.baidu.com")

    # 创建一个事件循环对象
    loop = asyncio.get_event_loop()

    # 创建 task 对象
    task = loop.create_task(coroutine)
    print(task)

    # 将 task 对象注册到 loop 中, 然后启动 loop
    loop.run_until_complete(task)
    print(task)

在这里插入图片描述

future 任务的基本使用. 和 task 创建对象有区别.
区别在于 task 对象由时间循环创建.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import asyncio


# async 修饰的函数
async def request_url(url):
    print("正在请求 " + url)
    print("成功请求 " + url)


if __name__ == '__main__':
    # 得到协程对象
    coroutine = request_url("www.baidu.com")

    # 创建一个事件循环对象
    loop = asyncio.get_event_loop()

    # 创建 task 对象
    task = asyncio.ensure_future(coroutine)
    print(task)

    # 将 task 对象注册到 loop 中, 然后启动 loop
    loop.run_until_complete(task)
    print(task)

在这里插入图片描述

绑定回调 的基本使用

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import asyncio


# async 修饰的函数
async def request_url(url):
    print("正在请求 " + url)
    print("成功请求 " + url)
    return url


# 回调函数
def callback(callback_task):
    # result() 方法是任务中封装的协程对象对应函数的返回值
    print(callback_task.result())


if __name__ == '__main__':
    # 得到协程对象
    coroutine = request_url("www.baidu.com")

    # 创建一个事件循环对象
    loop = asyncio.get_event_loop()

    # 创建 task 对象
    task = asyncio.ensure_future(coroutine)

    # 将回调对象绑定在任务对象中, task 成功后完成前看是回调该函数.
    task.add_done_callback(callback)

    # 将 task 对象注册到 loop 中, 然后启动 loop
    loop.run_until_complete(task)

在这里插入图片描述

4. 多任务协程

需要注意:

  1. 异步协程中出现同步模块相关的代码, 则无法进行异步.
  2. 当 asyncio 遇到阻塞操作必须进行手动挂起.
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import asyncio
import time


# async 修饰的函数
async def request_url(url):
    print("正在请求 " + url)
    # 异步协程中出现同步模块相关的代码, 则无法进行异步
    # time.sleep(2)
    # 当 asyncio 遇到阻塞操作必须进行手动挂起
    await asyncio.sleep(2)
    print("成功请求 " + url)


if __name__ == '__main__':

    # 开始时间
    start_time = time.time()

    # 请求的 url
    urls = [
        "https://www.baidu.com",
        "https://www.sougou.com/",
        "https://blog.csdn.net/"
    ]

    # 任务列表
    tasks = []
    for u in urls:
        # 得到协程对象
        coroutine = request_url(u)
        # 创建任务对象
        task = asyncio.ensure_future(coroutine)
        # 添加任务对象
        tasks.append(task)

    # 创建一个事件循环对象
    loop = asyncio.get_event_loop()

    # 将 tasks 对象封装到 wait 中后注册到 loop 中, 然后启动 loop
    loop.run_until_complete(asyncio.wait(tasks))

    # 总时间
    print("总时间: %d s" % (time.time() - start_time))


在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值