• python爬虫基础(二)


    requests+selenium+scrapy

    python爬虫基础(一)

    python爬虫

    1、异步爬虫

    目的:在爬虫中使用异步实现高性能的数据爬取操作

    requests.get()方法是一个阻塞的方法

    异步爬虫之多进程and多线程(不建议使用)

    多线程、多进程:

    1. 好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行
    2. 弊端:无法无限制的开启多线程或者多进程
    异步爬虫之线程池and进程池(适当使用)

    线程池、进程池:

    1. 好处:可以降低系统对进程或者线程创建销毁的一个频率,从而降低系统的开销
    2. 弊端:池中线程或进程的数量是有上限的
    # 单线程
    import time
    
    def get_page(string):
        print(f"正在下载:{string}")
        time.sleep(2)
        print("下载成功")
    
    names=['a','b','c','d']
    
    start=time.time()
    
    for i in range(len(names)):
        get_page(names[i])
    
    end=time.time()
    print(f"{end-start} second")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    # 线程池
    
    import time
    from multiprocessing.dummy import Pool
    
    
    def get_page(string):
        print(f"正在下载:{string}")
        time.sleep(2)
        print("下载成功")
    
    
    names = ['a', 'b', 'c', 'd']
    
    # 实例化线程池对象
    pool=Pool(4)
    pool.map(get_page,names)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    单线程+异步协程(推荐)

    event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行

    coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象

    task:任务,它是对协程对象的进一步封装,包含了任务的各个状态

    future:代表将来执行或还没有执行的任务,实际上和task没有本质区别

    async:定义一个协程

    await:用来挂起阻塞方法的执行

    # 协程
    import asyncio
    
    
    async def request(url):
        print(f"正在请求的url是:{url}")
        print("请求成功")
        return url
    
    
    # 被async修饰的函数,调用之后返回的一个协程对象
    a = request('www.baidu.com')
    
    # 创建事件循环对象
    loop = asyncio.get_event_loop()
    
    # 将协程对象注册到loop中,然后启动loop
    loop.run_until_complete(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    # task的使用
    import asyncio
    
    
    async def request(url):
        print(f"正在请求的url是:{url}")
        print("请求成功")
        return url
    
    
    # 被async修饰的函数,调用之后返回的一个协程对象
    a = request('www.baidu.com')
    
    loop = asyncio.get_event_loop()
    # 基于loop创建了一个task对象
    task = loop.create_task(a)
    
    loop.run_until_complete(task)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    # future的使用
    import asyncio
    
    
    async def request(url):
        print(f"正在请求的url是:{url}")
        print("请求成功")
        return url
    
    
    # 被async修饰的函数,调用之后返回的一个协程对象
    a = request('www.baidu.com')
    
    loop = asyncio.get_event_loop()
    
    future = asyncio.ensure_future(a)
    
    loop.run_until_complete(future)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    # future的使用
    import asyncio
    
    
    async def request(url):
        print(f"正在请求的url是:{url}")
        print("请求成功")
        return url
    
    
    # 被async修饰的函数,调用之后返回的一个协程对象
    a = request('www.baidu.com')
    
    def callback_func(task):
        # result返回的就是任务对象中封装的协程对象对应函数的返回值
        print(task.result())
    
    
    # 绑定回调
    loop = asyncio.get_event_loop()
    future = asyncio.ensure_future(a)
    
    # 将回调函数绑定到任务对象中
    future.add_done_callback(callback_func)
    loop.run_until_complete(future)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    补充:回调函数

    定义一个函数,然后将这个函数的函数名传递给另一个函数做参数,以这个参数命名的函数就是回调函数


    例如:有一家旅馆提供叫醒服务,但是要求旅客自己决定被叫醒的方法。可以是客房打电话,也可以是派服务员去敲门。“叫醒”这个行为是旅馆提供的,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数


    def way(arg: str):
        print(arg)
    
    
    def service(arg: str, callback):
        callback(arg)
    
    
    service('请打电话叫我起床', way)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    # 带额外状态信息的回调函数
    def add(x, y):
        return x + y
    
    
    class ResultHandler(object):
        def __init__(self):
            self.sequence = 0
    
        def handle(self, result):
            self.sequence += 1
            print(f"{self.sequence} got: {result}")
    
    
    def apply_async(func, args, *, callback):
        result = add(*args)
        callback(result)
    
    
    r = ResultHandler()
    apply_async(add, (1, 2), callback=r.handle)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    # 使用协程
    def add(x, y):
        return x + y
    
    
    def apply_async(func, args, *, callback):
        result = add(*args)
        callback(result)
    
    
    def make_handler():
        sequence = 0
        while True:
            result = yield
            sequence += 1
            print(f"[{sequence}] got: {result}")
    
    
    handler = make_handler()
    next(handler)
    apply_async(add, (2, 3), callback=handler.send)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    补充:yield

    yield可以看成return,但yield的作用不等于return。把yield看成return之后,再将yield看成生成器(generator)的一部分

    def ret():
        print("starting")
        while True:
            value = yield 4
            print(f"value: {value}")
    
    
    generator = ret()
    print(next(generator))
    print("divider")
    print(next(generator))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    1、程序开始执行以后,由于ret()函数中包含yield关键字,所以ret()函数并不会真正被执行(发现是不是和协程的特点很像),而是先得到一个生成器generator(一个class对象)

    2、直到调用next()方法,ret()函数正式开始执行,先执行ret()函数中的print方法,然后进入while循环

    3、程序遇到yield关键字,return出一个4(相当于是函数的一个返回值),然后程序停止,并没有对value执行赋值操作,此时next(generator)语句执行完成,输出的内容分别是"starting"和"4"

    4、程序执行print(“divider”)

    5、又开始执行下面的print(next(generator)),此时是从上面的next()方法执行结束之后程序停止的地方开始执行的,也就是执行对value的赋值操作,但这个时候赋值操作的右边是没有值的(因为刚才那个4已经return出去了),所以这个时候value赋值为None

    6、然后程序继续执行while语句,再次遇到yield时,return出去4,程序停止执行

    def ret():
        print("starting")
        while True:
            value = yield 4
            print(f"value: {value}")
    
    
    generator = ret()
    print(next(generator))
    print("divider")
    print(generator.send(2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    send()方法可以向yield所在行的变量发送一个值,同时send()方法也包含next()方法的功能

    7、程序执行generator.send(2),程序会从yield关键字所在行继续向下执行,同时send会将2这个值赋值给value变量

    8、send()方法中包含next()方法,所以程序会继续向下执行,直到程序再次遇到yield关键字,yield在返回后面的值后,程序再次暂停,直到再次调用next()方法或send()方法

    对于生成器的相关概念可以参考:一篇文章入门python基础

    多任务异步协程

    进程控制:七状态模型

    在这里插入图片描述

    import asyncio
    import time
    
    
    async def request(url):
        print(f"正在下载:{url}")
        # 在异步协程中如果出现了同步模块相关的代码,就无法实现异步
        # time.sleep(2)
    
        # 异步模块
        # 在asyncio中遇到阻塞操作时必须进行手动挂起
        await(asyncio.sleep(2))
        print(f"下载结束:{url}")
    
    
    urls = {
        'www.baidu.com',
        'www.sougou.com',
        'www.hnu.edu.cn'
    }
    
    # 任务列表:存放多个任务对象
    futures = []
    
    for url in urls:
        c = request(url)
    
        # future的使用
        future = asyncio.ensure_future(c)
        futures.append(future)
    
    # 创建事件循环对象
    loop = asyncio.get_event_loop()
    
    # 需要将任务列表封装到wait中
    loop.run_until_complete(asyncio.wait(futures))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    aiohttp模块

    对应的flask:

    from flask import Flask
    import time
    
    app = Flask(__name__)
    
    
    @app.route('/hyh')
    def index_hyh():
        time.sleep(2)
        return "霍雨浩"
    
    
    @app.route('/twt')
    def index_twt():
        time.sleep(2)
        return "唐舞桐"
    
    
    @app.route('/gyn')
    def index_gyn():
        time.sleep(2)
        return "古月娜"
    
    
    if __name__ == "__main__":
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    如果对flask不太了解,可以参考:Flask入门(一)Flask入门(二)模板flask入门(三)静态文件

    import requests
    import asyncio
    import time
    import aiohttp
    
    start=time.time()
    urls = [
        'http://127.0.0.1:5000/twt',
        'http://127.0.0.1:5000/hyh',
        'http://127.0.0.1:5000/gyn'
    ]
    
    async def request(url):
    
        # requests.get发起的请求基于同步,必须使用基于异步的网络请求模块进行指定url的请求发送
        # aiohttp:基于异步网络请求的模块
        # response=requests.get(url=url)
    
        async with aiohttp.ClientSession() as session:
    
            # get()、post()
            # headers:UA伪装、params/data:参数处理、proxy="http://ip:port"
            async with await session.get(url) as response:
                # text():返回字符串类型的响应数据
                # read():返回二进制类型的响应数据
                # json():返回json对象
    
                # 获取响应数据之前一定要使用await进行手动挂起
                text=await response.text()
    
                print(text)
    
    futures=[]
    
    for url in urls:
        c=request(url)
        future=asyncio.ensure_future(c)
        futures.append(future)
    
    loop=asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(futures))
    
    end=time.time()
    
    print(f"总耗时:{end-start}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    可能会遇到的异常及处理办法:Python - Task exception was never retrieved & AttributeError: aexit

    2、selenium

    selenium模块和爬虫之间的关联:

    1. 便捷地获取网站中动态加载的数据
    2. 便捷实现模拟登录

    selenium模块:基于浏览器自动化的模块:通过编写一些python的相关代码,让这些python代码表示一些行为动作,让这些python代码所表示的行为动作触发到浏览器中,然后浏览器根据代码的指示完成相关的自动化操作(测试中也会使用selenium,如果大家感兴趣,可以自行百度)

    selenium的使用流程:

    1. 环境的安装:pip install selenium
    2. 下载浏览器的驱动程序(谷歌浏览器的驱动程序)
      1. 驱动程序的路径:http://chromedriver.storage.googleapis.com/index.html
      2. 驱动程序和浏览器的映射关系:http://blog.csdn.net/huilan_same/article/details/51896672;或者直接在浏览器地址栏输入:chrome://version/
    3. 实例化一个浏览器对象
    4. 编写基于浏览器自动化的操作代码

    火狐浏览器对应的驱动程序:python+selenium+firefox使用与部署详解

    import time
    from selenium import webdriver
    driver = webdriver.Firefox(executable_path='./driver/geckodriver.exe')
    driver.get("https://www.baidu.com/")
    time.sleep(2)
    html = driver.page_source
    print(html)
    driver.quit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    实例:爬取药监管理局信息

    国家药品监督管理局化妆品生产许可信息管理系统服务平台:http://scxk.nmpa.gov.cn:81/xk/

    from selenium import webdriver
    from lxml import etree
    import time
    
    driver = webdriver.Firefox(executable_path='./driver/geckodriver.exe')
    driver.get("http://scxk.nmpa.gov.cn:81/xk/")
    time.sleep(2)
    # page_source:获取浏览器当前页面的页面源码数据
    html = driver.page_source
    
    tree=etree.HTML(html)
    
    contents=tree.xpath('//*[@class="hzblist"]/li')
    
    for content in contents:
        title=content.xpath('./dl/@title')
        print(title)
    
    driver.quit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    实例:淘宝之自动化操作
    # -*- coding: utf-8 -*-
    # @Time    : 2022/10/19 9:23
    # @Author  : 楚楚
    # @File    : 25淘宝.py
    # @Software: PyCharm
    from selenium import webdriver
    import time
    
    browser=webdriver.Firefox(executable_path="./driver/geckodriver.exe")
    
    
    browser.get("https://www.taobao.com/")
    
    # 标签定位
    search_input=browser.find_element_by_id('q')
    # 标签交互
    search_input.send_keys("IPhone13")
    
    # 执行一组js程序
    browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    
    # 点击搜索按钮
    btn=browser.find_element_by_css_selector('.btn-search')
    btn.click()
    
    browser.get('https://www.baidu.com/')
    time.sleep(2)
    # 回退
    browser.back()
    time.sleep(2)
    # 前进
    browser.forward()
    
    
    browser.quit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    基于浏览器自动化的操作代码:

    1. 发起请求:get(url)
    2. 标签定位:find系列的方法
    3. 标签交互:send_keys(‘xxx’)
    4. 执行js程序execute_script(js代码)
    5. 前进、后退:back()、forward()
    6. 关闭浏览器:quit()
    iframe处理+动作链
    # -*- coding: utf-8 -*-
    # @Time    : 2022/10/19 12:11
    # @Author  : 楚楚
    # @File    : 26iframe.py
    # @Software: PyCharm
    import time
    
    from selenium import webdriver
    from selenium.webdriver import ActionChains
    
    driver=webdriver.Firefox(executable_path='./driver/geckodriver.exe')
    
    driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
    
    # 如果定位的标签是存在于iframe标签中,则必须通过如下操作再进行标签定位
    driver.switch_to.frame('iframeResult') # 切换浏览器标签定位的作用域
    div=driver.find_element_by_id('draggable')
    
    # 动作链
    action=ActionChains(driver)
    # 点击长按指定的标签
    action.click_and_hold(div)
    for i in range(5):
        # perform():立即执行动作链操作
        action.move_by_offset(17,0).perform()
        time.sleep(0.5)
    
    action.release()
    
    driver.quit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 如果定位的标签存在于iframe标签中,则必须使用switch_to.frame(id属性值)切换浏览器的标签定位
    • 动作链:from selenium.webDriver import ActionChains
      • 实例化一个动作链对象:action=ActionChains(driver)
      • click_and_hold(标签名称):长按且点击操作
      • move_by_offset(x,y)
      • perform()让动作链立即执行
      • action.release()释放动作链对象
    实例:EI检索
    from selenium import webdriver
    from time import sleep
    
    driver=webdriver.Firefox(executable_path='./driver/geckodriver.exe')
    driver.get("https://www.engineeringvillage.com/search/quick.url")
    
    search=driver.find_element_by_id("search-word-1")
    search.send_keys("industrial design")
    
    button=driver.find_element_by_id('searchBtn')
    button.click()
    
    sleep(10)
    
    driver.quit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    无头浏览器+规避检测
    from selenium import webdriver
    # 实现无可视化界面的
    from selenium.webdriver.firefox.options import Options
    # 规避检测
    from selenium.webdriver import FirefoxOptions
    from selenium.webdriver import FirefoxProfile
    
    # 无可视化界面的操作
    firefox_options = Options()
    firefox_options.add_argument("--headless")
    firefox_options.add_argument("--disable-gpu")
    
    # 实现规避检测(这种方法不晓得生不生效)
    options = FirefoxOptions()
    profile = FirefoxProfile()
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0'
    profile.set_preference("network.proxy.type", 4) #自动检测代理设置
    profile.set_preference("dom.webdriver.enabled", False)  # 设置非driver驱动
    profile.set_preference('useAutomationExtension', False)  # 关闭自动化提示
    profile.update_preferences()  # 更新设置
    
    driver = webdriver.Firefox(executable_path='./driver/geckodriver.exe', firefox_options=firefox_options, options=options,firefox_profile=profile)
    
    # 无可视化界面(无头浏览器) phantomJs
    driver.get("https://www.baidu.com")
    
    print(driver.page_source)
    with open('./baidu.html', 'w', encoding='utf-8') as file:
        file.write(driver.page_source)
    driver.quit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    实例:百度

    通用模板:

    from selenium import webdriver
    # 实现无可视化界面的
    from selenium.webdriver.firefox.options import Options
    # 规避检测
    from selenium.webdriver import FirefoxOptions
    from selenium.webdriver import FirefoxProfile
    
    # 无可视化界面的操作
    firefox_options = Options()
    firefox_options.add_argument("--headless")
    firefox_options.add_argument("--disable-gpu")
    
    # 实现规避检测
    options = FirefoxOptions()
    profile = FirefoxProfile()
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0'
    profile.set_preference("network.proxy.type", 4) #自动检测代理设置
    profile.set_preference("dom.webdriver.enabled", False)  # 设置非driver驱动
    profile.set_preference('useAutomationExtension', False)  # 关闭自动化提示
    profile.update_preferences()  # 更新设置
    
    driver = webdriver.Firefox(executable_path='./driver/geckodriver.exe', firefox_options=firefox_options, options=options,firefox_profile=profile)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    from selenium import webdriver
    # 实现无可视化界面的
    from selenium.webdriver.firefox.options import Options
    # 规避检测
    from selenium.webdriver import FirefoxOptions
    from selenium.webdriver import FirefoxProfile
    
    from lxml import etree
    
    # 无可视化界面的操作
    firefox_options = Options()
    firefox_options.add_argument("--headless")
    firefox_options.add_argument("--disable-gpu")
    
    # 实现规避检测
    options = FirefoxOptions()
    profile = FirefoxProfile()
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0'
    profile.set_preference("network.proxy.type", 4)  # 自动检测代理设置
    profile.set_preference("dom.webdriver.enabled", False)  # 设置非driver驱动
    profile.set_preference('useAutomationExtension', False)  # 关闭自动化提示
    profile.update_preferences()  # 更新设置
    
    driver = webdriver.Firefox(executable_path='./driver/geckodriver.exe', firefox_options=firefox_options, options=options,
                               firefox_profile=profile)
    
    driver.get("https://www.baidu.com/")
    
    # search = driver.find_element_by_id("kw")
    # search.send_keys("湖南大学")
    #
    # btn = driver.find_element_by_id("su")
    # btn.click()
    
    tree = etree.HTML(driver.page_source)
    contents = tree.xpath('//*[@id="hotsearch-content-wrapper"]/li')
    for content in contents:
        href = content.xpath('./a/@href')[0]
        title = content.xpath('./a/span[2]/text()')[0]
        print((href, title))
    
        driver.get(href)
        sub_tree = etree.HTML(driver.page_source)
        data = sub_tree.xpath(
            '/html/body/div[2]/div[4]/div[1]/div[3]/div[1]/div[1]/div/div/div/div/div[2]/p[1]/a/em/text()')[0]
        print(data)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    参考

    1、python3回调函数(callback)

    2、彻底弄懂Python中的回调函数(callback)

    3、python中yield的用法详解——最简单,最清晰的解释

    4、python生成器和迭代器的区别

    5、火狐真机绕过selenium检测

  • 相关阅读:
    vulnhub靶场之HACKSUDO: THOR
    LeetCode刷题记录-简单模块(三)
    复习计算机网络——第一章
    统计信号处理基础 习题解答6-7
    Hadoop的第二个核心组件:MapReduce框架第四节
    何不花个做核酸排队的时间来编译一下Paddle?
    【Java】HashMap 背诵版
    【算法与数据结构】--高级算法和数据结构--高级数据结构
    学习SDN开发工具
    简单工厂模式( Simple Factory Pattern )
  • 原文地址:https://blog.csdn.net/julac/article/details/127407311