zoukankan      html  css  js  c++  java
  • 爬虫小案例:多协程工作

    为提高工作效率,让多个爬虫一起工作

    需要使用的库是:gevent

    方式一

    1.使用队列的形式:

    from gevent import monkey
    # 从gevent库里导入monkey模块。
    monkey.patch_all()
    # monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
    import gevent,time,requests
    # 导入gevent、time、requests
    from gevent.queue import Queue
    # 从gevent库里导入queue模块
    
    start = time.time()
    # 记录开始时间
    
    url_list = [
        'https://www.baidu.com/',
        'https://www.sina.com.cn/',
        'http://www.sohu.com/',
        'https://www.qq.com/',
        'https://www.163.com/',
        'http://www.iqiyi.com/',
        'https://www.tmall.com/',
        'http://www.ifeng.com/'
    ]
    # 要爬取的网站
    
    work = Queue()
    # 创建队列对象,并赋值给work。
    for url in url_list:
        work.put_nowait(url)
        # 用put_nowait()函数可以把网址都放进队列里
    
    def crawler():
        while not work.empty():
            # 当队列不是空的时候,就执行下面的程序。
            url = work.get_nowait()
            # 用get_nowait()函数可以把队列里的网址都取出。
            r = requests.get(url)
            # 抓取网站内容
            print(url, work.qsize(), r.status_code)
            # 打印网址、队列长度、抓取请求的状态码
    
    task_list = []
    # 创建空的任务列表
    
    for i in range(2):
        # 相当于创建了2个爬虫
        task = gevent.spawn(crawler)
        # 用gevent.spawn()函数创建执行crawler()函数的任务。
        task_list.append(task)
        # 往任务列表添加任务
    
    gevent.joinall(task_list)
    # 用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。
    
    end = time.time()
    # 结束时间
    
    print(end-start)
    # 总用时

    以上是创建了一个队列存储 url,然后让 2 个爬虫一起工作,哪个爬虫执行完了,就可以从队列中获取下一个 url 去执行

    方式二

    2.不使用队列,有多少个任务,就创建多少个爬虫去执行:

    from gevent import monkey
    # 从gevent库里导入monkey模块。
    monkey.patch_all()
    # monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
    import gevent,time,requests
    #导入gevent、time、requests。
    
    start = time.time()
    # 记录程序开始时间。
    
    url_list = [
        'https://www.baidu.com/',
        'https://www.sina.com.cn/',
        'http://www.sohu.com/',
        'https://www.qq.com/',
        'https://www.163.com/',
        'http://www.iqiyi.com/',
        'https://www.tmall.com/',
        'http://www.ifeng.com/'
    ]
    #把8个网站封装成列表。
    
    def crawler(url):
    #定义一个crawler()函数。
        r = requests.get(url)
        #用requests.get()函数爬取网站。
        print(url,time.time()-start,r.status_code)
        #打印网址、请求运行时间、状态码。
    
    tasks_list = []
    #创建空的任务列表。
    
    for url in url_list:
        # 遍历url_list。
        task = gevent.spawn(crawler, url)
        # 用gevent.spawn()函数创建任务。
        tasks_list.append(task)
        # 往任务列表添加任务。
    
    gevent.joinall(tasks_list)
    # 执行任务列表里的所有任务,就是让爬虫开始爬取网站。
    end = time.time()
    # 记录程序结束时间。
    print(end-start)
    #打印程序最终所需时间。

    以上只有 8 个网站,数量不多,可以这样操作,但是,如果是 1000 个网站,就是一下子发起1000 次请求,这样的恶意请求,会拖垮网站的服务器。所以,这样做是不可取的,要使用队列的形式来操作。

    小练习:多协程爬取豆瓣 Top250电影

    from gevent import monkey
    # 程序异步执行
    monkey.patch_all()
    import gevent,requests,csv
    from bs4 import BeautifulSoup
    from gevent.queue import Queue
    
    # 电影列表
    movies = []
    
    # 队列对象
    work = Queue()
    for x in range(10):
        url = 'https://movie.douban.com/top250?start={}&filter='.format(x * 25)
        work.put_nowait(url)
    
    def moviesort(movie):
        return int(movie[0])
    
    def crawler():
        while not work.empty():
            url = work.get_nowait()
            # 为躲避反爬机制,伪装成浏览器的请求头
            headers = {
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36 OPR/65.0.3467.78 (Edition Baidu)'}
            print('正请求:{}'.format(url))
            res = requests.get(url, headers=headers)
            if res.status_code == 200:
                htmltext = res.text
                soup = BeautifulSoup(htmltext, 'html.parser')
                ol = soup.find('ol', class_='grid_view')
                for li in ol.find_all('li'):
                    # 排名
                    num = li.find('div', class_='pic').find('em').text
    
                    info = li.find('div', class_='info')
    
                    # 标题
                    title = info.find('div', class_='hd').find('span', class_='title').text.strip()
    
                    # 链接
                    link = info.find('div', class_='hd').find('a')['href']
    
                    # 评分
                    rating_num = info.find('span', class_='rating_num').text
    
                    # 推荐语
                    inq = info.find('span', class_='inq').text
    
                    # 上映时间、地区、类型
                    bd = info.find('div', class_='bd').find('p').contents[2]
                    bd = bd.split('/')
    
                    movies.append([num,title,link,rating_num,inq,bd[0].strip(),bd[1].strip(),bd[2].strip()])
            else:
                print('{}:请求失败!'.format(url))
    
    task_list = []
    
    # 创建 2 个爬虫
    for i in range(2):
        # 创建任务
        task = gevent.spawn(crawler)
        # 把任务添加到任务列表
        task_list.append(task)
    
    # 执行所有任务
    gevent.joinall(task_list)
    
    # 排序
    movies.sort(key=moviesort)
    
    # 打印电影数据
    print(movies)
    
    # 保存数据
    with open('./豆瓣Top250电影.csv', 'w', newline='', encoding='utf-8-sig') as file:
        # 编码utf-8-sig:支持python3,不支持python2
        writer = csv.writer(file)
        writer.writerow(['排名', '电影名称', '链接', '评分', '推荐语', '上映时间', '地区', '类型'])
        for movie in movies:
            writer.writerow(movie)
    
    print('完毕!')

    小练习:多协程爬取薄荷网食物热量

    from gevent import monkey
    # 程序异步执行
    monkey.patch_all()
    import gevent,requests,csv
    from bs4 import BeautifulSoup
    from gevent.queue import Queue
    
    foods = []
    
    # 队列对象
    work = Queue()
    url = "http://www.boohee.com/food/group/{type}?page={page}"
    for x in range(1, 4):
        for y in range(1, 4):
            url1 = url.format(type=x, page=y)
            work.put_nowait(url1)
    
    url = "http://www.boohee.com/food/view_menu?page={page}"
    for x in range(1, 4):
        url2 = url.format(page=x)
        work.put_nowait(url2)
    
    # 爬虫
    def crwaler():
        while not work.empty():
            url = work.get_nowait()
            headers = {
                'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36 OPR/66.0.3515.36 (Edition Baidu)'
            }
            print("正请求网页:{}".format(url))
            res = requests.get(url, headers=headers)
            res.encoding = 'utf-8'
            soup = BeautifulSoup(res.text, 'html.parser')
            foodlist = soup.find('ul', class_='food-list').find_all('li', class_='item')
            for food in foodlist:
                box = food.find("div", class_='text-box')
                # 名称
                name = box.find("a")['title']
                # 链接
                link = "http://www.boohee.com" + box.find("a")['href']
                # 热量
                heat = box.find("p").text.strip()
    
                foods.append([name, link, heat])
    
    # 任务列表
    task_list = []
    
    # 创建 2 个爬虫
    for i in range(2):
        # 创建任务
        task = gevent.spawn(crwaler)
        task_list.append(task)
    
    # 执行所有任务
    gevent.joinall(task_list)
    
    print(foods)
    
    # 保存数据
    with open('./食物热量.csv', 'w', newline='', encoding='utf-8-sig') as file:
        writer = csv.writer(file)
        writer.writerow(['名称', '链接', '热量'])
        for food in foods:
            writer.writerow(food)
    
    print("完毕!")
  • 相关阅读:
    CCF——分蛋糕(2017-3)
    CCF——公共钥匙盒(2017-9)
    CCF——打酱油(2017-9)
    CCF——游戏(2017-12)
    SDS-简单动态字符串
    Redis主从复制
    MySQL 知识点
    MySQL 死锁
    Java 类加载机制
    Java IO
  • 原文地址:https://www.cnblogs.com/KeenLeung/p/12234965.html
Copyright © 2011-2022 走看看