• 爬虫之 单线程+多任务异步协程


    单线程+多任务异步协程的概念

    • 实现的意义: 
      •    提取爬取数据的效率
    • 实现异步爬虫的方式
      • 多线程/多进程(不建议)
      • 池(适当)
      • 单线程+多任务异步协程(推荐)
    •  概念
      • 协程:协程对象。可以使用async关键字修饰一个函数的定义(特殊的函数),当该特殊的函数被调用后,就可以返回一个协程对象。当函数调用后,函数内部的实现语句不会被立即执行
        • 协程 == 特殊函数
      • 任务对象
        • 本质上就是对协程对象的进一步封装
          • 任务对象 == 特殊函数
        • 给任务对象绑定一个回调
          • add_done_callback(callback)  
      • 事件循环(EventLoop):无限的循环对象
        • 我们必须将任务对象注册到事件循环对象中,然后开启事件循环对象
        • 事件循环对象在执行任务对象的时候是基于异步 
      • await async
      • 注意事项
        • 保证特殊函数内部不可以出现不支持异步模块对应的代码
        • 在特殊函数内部遇到阻塞操作必须使用await关键字对其进行手动挂起
        • 如果想要将多个任务对象注册到事件循环中,必须将多个任务对象封装到一个列表中,然后将列表注册(必须使用wait方法将列表中的任务对象进行挂起)到事件循环中 
      • aiohttp模块
        • 是一个支持异步的网络请求模块
        • pip install aiohttp
      • selenium模块的使用
        • 概念:就是一个基于浏览器自动化的模块
        • elenium和爬虫之间的关联
          • 很便捷的捕获动态加载的数据 - 可见即可得
          • 实现模拟登陆
        • 使用
          • 环境的安装:
            • pip install selenium
            • 下载一个浏览器的驱动程序
            • 谷歌驱动下载:http://chromedriver.storage.googleapis.com/index.html
            • 驱动程序和浏览器版本的映射关系:http://blog.csdn.net/huilan_same/article/details/51896672
        • 创建某一款一个浏览器对象
        • 动作链 如果想要触发一系列连续的行为动作

    代码逻辑:

    定义一个特殊的函数,协程对象

    import asyncio
    import time
    # 定义了一个特殊的函数
    # 特殊:调用后会返回一个协程对象,且函数内部的实现语句不会被立即执行
    # 创建一个协程对象
    async def test(num):
        print(num)
    
    c = test(10)
    print(c)
    #<coroutine object test at 0x0000025058808A40>

    封装一个任务对象           

    #封装一个任务对象
    async def test(num):
        print(num)
    
    c = test(10)
    # #根据协程对象封装了一个任务对象
    task = asyncio.ensure_future(c)
    print(task)
    #<Task pending coro=<test() running at H:/autoclient/test/test.py:6>>

     创建事件并执行          

    #事件循环对象
    async def request(url):
        print('正在请求:',url)
        time.sleep(2)
        print('请求完毕!',url)
    
    
    c1 = request('www.1.com')
    
    task_A = asyncio.ensure_future(c1)
    
    
    #创建一个事件循环对象
    loop = asyncio.get_event_loop()
    #将任务对象注册到该对象中并且启动事件循环
    loop.run_until_complete(task_A)

    任务对象的绑定回调

    import asyncio
    import time
    
    async def request(url):
        print('正在请求:',url)
        time.sleep(2)
        print('请求完毕!',url)
    
        return url
    
    
    #定义一个任务对象的回调函数
    #task参数表示的就是该函数被绑定的那个任务对象
    def task_callback(task):
        print('i am task_callback()')
        print(task.result())
        #task.result()返回的就是任务对象对应的特殊函数内部的返回值
    
    c = request('www.xxx.com')
    
    task = asyncio.ensure_future(c)
    task.add_done_callback(task_callback)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(task)

    多任务异步协程:

    import asyncio
    import time
    start = time.time()
    #在特殊函数内部不可以出现不支持异步模块相关的代码
    async def request(url):
        print('正在请求:',url)
        # time.sleep(2)#time模块是不支持异步
        await asyncio.sleep(2)  #阻塞操作必须使用await关键字进行挂起
        print('请求完毕!',url)
    
        return url
    
    urls = [
        'www.1.com',
        'www.2.com',
        'www.3.com'
    ]
    def task_callback(task):
        print(task.result())
    
    tasks = [] #多任务列表:存放多个任务对象
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(c)
        task.add_done_callback(task_callback)
        tasks.append(task)  #将多个任务对象装在到一个任务列表中
    
    loop = asyncio.get_event_loop()
    #多任务注册
    #wait就是将任务列表中的任务对象进行挂起
    loop.run_until_complete(asyncio.wait(tasks))
    
    print(time.time()-start)

    多任务异步爬虫测试:一般开500个协程

    flask代码:

    from flask import Flask
    import time
    
    app = Flask(__name__)
    
    
    @app.route('/bobo')
    def index_bobo():
        time.sleep(2)
        return 'Hello bobo'
    
    @app.route('/jay')
    def index_jay():
        time.sleep(2)
        return 'Hello jay'
    
    @app.route('/tom')
    def index_tom():
        time.sleep(2)
        return 'Hello tom'
    
    if __name__ == '__main__':
        app.run(threaded=True)

    爬虫测试代码:

    import asyncio
    import time
    import requests
    start = time.time()
    #在特殊函数内部不可以出现不支持异步模块相关的代码
    async def request(url):
       print('正在请求:',url)
       response = requests.get(url)
       return response.text
    
    urls = [
        'http://127.0.0.1:5000/bobo',
        'http://127.0.0.1:5000/tom',
        'http://127.0.0.1:5000/jay'
    ]
    
    def parse(task):
        page_text = task.result()
        print(page_text+',请求到的数据!!!')
    
    tasks = []
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(c)
        task.add_done_callback(parse)
        tasks.append(task)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    
    
    print(time.time()-start)

    aiohttp的使用:

    mport asyncio
    import time
    import aiohttp
    start = time.time()
    #在特殊函数内部不可以出现不支持异步模块相关的代码
    #简单的基本架构:
    # async def request(url):
    #    with aiohttp.ClientSession() as s:
    #        #s.get/post和requests中的get/post用法几乎一样:url,headers,data/prames
    #        #在s.get中如果使用代理操作:proxy="http://ip:port"
    #        with s.get(url) as response:
    #            #获取字符串形式的响应数据:response.text()
    #            #获取byte类型的:response.read()
    #            page_text = response.text()
    #            return page_text
    #在当前架构的基础上补充细节即可
        #细节1:在每一个with前加上async关键字
        #细节2:在get方法前和response.text()前加上await关键字进行手动挂起操作
    async def request(url):
       async with aiohttp.ClientSession() as s:
           #s.get/post和requests中的get/post用法几乎一样:url,headers,data/prames
           #在s.get中如果使用代理操作:proxy="http://ip:port"
           async with await s.get(url) as response:
               #获取字符串形式的响应数据:response.text()
               #获取byte类型的:response.read()
               page_text = await response.text()
               return page_text
    
    # urls = [
    #     'http://127.0.0.1:5000/bobo',
    #     'http://127.0.0.1:5000/tom',
    #     'http://127.0.0.1:5000/jay',
    #     'http://127.0.0.1:5000/bobo',
    #     'http://127.0.0.1:5000/tom',
    #     'http://127.0.0.1:5000/jay',
    #     'http://127.0.0.1:5000/bobo',
    #     'http://127.0.0.1:5000/tom',
    #     'http://127.0.0.1:5000/jay',
    # ]
    urls = []
    for i in range(500):
        urls.append('http://127.0.0.1:5000/bobo')
    def parse(task):
        page_text = task.result()
        print(page_text+',请求到的数据!!!')
    
    tasks = []
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(c)
        task.add_done_callback(parse)
        tasks.append(task)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    print(time.time()-start)

    案例:

    import aiohttp
    import asyncio
    from lxml import etree
    
    all_titles = []
    
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
    
    }
    async def request(url):
        async with aiohttp.ClientSession() as s:
            async with await s.get(url,headers=headers) as response:
                page_text = await response.text()
                return page_text
    
    urls = []
    url = 'http://wz.sun0769.com/index.php/question/questionType?type=4&page=%d'
    for page in range(100):
        u_page = page * 30
        new_url = format(url%u_page)
        urls.append(new_url)
    
    tasks = []
    def parse(task):
        page_text = task.result()
        page_text = page_text.encode('gb2312').decode('gbk')
        tree = etree.HTML(page_text)
        tr_list = tree.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')
        for tr in tr_list:
            title = tr.xpath('./td[2]/a[2]/text()')[0]
            print(title)
            all_titles.append(title)
    
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(c)
        task.add_done_callback(parse)
        tasks.append(task)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))

    selenim演示:

    from selenium import webdriver
    from time import sleep
    
    # 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的
    driver = webdriver.Chrome(r'chromedriver.exe')
    # 用get打开百度页面
    driver.get("http://www.baidu.com")
    # 查找页面的“设置”选项,并进行点击
    driver.find_elements_by_link_text('设置')[0].click()
    sleep(2)
    # # 打开设置后找到“搜索设置”选项,设置为每页显示50条
    driver.find_elements_by_link_text('搜索设置')[0].click()
    sleep(2)
    
    # 选中每页显示50条
    m = driver.find_element_by_id('nr')
    sleep(2)
    m.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
    m.find_element_by_xpath('.//option[3]').click()
    sleep(2)
    
    # 点击保存设置
    driver.find_elements_by_class_name("prefpanelgo")[0].click()
    sleep(2)
    
    # 处理弹出的警告页面   确定accept() 和 取消dismiss()
    driver.switch_to_alert().accept()
    sleep(2)
    # 找到百度的输入框,并输入 美女
    driver.find_element_by_id('kw').send_keys('美女')
    sleep(2)
    # 点击搜索按钮
    driver.find_element_by_id('su').click()
    sleep(2)
    # 在打开的页面中找到“Selenium - 开源中国社区”,并打开这个页面
    driver.find_elements_by_link_text('美女_百度图片')[0].click()
    sleep(3)
    
    # 关闭浏览器
    driver.quit()

    selenium的基本使用:

    from selenium import webdriver
    from time import sleep
    //chromedriver.exe 浏览器的驱动文件 bro
    = webdriver.Chrome(executable_path='chromedriver.exe') #发起指定url的请求 bro.get('https://www.jd.com/') #在搜索框中搜索商品 #可以使用find系列的方法进行标签定位 search_input = bro.find_element_by_xpath('//*[@id="key"]') #想搜索框中写入商品名称 search_input.send_keys('iphonex') sleep(2) btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button') btn.click() sleep(2) #执行js让滚轮向下滑动,滑动一屏 bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(2) # bro.execute_script('window.scrollTo(0,-document.body.scrollHeight)') page_text = bro.page_source with open('./jingdong.html','w',encoding='utf-8') as fp: fp.write(page_text) print(page_text) sleep(4) #关闭浏览器 bro.quit()

    selenium的动作链

    from selenium import webdriver
    from selenium.webdriver import ActionChains #动作连
    from time import sleep
    
    bro = webdriver.Chrome(executable_path='chromedriver.exe')
    bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
    
    #定位要拖动的标签
    #定位的标签是存在于iframe的子页面中,如果直接使用find做定位,是定位不到的
    # target_ele = bro.find_element_by_id('draggable')
    
    #像定位iframe中子页面中的标签必须进行如下操作
    bro.switch_to.frame('iframeResult')
    target_ele = bro.find_element_by_id('draggable')
    
    #基于动作连实现滑动操作
    action = ActionChains(bro)
    #点击且长按
    action.click_and_hold(target_ele)
    
    for i in range(5):
        #perform()表示立即执行动作连指定好的动作
        action.move_by_offset(17,0).perform()
        sleep(0.5)
    
    action.release()
    
    sleep(4)
    
    bro.quit()
  • 相关阅读:
    web socket RFC6455 frame 打包、解包
    Cacti 加入多台主机带宽汇聚
    C-链表实现,保存文件,评估-单项选择题系统课程设计---ShinePans
    ios7.1安装提示"无法安装应用程序 由于证书无效"的解决方式二(dropbox被封项目转移到Appharbor上)
    【模板】第二类斯特林数·列
    2018-8-10-win10-uwp-slider-隐藏显示数值
    2018-8-10-win10-uwp-slider-隐藏显示数值
    2019-1-27-WPF-使用-ItemsPanel-修改方向
    2019-1-27-WPF-使用-ItemsPanel-修改方向
    2018-8-10-win10-uwp-x_Bind-无法获得资源
  • 原文地址:https://www.cnblogs.com/lulin9501/p/11308932.html
Copyright © 2020-2023  润新知