IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python异步爬虫(1):引入+线程池实战 -> 正文阅读

[Python知识库]python异步爬虫(1):引入+线程池实战

python异步爬虫(1):引入+线程池实战

? 使用异步爬虫可以实现高性能高效率的数据爬取操作

一.引入

先运行下面这个程序:

import requests

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36"
}
urls = ['https://www.sogou.com/',
        'https://cn.bing.com/?mkt=zh-CN&mkt=zh-CN',
        'https://www.baidu.com/']

def get_content(url):
    print("正在爬取",url)
    resp=requests.get(url=url,headers=headers)
    if resp.status_code == 200:
        return resp.content
def parse_content(content):
    print("响应数据的长度为",len(content))

for url in urls:
    content =get_content(url)
    parse_content(content)

image-20210801090501894

输出结果的时候,明显可以观察到爬取过程是一个阻塞操作,是耗时过程

也可以加入时间来观察:

for url in urls:
    time_start=time.time()
    content=get_content(url)
    parse_content(content)
    time_end=time.time()
    time_c=time_end-time_start
    print("time_cost",time_c,'s')

image-20210801085832213

这是一个单线程环境,如果要爬取100个,10000个,那么…显然效率很低

那么效率高的异步爬虫有哪些 方式呢:

  • 多线程、多进程
    • 好处:可以为相关阻塞的操作单独开启线程或进程,阻塞操作可以异步执行
    • 弊端:无法无限制开启多线程或者多进程(CPU有限制)
  • 线程池、进程池
    • 好处:可以降低系统对进程或线程创建和销毁的频率,从而降低系统开销
    • 弊端:池中线程或进程的数量有上限

二.线程池实战

? 线程池的解释

? ★这个视频里的动画解释很生动:B站 楠哥讲解视频

插播:python response.text和response.content的区别

? 昨天pymongo实战那篇文章里面搜到的Xpath返回为空的那篇文章里面也提到了这个,并且更加推荐resp.content

? 还可以看看这个:python中的requests,response.text与response.content ,及其编码

image-20210814102508835

1.简单模拟一下

单线程:

import time

def get_page(str):
    print("正在爬取:",str)
    time.sleep(2)
    print('下载成功:',str)

name_list=['a','b','c','d']

start_time=time.time()

for i in range(len(name_list)):
    get_page(name_list[i])

end_time=time.time()
print('%d second'%(end_time-start_time))

image-20210801092339801

换成线程池来处理:


import time
from multiprocessing.dummy import Pool
# Pool是线程池对应的类
start_time = time.time()
def get_page(str):
    print("正在爬取:", str)
    time.sleep(2)
    print('下载成功:', str)
name_list = ['a', 'b', 'c', 'd']
# 先实例化一个线程池对象
pool = Pool(4)  # 四个线程
# 第一个参数为函数,放入会发生阻塞的操作
# 将列表中每一个列表元素传递给get_page处理
# 第二个参数是可迭代对象
# 四个线程池对象分别进行阻塞操作,map的返回值,就是get_page的返回值
# get_page的返回值会依次赋值给map的返回值,所以map的返回值是一个列表
pool.map(get_page, name_list)
end_time = time.time()
print('%d second' % (end_time-start_time))

image-20210801093802896

改成%f输出的是2.029824 second

? 关于python的map()函数

2.实战:线程池爬取蔬菜网市场价格前200页

pymongo实战(2)那篇博客的基础上稍作修改,加上线程池

代码

# https://www.nonghecj.com/p01
import pymongo
import  requests
from lxml import  etree
#引入线程池
from concurrent.futures import ThreadPoolExecutor

myClient = pymongo.MongoClient("localhost")
#新建一个叫vegetable2的数据库
myDb = myClient["vegetable2"]
myCol =myDb["price"]


headers={"User-Agent":
             "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36"}
#下载一页
def download_onepage(url,num_1):
    resp = requests.get(url,headers=headers)
    resp.encoding=resp.apparent_encoding

    # 生成etree对象
    pageText = etree.HTML(resp.text)
    # xpath列表
    rows = pageText.xpath('/html//div[@class="pri_k"]/p')
    #每轮获取一行数据
    num_2=0
    for row in rows:
        # 日期
        date = row.xpath('./span[1]/text()')[0].replace('[', '').replace(']', '')
        # 品种
        variety = row.xpath('./span[2]/text()')[0]
        # 批发市场
        market = row.xpath('./span[3]/a/text()')[0]
        # 最低价格
        min_price = row.xpath('./span[4]/text()')[0]
        # 最高价格
        max_price = row.xpath('./span[5]/text()')[0]
        # 平均价格
        mean_price = row.xpath('./span[6]/text()')[0]
        # 计量单位
        measure_unit = row.xpath('./span[7]/text()')[0]

        kv_dict={"date":date,"variety":variety,"market":market,
                 "min_price":min_price,"max_price":max_price,
                 "mean_price":mean_price,"measure_unit":measure_unit}
        # myCol.insert_one(kv_dict)
        myCol.update_one({"index":f"{num_1}{num_2}"},{"$set":kv_dict},upsert=True)

        num_2+=1
    print(f"{url}抓取完毕")

    resp.close()


if __name__=="__main__":
    # # 每轮获取一页数据
    # for i in range(1, 4):
    #准备50个线程池,提取前200页
    with ThreadPoolExecutor(50) as t:
        for i in range(1,201):

            t.submit(download_onepage,f'http://www.vegnet.com.cn/Price/List_p{i}.html',i)

    print("done!")


image-20210814092138735

? ThreadPoolExecutor(线程池介绍)

? python线程池 ThreadPoolExecutor 使用详解

遇到的问题

if __name__ == '__main__':

一开始一直不运行,结果发现'__main__'前后两个下划线漏了…

数据条数

每一页是26行数据,但是运行了好几次都是4715条数据,按理说前200页应该是5200条,目前还不知道原因…

image-20210814085258031

好像被封IP了…

image-20210814015554532

不过好像,过一会儿又解封了…

又能继续爬取

线程池爬取不能保证数据的有序性

一方面查看的时候可以用sort解决,但是线程池会把页数打乱…

目前还没有进一步想法

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-08-15 15:29:40  更:2021-08-15 15:32:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 12:03:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计