Python线程的创建、执行和管理以及注意事项

106 篇文章 0 订阅
14 篇文章 0 订阅

亿牛云代理
Python提供了多种方法来创建、执行和管理线程,并且需要注意线程安全性和性能方面的问题。其中使用threading模块创建线程,并获取其执行的函数返回值的方法有:

  1. 使用concurrent.futures模块:提供了高级API,可以将返回值和异常从工作线程传递到主线程。但可能比使用threading模块更耗费资源。
  2. 使用multiprocessing.pool模块:提供了类似的接口,可以使用进程或线程池,并使用apply_async方法异步地执行函数并获取结果。但需要序列化和传递数据,而且不能共享内存。
  3. 使用可变对象作为参数传递给线程的构造器,并让线程将其结果存储在该对象的指定位置。但可能会导致竞争条件。
  4. 使用Thread的子类:重写run和join方法,使得join方法可以返回目标函数的返回值。但需要访问一些私有的数据结构。

在选择方法时,需要考虑具体需求和场景。以下是需要注意的一些方面:

  1. concurrent.futures模块可以简化线程的创建和管理,但可能比使用threading模块更耗费资源。
  2. multiprocessing.pool模块可以利用多核处理器并行执行函数,但需要序列化和传递数据,而且不能共享内存。
  3. 使用可变对象作为参数传递给线程可能会导致竞争条件,即多个线程同时修改同一个对象,造成数据不一致或错误。
  4. 使用Thread的子类来返回目标函数的返回值可能会破坏Thread的原有设计,而且需要访问一些私有的数据结构。
  5. Python的线程受到全局解释器锁(GIL)的限制,即在任何时刻只有一个线程能够执行Python字节码,因此对于计算密集型的任务,线程并不能提高性能。
  6. Python的线程在执行I/O操作或其他阻塞调用时会释放GIL,因此对于I/O密集型的任务,线程可以提高性能。
  7. Python的线程需要注意线程安全性,即避免多个线程同时访问或修改共享的资源,否则可能会造成数据损坏或不一致。
  8. Python提供了一些工具来保证线程安全性,例如锁(Lock)、信号量(Semaphore)、定时器(Timer)和屏障(Barrier)等。

例如用”汽车”和“冰淇淋”作为关键词对B站进行搜索,将返回的视频标题进行采集整理并写入数据库,同时计算数据总量,以此进行热点事件分析,代码如下:

# 导入所需的模块
import requests
import re
import sqlite3
import threading

# 定义一个函数,根据关键词和页码获取B站搜索结果页面的HTML内容
def get_html(keyword, page):
    # 构造请求头和参数
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
    }
    params = {
        "keyword": keyword,
        "page": page,
        "order": "totalrank"
    }

    #设置亿牛云爬虫代理加强版 代理IP的服务器地址、端口、用户名和密码
    proxyHost = "www.16yun.cn"
    proxyPort = "31111"

    # 代理验证信息
    proxyUser = "16YUN"
    proxyPass = "16IP"

    proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {
        "host" : proxyHost,
        "port" : proxyPort,
        "user" : proxyUser,
        "pass" : proxyPass,
    }

    # 设置 http和https访问都是用HTTP代理
    proxies = {
        "http"  : proxyMeta,
        "https" : proxyMeta,
    }
    
    # 发送GET请求,获取响应内容,使用代理IP和用户名密码
    response = requests.get("https://search.bilibili.com/all", headers=headers, params=params, proxies=proxies)
    # 返回HTML内容
    return response.text

# 定义一个函数,从HTML内容中提取视频标题,并将其写入数据库
def extract_and_save(html):
    # 连接数据库,创建游标
    conn = sqlite3.connect("bilibili.db")
    cursor = conn.cursor()
    # 创建数据表,如果已存在则忽略
    cursor.execute("CREATE TABLE IF NOT EXISTS videos (title TEXT)")
    # 从HTML内容中提取视频标题,使用正则表达式匹配
    titles = re.findall(r"<a title=\"(.*?)\" href=", html)
    # 遍历每个标题,将其插入数据表中
    for title in titles:
        cursor.execute("INSERT INTO videos VALUES (?)", (title,))
    # 提交事务,关闭连接
    conn.commit()
    conn.close()

# 定义一个函数,计算数据库中的数据总量,并打印结果
def count_and_print():
    # 连接数据库,创建游标
    conn = sqlite3.connect("bilibili.db")
    cursor = conn.cursor()
    # 查询数据表中的记录数
    cursor.execute("SELECT COUNT(*) FROM videos")
    count = cursor.fetchone()[0]
    # 打印结果
    print(f"共采集了{count}条数据")
    # 关闭连接
    conn.close()

# 定义一个主函数,使用线程进行快速I/O操作
def main():
    # 定义关键词和页码范围
    keyword = "汽车 冰淇淋"
    pages = range(1, 11)
    # 创建一个空列表,用于存储线程对象
    threads = []
    # 遍历每个页码,创建一个线程对象,执行get_html和extract_and_save函数,并将其添加到列表中
    for page in pages:
        thread = threading.Thread(target=lambda: extract_and_save(get_html(keyword, page)))
        thread.start()
        threads.append(thread)
    # 等待所有线程结束
    for thread in threads:
        thread.join()
    # 调用count_and_print函数,计算并打印数据总量
    count_and_print()

# 调用主函数
if __name__ == "__main__":
    main()
    

总体来说,这段代码使用了多线程技术,使用多个线程并发地访问B站的搜索结果页面,提取其中的视频标题,并将其写入数据库,将网络请求和数据库操作分别放到不同的线程中执行,从而实现了快速爬取和处理大量数据的目的。同时,该代码还使用了爬虫代理IP,提高了爬虫的稳定性和安全性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值