2021-07-03

线程

线程的具体实现步骤

导包:

from threading import Thread

NO.1
这里我们定义俩方法

def func():
    for i in range(1000):
        print("func",i)

def funa(name):
    for i in range(1000):
        print(name,i)

线程的实现

if __name__ == '__main__':
    t1=Thread(target=funa,args=("孙悟空",))
    # 传递参数必须是元组所以加逗号
    t1.start()
    t2=Thread(target=funa,args=("牛牛公主",))
    t2.start()

NO.2
定义类

class MyThread(Thread)def run(self):
        for i in range(1000):
            print("func", i)

在主函数里面可以任意造线程实现实例

if __name__ == '__main__':
    t=MyThread()
    t1=MyThread()
    t2=MyThread()
    t.start()
    t1.start()
    t2.start()
    #.......

线程池:
系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。
具体实现步骤

from concurrent.futures import ThreadPoolExecutor

开辟线程池的方法

    with ThreadPoolExecutor(20) as t:
        for i in range(100):
            t.submit(func,name=f"线程{i}")

这里是开辟了20个线程调用submit来共同执行这个函数

实例:爬新发地菜价信息

如果不用线程池的话代码如下

import time

import requests
from bs4 import BeautifulSoup
import csv

url = "http://www.xinfadi.com.cn/marketanalysis/0/list/"

f = open("菜价.csv", mode="w",encoding="utf-8",newline="")
cswriter = csv.writer(f)
t1=time.time()
for i in range(1,101):
    url=url+str(i)+".shtml"
    resp = requests.get(url)
    # 解析数据
    # 1.把页面源代码交给BeautifulSoup进行处理,生成bs对象
    page = BeautifulSoup(resp.text, 'html.parser')  # 指定html解析器
    # 2.从bs对象中查找数据:find(标签,属性=值),  find_all(标签,属性=值)
    # page.find("table", class_="hq_table")   # class是python的关键字,加上_则不为关键字
    table = page.find("table", attrs={"class": "hq_table"})   # 与上一行一致
    # 拿到所有数据行
    trs = table.findAll("tr")[1:]
    for tr in trs:
        tds = tr.find_all("td")  # 拿到每一行中的所有td
        name = tds[0].text  # 表示拿到被标签标记的内容
        low = tds[1].text
        avg = tds[2].text
        high = tds[3].text
        gui = tds[4].text
        kind = tds[5].text
        date = tds[6].text
        cswriter.writerow([name,low,avg,high,gui,kind,date])
    print("第"+str(i)+"页下载结束")
t2=time.time()
print("用时"+str(t2-t1)+"秒")

这里是我的结果
20*100=2000
在这里插入图片描述
用时如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可见效率低下
而我们使用线程池之后

#coding:utf-8
import time

import requests
from lxml import etree
import csv
from concurrent.futures import ThreadPoolExecutor
f=open("data.csv",mode="w",encoding="utf-8",newline="")
csvwriter=csv.writer(f)
def download(url):
    head={
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3870.400 QQBrowser/10.8.4405.400"
    }
    resp=requests.get(url,headers=head)
    html=etree.HTML(resp.text)
    table=html.xpath("/html/body/div[2]/div[4]/div[1]/table")[0]
    trs=table.xpath("./tr")[1:]
    for tr in trs:
        text=tr.xpath("./td/text()")
        # 处理掉//
        text=(item.replace("\\","").replace("|","").replace("/","") for item in text)
        csvwriter.writerow(text)

    print()
if __name__ == '__main__':
    # 效率低下
    # for i in range(1,16721):
    #     download(f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml")
    t1=time.time()
    with ThreadPoolExecutor(50) as t:
        for i in range(1,101):# 199*20=3980
            t.submit(download(f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml"))
            print(str(i)+"下载完毕")
    t2=time.time()
    print("用时"+str(t2-t1)+"秒")

用时:
在这里插入图片描述
在这里插入图片描述
效率还算一般

协程

理解

协程是一种比线程更加轻量级的存在,最重要的是,协程不被操作系统内核管理,协程是完全由程序控制的。
运行效率极高,协程的切换完全由程序控制,不像线程切换需要花费操作系统的开销,线程数量越多,协程的优势就越明显。
协程不需要多线程的锁机制,因为只有一个线程,不存在变量冲突。
对于多核CPU,利用多进程+协程的方式,能充分利用CPU,获得极高的性能。

通过函数调用了解协程的优势

我们给出三个函数(都需要睡眠)

async def func1():
    print("aaaaa")
    await asyncio.sleep(4)
    print("aaaaaover")
async def func2():
    print("bbbbb")
    await asyncio.sleep(3)
    print("bbbbbover")
async def func3():
    print("ccccc")
    await asyncio.sleep(2)
    print("cccccover")

main函数及调用:

async def main():
    tasks=[
        asyncio.create_task(func1()),
        asyncio.create_task(func2()),
        asyncio.create_task(func3())
    ]
    await asyncio.wait(tasks)
if __name__ == '__main__':
    t1 = time.time()
    asyncio.run(main())
    t2 = time.time()
    print(t2-t1)

由理论指导得出所需时长应该等于最长的休眠时间+两次切换的时间,应该等于4点多秒
在这里插入图片描述
若按照单进程的话应该要9秒多
这里体现了多进程的优势

实例:爬取三个图片

#coding:utf-8
import aiohttp
import asyncio
#三个图片网址
urls=[
    "http://kr.shanghai-jiuxin.com/file/2021/0702/662c6fbaa13160d20122c30f97de8bd8.jpg",
    "http://kr.shanghai-jiuxin.com/file/2021/0702/47337f462fc56624218e69c2e070dd6d.jpg",
    "http://kr.shanghai-jiuxin.com/file/2021/0702/be7e8685eb8808c1c0809227760e5016.jpg"
]
async def aiodown(url):
    name=url.rsplit("/",1)[1]
    # aiohttp.ClientSession()相当于requests模块
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            resp.content.read()#resp.content
            with open(name,mode="wb")as f:
                f.write(await resp.content.read())
    print(name)
async def main():
    #任务元组
    tasks=[]
    for url in urls:
        tasks.append(aiodown(url))
    await asyncio.wait(tasks)
if __name__ == '__main__':
    asyncio.run(main())

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

爬100章小说

整体思路是用同步代码访问主页面拿到页面json
再通过异步代码下载小说内容
同步代码:

async def getCatalog(url):
    resp=requests.get(url)
    dic=resp.json()
    #任务元组
    tasks = []
    #获取每章的title和cid
    for item in dic['data']['novel']['items']:
        title=item['title']
        cid=item['cid']
#调用异步代码方法download
        tasks.append(download(cid,bid ,title))
    await asyncio.wait(tasks)

异步代码:

async def download(cid,bid,title):
#字典元素
    data={
        "book_id":bid,
        "cid": f"{bid}|{cid}",
        "need_bookinfo": 1
    }
    #转换成json对象
    data=json.dumps(data)
    #子页面链接
    url=f"http://dushu.baidu.com/api/pc/getChapterContent?data={data}"
    #相当于requests.get()
    async with aiohttp.ClientSession()as session:
        async with session.get(url)as resp:
            dic= await resp.json()
            #这里用到了aiofiles模块用于异步操作
            async with aiofiles.open(title,mode="w",encoding="utf-8")as f:
                await f.write(dic['data']['novel']['content'])
                

main:

if __name__ == '__main__':
    bid="4306063500"
    url='http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"'+bid+'"}'
    asyncio.run(getCatalog(url))

爬取西游记100章小说结果:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值