<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
<channel>
<atom:link href="https://geekrain.site/feed" rel="self" type="application/rss+xml"/>
<title>吃猫的鱼の总部</title>
<link>https://geekrain.site</link>
<description>长官在这里发号施令</description>
<language>zh-CN</language>
<copyright>© Cloudy </copyright>
<pubDate>Mon, 22 Jun 2026 05:07:41 GMT</pubDate>
<generator>Mix Space CMS (https://github.com/mx-space)</generator>
<docs>https://mx-space.js.org</docs>
<image>
    <url>https://free.picui.cn/free/2026/01/25/69760dea9ac4f.png</url>
    <title>吃猫的鱼の总部</title>
    <link>https://geekrain.site</link>
</image>
<item>
    <title>惑</title>
    <link>https://geekrain.site/notes/7</link>
    <pubDate>Sun, 22 Mar 2026 10:01:39 GMT</pubDate>
    <description>到武汉了，辛好有一个同期实习的小伙伴，不然真的感觉好emo啊

希望明天上班顺利。</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/notes/7'>https://geekrain.site/notes/7</a></blockquote>
      <p>到武汉了，辛好有一个同期实习的小伙伴，不然真的感觉好emo啊</p>
<p>希望明天上班顺利。</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/notes/7#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69bfbe03c464ea83fd82b614</guid>
  <category>notes</category>
false
 </item>
  <item>
    <title>静</title>
    <link>https://geekrain.site/notes/6</link>
    <pubDate>Tue, 10 Mar 2026 16:00:48 GMT</pubDate>
    <description>宿舍的深夜感觉比我在村子里还要安静，明天约了三个实习面试，此时此刻反而有点紧张不起来了，又让我想起了</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/notes/6'>https://geekrain.site/notes/6</a></blockquote>
      <p>宿舍的深夜感觉比我在村子里还要安静，明天约了三个实习面试，此时此刻反而有点紧张不起来了，又让我想起了我一言里记录的那句，事情没到最后一刻，人的脑子总会给你一丝丝美好的幻想，这是什么保护机制吗。真是神奇，不知道说些什么了感觉有点小平静，希望有一个好的结果吧，无论最后成功与否我都要去魏家凉皮买一个</p>
<p>大大大大汉堡</p>
<p>来犒劳犒劳自己，不要抱太高的期望，平常心呀，你还年轻呢~</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/notes/6#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69b04030c464ea83fd82712c</guid>
  <category>notes</category>
false
 </item>
  <item>
    <title>让终端出现一只牛和你说话</title>
    <link>https://geekrain.site/posts/everytingRecoder/xiaowanyi</link>
    <pubDate>Tue, 10 Mar 2026 04:44:12 GMT</pubDate>
    <description>发现了一个好玩的玩意cowsay是一个linux小程序，可以使用ASCII 字符生成小动物输出你想要</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/posts/everytingRecoder/xiaowanyi'>https://geekrain.site/posts/everytingRecoder/xiaowanyi</a></blockquote>
      <p>发现了一个好玩的玩意cowsay是一个linux小程序，可以使用<strong>ASCII 字符</strong>生成小动物输出你想要说的话，话不多说直接开始。</p>
<p>非常简单我们只需要在linux里安装<code>cowsay</code>程序使用以下命令:</p>
<pre><code class="language-shell">sudo apt update
sudo apt install cowsay</code></pre><p>之后在系统环境变量中进行配置</p>
<pre><code class="language-bash">nano ~/.bashrc</code></pre><p>然后在文件最后添加</p>
<pre><code class="language-">export PATH=$PATH:/usr/games
#让配置立即生效
source ~/.bashrc</code></pre><p>之后就可以让控制台出现一头牛了</p>
<pre><code class="language-\">cowsay 'Can you speak Chinese'</code></pre><p>接下来你就可以看到一头牛</p>
<p></p>
<p><code>cowsay</code> 不仅仅只有奶牛，它内置了许多其他动物的字符画。你可以使用 <code>-f</code> (file) 参数来切换角色。</p>
<p>常用角色包括：<code>dragon</code> (龙), <code>turtle</code> (乌龟), <code>tux</code> (Linux企鹅), <code>daemon</code> (BSD恶魔), <code>elephant</code> (大象), <code>kitty</code> (猫) 等。</p>
<p></p>
<p>(啊呀呀，这里Offer打错了，不管了)</p>
<p>还可以让使用管道符来输出命令内容比如ls输出文件目录：</p>
<pre><code class="language-">ls | cowsay</code></pre><p>还有这些，其他可以自行探索，挺好玩的。</p>
<ul>
<li><strong><code>-d</code></strong>：让奶牛呈现“死掉”的状态（眼睛是 X）。</li>
<li><strong><code>-b</code></strong>：让奶牛 Borg 化（像星际迷航里的博格人，眼睛是奇怪的符号）。</li>
<li><strong><code>-y</code></strong>：让奶牛表现出惊讶的样子。</li>
<li><strong><code>-w</code></strong>：设置文本气泡的宽度（防止文字太长换行太多）。</li>
</ul>

      <p style='text-align: right'>
      <a href='https://geekrain.site/posts/everytingRecoder/xiaowanyi#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69afa19cc464ea83fd82694f</guid>
  <category>posts</category>
<category>包罗万象</category>
 </item>
  <item>
    <title>虑</title>
    <link>https://geekrain.site/notes/5</link>
    <pubDate>Mon, 02 Mar 2026 04:47:23 GMT</pubDate>
    <description>回学校了，第一晚，那熟悉的感觉立马就来了，是的没错就是焦虑，在家里不知道为什么，一点感觉没有，太安逸</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/notes/5'>https://geekrain.site/notes/5</a></blockquote>
      <p>回学校了，第一晚，那熟悉的感觉立马就来了，是的没错就是焦虑，在家里不知道为什么，一点感觉没有，太安逸了，那种感觉，提不起一点劲来，就想吃睡躺。</p>
<p>但是一到学校我就失眠了，昨天三点才睡，莫名奇妙，心里十分烦躁，一直规劝自己说焦虑没有任何作用，自己心里其实也都明白，但是还是止不住的去想那些，难受啊，不过也好，这样才有动力嘛，瞬间干啥都有动力了。所以说人真奇怪，有时候你情绪到那里了真的是无法控制，怪不得那么多人都说什么修身养性啥的，能合理的控制自己的情绪也是一种很强大的能力。</p>
<p>唯一能解决我这种心情的办法就是找个实习了，我的目标也是这个。虽然知道就算找到了，还可能会有新的问题,实际也可能和你想的不一样，不过这些之后再说吧，一定要找到实习，先解决当下这个困扰我已久的问题。</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/notes/5#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69a5165bc464ea83fd82251c</guid>
  <category>notes</category>
false
 </item>
  <item>
    <title>Selenium学习笔记</title>
    <link>https://geekrain.site/posts/codeRecoder/Selenium</link>
    <pubDate>Fri, 27 Feb 2026 09:21:04 GMT</pubDate>
    <description>简介
本文借鉴了b站up白月黑羽的Selenium教学文档，对于其中内容进行总结修改cv等操作整合为</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/posts/codeRecoder/Selenium'>https://geekrain.site/posts/codeRecoder/Selenium</a></blockquote>
      <h1>简介</h1>
<blockquote>
<p>本文借鉴了b站up白月黑羽的Selenium教学文档，对于其中内容进行总结修改cv等操作整合为这篇便于我进行复习的文档,感谢开源大佬。</p>
</blockquote>
<p>Selenium是目前主流的web浏览器自动化方案，通过他我们可以写出自动化程序，让他帮我们操作浏览器，就像你小时候玩部落冲突用红手指里面的游戏脚本，帮你刷经济那样，自动进行控制。</p>
<p>他还可以爬去网页数据，从web界面获取信息，然后在用程序进行分析处理。</p>
<p>自动化原理图如下：</p>
<img src="https://api.geekrain.site/api/v2/objects/file/b1s2xyo0ccril87ogs.png" width="300" style="float: left; margin-right: 20px;">

<p>大概解释一下，Selenium的自动化执行流程：</p>
<ol>
<li>自动化程序调用Selenium 客户端库函数（比如点击按钮元素）</li>
<li>客户端库会发送Selenium 命令 给浏览器的驱动程序</li>
<li>浏览器驱动程序接收到命令后 ,驱动浏览器去执行命令</li>
<li>浏览器执行命令</li>
<li>浏览器驱动程序获取命令执行的结果，返回给我们自动化程序</li>
<li>自动化程序对返回结果进行处理</li>
</ol>
<p>消息收发是通过Selenium客户端库底层封装好的，我们只需要进行调用即可，不同的编程语言，官方有对应的库来方便开发者使用。</p>
<p>安装使用方面：</p>
<p>环境安装需要三个东西，<strong>Selenium客户端库</strong>，<strong>浏览器驱动</strong>,<strong>浏览器</strong>。</p>
<p>客户端库方面对于Python直接用pip命令进行安装即可，</p>
<pre><code class="language-shell">pip install selenium</code></pre><p>安装慢就可以使用国内清华大学源</p>
<pre><code class="language-shell">pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple</code></pre><p>浏览器驱动现在新版Python Selenium客户端库内置了一个selenium manager 工具，运行时他会帮我们自动下载浏览器驱动和浏览器。</p>
<p>但是需要注意国内的网络原因，不然下载速度会很慢或者直接下载失败，可以通过手动配置镜像，或者手动下载在安装来解决。</p>
<pre><code class="language-">set SE_DRIVER_MIRROR_URL=https://cdn.npmmirror.com/binaries/chrome-for-testing</code></pre><p>上述代码可以给当前命令窗口设置临时的环境变量镜像。</p>
<blockquote>
<p>我在项目虚拟环境中安装Selenium客户端，导入依赖Pycharm报错提示未找到，这是因为我没有切换我的解释器，我仍然使用的是我的全局Python解释器，而不是我虚拟环境中安装了Selenium客户端库的解释器，在设置里添加一下就好了。</p>
</blockquote>
<h2>安装好了以后编写了一个简单的示例如下</h2>
<pre><code class="language-python">from selenium import webdriver
# 创建webdriver对象
web = webdriver.Edge()
#打开我的博客网站
web.get('https://www.geekrain.site')
#程序运行完成之后会自动关闭浏览器，就是很多人说的闪退
#这里加上一个等待用户输入，防止闪退
input('按回车退出')

#关闭浏览器窗口
web.quit()</code></pre><p>感觉这个成就感比我写出后端哪些增删改查接口要大，这就是控制的感觉吗。感觉我从小不管是玩游戏还是玩玩具都喜欢那种能自行操控的东西，比如游戏里的坦克，坐骑，现实生活里的小汽车，无人机，还有游戏脚本，还记得当时玩bekemmo那个游戏，自己用一键玩脚本简单的写了一个刷经验的脚本，看着他动起来真的很开心，真是怀念呀，当时还是超级破烂手机，电池也不行，屏幕也碎了，还没有root,要装一个虚拟机在那个虚拟环境里才有root权限可以弄脚本，手机给我搞得超级烫，那时候就挺喜欢折腾了。</p>
<h3>小问题：</h3>
<h4>1.关闭日志</h4>
<p>写好自动化程序之后，在终端运行打印出很多浏览器驱动发出的日志，大部分都是无用的可以这样关闭</p>
<p>可以这样关闭</p>
<pre><code class="language-py">from selenium import webdriver
 
# 加上参数，禁止 chromedriver 日志写屏
options = webdriver.ChromeOptions()
options.add_experimental_option(
    'excludeSwitches', ['enable-logging'])
 
# 这里指定 options 参数
wd = webdriver.Chrome(options=options)</code></pre><p>Edge浏览器驱动，也可以使用这个参数，比如</p>
<pre><code class="language-py">wd = webdriver.Edge(options=options)</code></pre><h4>2.病毒重置</h4>
<p>有的朋友的电脑上Selenium自动化时，浏览器会弹出防病毒重置设置，解决方法如下</p>
<ul>
<li>命令行输入 <code>regedit</code> ，运行注册表编辑器</li>
<li>在左边的目录树找到 <code>HKEY_CURRENT_USER\Software\Google\Chrome</code></li>
<li>删除其下的 <code>TriggeredReset</code> 子项</li>
<li>关闭 注册表编辑器</li>
</ul>
<h1>选择元素的基本方法</h1>
<p>自动化的操作重要的就是定位元素，元素定位之后执行相关的自动化操作，所以这一部分是重点，感觉和前端css样式里面一样，里面的选择器语法也是很重要的部分。</p>
<h2>在浏览器上选择元素的方法</h2>
<p>使用F12开发者工具，快捷键ctrl + shift + c后可以通过鼠标来选定元素，会给出页面的源码。</p>
<p>除了上面的方法，还可以选择元素之后邮件，点击检查选项也可以实现。</p>
<h2>根据ID属性选择元素</h2>
<p>很多元素，为了更好的操作，定义css样式，会添加id属性，我们可以根据这个id属性来定位页面元素，使用方法如下：</p>
<pre><code class="language-python">from selenium import webdriver
from selenium.webdriver.common.by import By

# 创建webdriver对象
web = webdriver.Edge()
#访问网址
web.get('https://www.byhy.net/cdn2/files/selenium/stock1.html')
#定位页面元素位置
element = web.find_element(By.ID,'kw')
#键入信息
element.send_keys('通讯')
input('输入回车关闭浏览器页面')
#关闭浏览器窗口
pass</code></pre><p>浏览器 找到id为kw的元素后，将结果通过 浏览器驱动 返回给 自动化程序， 所以 find_element 方法会 返回一个 <strong>WebElement 类型的对象</strong>。</p>
<p>这个WebElement 对象可以看成是对应 <strong>页面元素</strong>的遥控器。</p>
<p>我们通过这个WebElement对象，就可以<strong>操控</strong> 对应的界面元素。</p>
<p>但是如果没有这个传入的Id,找不到元素，就会抛出<code>selenium.common.exceptions.NoSuchElementException</code>,在一些特殊场景，可以补货该异常来执行后续操作。</p>
<h2>根据Class属性选择元素</h2>
<p>勾起了我学习前端时的记忆，有时候一些元素属于同一种类型，就用class属性来进行分类，形式如下：</p>
<pre><code class="language-html">&lt;body&gt;
        <div><span>土豆</span></div>
        <div><span>洋葱</span></div>
        <div><span>白菜</span></div>

        <div><span>狮子</span></div>
        <div><span>老虎</span></div>
        <div><span>山羊</span></div>

    &lt;/body&gt;</code></pre><p>如果我们要选中所有的动物元素，就可以像这样写:<code>wd.find_elements(By.CLASS_NAME, &#39;animal&#39;)</code></p>
<p><strong>find_elements</strong> 返回的是找到的符合条件的 <code>所有</code> 元素 (这里有3个元素)， 放在一个 <strong>列表</strong> 中返回。</p>
<p><strong>而如果我们使用 <code>wd.find_element</code> (注意少了一个s) 方法， 就只会返回第一个元素.</strong></p>
<p>演示代码如下：</p>
<pre><code class="language-python">from selenium import webdriver
from selenium.webdriver.common.by import By
# 创建webdriver对象
web = webdriver.Edge()
web.get('https://www.byhy.net/cdn2/files/selenium/sample1.html')
#程序运行完成之后会自动关闭浏览器，就是很多人说的闪退
#获取Class属性为动物的信息
elements = web.find_elements(By.CLASS_NAME,'animal')
#获取输入框
shuru = web.find_element(By.ID,'searchtext')
#便利动物信息然后输入到输入框中
for elements in elements:
    shuru.send_keys(elements.text)
#这里加上一个等待用户输入，防止闪退
input('输入回车')
#关闭浏览器窗口
pass</code></pre><p>Class类型定义的时候，不知可以定义多个，就那一个人来举例人可以有多个标签，中国人 ，学生，青少年这种，这时候如果我们选择到这种有多个Class属性的元素，只要指定其中一个属性就可以选到这个元素</p>
<pre><code class="language-html"><span>张三</span></code></pre><p>我们要用代码选择这个元素，可以指定任意一个class 属性值，都可以选择到这个元素，如下</p>
<pre><code class="language-python">element = wd.find_elements(By.CLASS_NAME,'chinese')</code></pre><p>或者</p>
<pre><code class="language-python">element = wd.find_elements(By.CLASS_NAME,'student')</code></pre><p>而不能这样写</p>
<pre><code class="language-python">element = wd.find_elements(By.CLASS_NAME,'chinese student')</code></pre><h2>根据Tag名选择元素</h2>
<p>Tag就是标签属性例如div,span这种。</p>
<p>类似的，我们可以通过指定 参数为 <code>By.TAG_NAME</code> ，选择所有的tag名为 div的元素，如下所示</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('https://www.byhy.net/cdn2/files/selenium/sample1.html')

# 根据 tag name 选择元素，返回的是 一个列表
# 里面 都是 tag 名为 div 的元素对应的 WebElement对象
elements = wd.find_elements(By.TAG_NAME, 'div')

# 取出列表中的每个 WebElement对象，打印出其text属性的值
# text属性就是该 WebElement对象对应的元素在网页中的文本内容
for element in elements:
    print(element.text)

input('按回车退出')     </code></pre><h2>find_element和find_elements的区别</h2>
<p>使用 <code>find_elements</code> 选择的是符合条件的 <code>所有</code> 元素， 如果没有符合条件的元素， <code>返回空列表</code></p>
<p>使用 <code>find_element</code> 选择的是符合条件的 <code>第一个</code> 元素， 如果没有符合条件的元素， <code>抛出 NoSuchElementException 异常</code></p>
<h2>通过WebElement对象选择元素</h2>
<p>WebDriver对象现有选择元素的方法，Webelement对象也有选择元素的方法，选择的范围有区别</p>
<p>WebDriver:选择范围是整个web页面</p>
<p>WebElement:选择范围是这个元素的内部（他也可以调用find_elements,find_elements这些方法）</p>
<pre><code class="language-python">from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('https://www.byhy.net/cdn2/files/selenium/sample1.html')

element = wd.find_element(By.ID,'container')

# 限制 选择元素的范围是 id 为 container 元素的内部。
spans = element.find_elements(By.TAG_NAME, 'span')
for span in spans:
    print(span.text)

input('按回车退出')</code></pre><h1>等待元素出现</h1>
<p>在一些时候，比如查询数据的情况，数据并不能第一时间就被获取然显示到页面，中间是有一定时间隔的，要从数据库查，也可能会受送到网速的影响。如果这时候调用元素查找，会报未找到元素的异常错误<code>NoSuchElementException</code> 。</p>
<p>除了最简单的使用时间函数让程序在点击查询之后休眠一定时间，弊端也显而易见，你并不能确定等待的时间是多少。</p>
<p>Selenium提供了一个合理的解决方法，隐式等待：</p>
<p>原理就是当发现元素没有找到的时候，并不是立即返回找不到的错误，而是执行一个循环以半秒为一个周期重新查找，直到找到那个元素，或者在等到超过最大等待时间抛出未找到错误（如果是find_elements之类的方法，则是返回一个空列表）</p>
<p>该方法接受一个参数， 用来指定 最大等待时长。</p>
<p><code>wd.implicitly_wait(10)</code></p>
<blockquote>
<p>小贴士：一般在创建了WebDriver对象之后，就可以在后后面加上这句隐式等待方法。</p>
</blockquote>
<p>并不是有了这个方法，<code>time.sleep()</code>这个函数就没有用处了，<code>impolcitly_wait</code>这个函数他只会在元素没找到的时候执行，在一些特殊情况，比如下拉列表，元素是存在的，只是因为点击的时候页面渲染差了一点时间没有找到对应的元素就会报错，这个时候就要，自己定义休眠时间，让程序停下来一会儿。</p>
<h1>操作元素的基本方法</h1>
<p>获取到我们想要的元素之后，代码就会返回WebElement对象，通过这个对象，我们就可以操控元素了</p>
<p>操作通常包括：</p>
<ul>
<li>点击元素</li>
<li>在元素中输入字符串</li>
<li>获取元素包含的信息，例如文本内容，元素的属性</li>
</ul>
<h2>点击元素</h2>
<p>调用WebElement对象的click方法</p>
<p>调用这个方法，浏览器接受自动化命令点击的是该元素的中心点位置。</p>
<h2>输入框</h2>
<p>调用元素WebElement对象的send_keys方法</p>
<p>如果要吧输入框中已经有的内容清除掉，则使用WebElement对象的clear方法</p>
<h2>获取元素信息</h2>
<h3>获取元素的文本内容</h3>
<p>上一章，我们已经知道，通过WebElement对象的 <code>text</code> 属性，可以获取元素 <code>展示在界面上的</code> 文本内容。</p>
<p>比如</p>
<pre><code class="language-py">element = wd.find_element(By.ID, 'animal')
print(element.text)</code></pre><h3>获取元素属性</h3>
<p>通过WebElement对象的 <code>get_attribute</code> 方法来获取元素的属性值</p>
<p>比如要获取元素属性class的值，就可以使用 <code>element.get_attribute(&#39;class&#39;)</code></p>
<p>如下：</p>
<pre><code class="language-python">element = wd.find_element(By.ID, 'input_name')
print(element.get_attribute('class'))</code></pre><p>执行完自动化代码，如果想关闭浏览器窗口可以调用WebDriver对象的 quit 方法，像这样 wd.quit()</p>
<h3>获取整个元素对应的HTML</h3>
<p>要获取整个元素对应的HTML文本内容，可以使用 <code>element.get_attribute(&#39;outerHTML&#39;)</code></p>
<p>如果，只是想获取某个元素 <code>内部</code> 的HTML文本内容，可以使用 <code>element.get_attribute(&#39;innerHTML&#39;)</code></p>
<h3>获取输入框里面的文字</h3>
<p>对于input输入框的元素，要获取里面的输入文本，用text属性是不行的，这时可以使用 <code>element.get_attribute(&#39;value&#39;)</code></p>
<p>比如</p>
<pre><code class="language-python">element = wd.find_element(By.ID, "input1")
print(element.get_attribute('value')) # 获取输入框中的文本</code></pre><h3>获取元素文本内容2</h3>
<p>通过WebElement对象的 <code>text</code> 属性，可以获取元素 <code>展示在界面上的</code> 文本内容。</p>
<p>但是，有时候，元素的文本内容没有展示在界面上，或者没有完全完全展示在界面上。 这时，用WebElement对象的text属性，获取文本内容，就会有问题。</p>
<p>出现这种情况，可以尝试使用 <code>element.get_attribute(&#39;innerText&#39;)</code> ，或者 <code>element.get_attribute(&#39;textContent&#39;)</code></p>
<p>使用 innerText 和 textContent 的区别是，前者只显示元素可见文本内容，后者显示所有内容（包括display属性为none的部分）</p>
<h1>CSS表达式基础</h1>
<p>在我自己做了一些练习之后，我深刻的认识到普通元素选择的局限性，遇到复杂的页面，要选到对应的元素非常繁琐，还有一种无从下手之感,前端中可以通过</p>
<p><strong>Css selector(选择器)</strong> 语法选择元素，那么自然在自动化中也可以使用这个语法来确定元素(熟悉的感觉，仿佛回到当时打比赛库库写前端页面的时候，虽然也没啥用)。</p>
<h2>根据 tag名、id、class 选择元素</h2>
<p>CSS Selector 同样可以根据tag名、id 属性和 class属性 来 选择元素，</p>
<p>根据 tag名 选择元素的 CSS Selector 语法非常简单，直接写上tag名即可，</p>
<p>比如 要选择 所有的tag名为div的元素，就可以是这样</p>
<pre><code class="language-python">elements = wd.find_elements(By.CSS_SELECTOR, 'div')</code></pre><p>等价于</p>
<pre><code class="language-python">elements = wd.find_elements(By.TAG_NAME, 'div')</code></pre><hr>
<p>根据id属性 选择元素的语法是在id号前面加上一个井号： <code>#id值</code></p>
<p>比如 <a href="https://www.byhy.net/cdn2/files/selenium/sample1.html">请点击打开这个网址</a></p>
<p>有下面这样的元素：</p>
<pre><code class="language-html">&lt;input  type="text" id='searchtext' /&gt;</code></pre><p>就可以使用 <code>#searchtext</code> 这样的 CSS Selector 来选择它。</p>
<p>比如，我们想在 <code>id 为 searchtext</code> 的输入框中输入文本 <code>你好</code> ，完整的Python代码如下</p>
<pre><code class="language-python">from selenium import webdriver
from selenium.webdriver.common.by import By
 
wd = webdriver.Chrome()
 
wd.get('https://www.byhy.net/cdn2/files/selenium/sample1.html')
 
element = wd.find_element(By.CSS_SELECTOR, '#searchtext')
element.send_keys('你好')</code></pre><hr>
<p>根据class属性 选择元素的语法是在 class 值 前面加上一个点： <code>.class值</code></p>
<p>要选择<strong>所有</strong> class 属性值为 animal的元素 动物 除了这样写</p>
<pre><code class="language-py">elements = wd.find_elements(By.CLASS_NAME, 'animal')</code></pre><p>还可以这样写</p>
<pre><code class="language-py">elements = wd.find_elements(By.CSS_SELECTOR, '.animal')</code></pre><p>因为是选择 <code>所有</code> 符合条件的 ，所以用 <code>find_elements</code> 而不是 <code>find_element</code></p>
<p><strong>总的来说就是Tag 直接输入，标签名 Class 前面加个.然后输入class名，id的话输入之前加个#号</strong></p>
<h2>选择子元素和后代元素</h2>
<p>简单比喻一下，这个子元素和后代元素以及父元素他们的关系可以想象成一个笔袋，最大的元素是笔袋，笔袋里面直接的装了很多笔，这些笔称之为笔袋的子元素，而中性笔里面又装着可替换式的笔芯，笔芯就是笔袋的后代元素，笔袋则是笔芯的祖宗元素。</p>
<p>语法规则如下。</p>
<p>如果 <code>元素2</code> 是 <code>元素1</code> 的 直接子元素， CSS Selector 选择子元素的语法是这样的</p>
<pre><code class="language-undefined">元素1 &gt; 元素2</code></pre><p>中间用一个大于号 （我们可以理解为箭头号）</p>
<p>注意，最终选择的元素是 <strong>元素2</strong>， 并且要求这个 <strong>元素2</strong> 是 <strong>元素1</strong> 的直接子元素</p>
<p>也支持更多层级的选择， 比如</p>
<pre><code class="language-undefined">元素1 &gt; 元素2 &gt; 元素3 &gt; 元素4</code></pre><p>就是选择 <code>元素1</code> 里面的子元素 <code>元素2</code> 里面的子元素 <code>元素3</code> 里面的子元素 <code>元素4</code> ， 最终选择的元素是 <strong>元素4</strong></p>
<p>如果 <code>元素2</code> 是 <code>元素1</code> 的 后代元素， CSS Selector 选择后代元素的语法是这样的</p>
<pre><code class="language-undefined">元素1   元素2</code></pre><p>中间是一个或者多个空格隔开</p>
<p>最终选择的元素是 <strong>元素2</strong> ， 并且要求这个 <strong>元素2</strong> 是 <strong>元素1</strong> 的后代元素。</p>
<p>也支持更多层级的选择， 比如</p>
<pre><code class="language-undefined">元素1   元素2   元素3  元素4</code></pre><p>最终选择的元素是 <strong>元素4</strong></p>
<p>CSS 支持 大于号和 空格 混合使用，比如</p>
<pre><code class="language-py">元素1 &gt;  元素2   元素3 &gt; 元素4</code></pre><p>其中每个元素的写法 可以是 <strong>复合写法</strong> ，比如</p>
<pre><code class="language-css">span.date  // 选择 class 值有 date 的 span 元素

div#bottom // 选择 id 为 bottom 的 div 元素

div#bottom.date // 选择 id 为 bottom, class 值有 date, 的 div 元素</code></pre><h2>根据属性选择</h2>
<p><strong>简单来说[ ]的作用是能通过HTML标签内部的“属性”和“属性值”来进行定位。</strong></p>
<p>id、class 都是web元素的 <code>属性</code> ，因为它们是很常用的属性，所以css选择器专门提供了根据 id、class 选择的语法。</p>
<p>那么其他的属性呢？</p>
<pre><code class="language-html"><a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a></code></pre><p>里面根据 href选择，可以用css 选择器吗？</p>
<p>当然可以！</p>
<p>css 选择器支持通过任何属性来选择元素，语法是用一个方括号 <code>[]</code> 。</p>
<p>比如要选择上面的a元素，就可以使用 <code>[href=&quot;http://www.miitbeian.gov.cn&quot;]</code> 。</p>
<p>这个表达式的意思是，选择 属性href值为 <code>http://www.miitbeian.gov.cn</code> 的元素。</p>
<p>完整代码如下</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('https://www.byhy.net/cdn2/files/selenium/sample1.html')

# 根据属性选择元素
element = wd.find_element(By.CSS_SELECTOR, '[href="http://www.miitbeian.gov.cn"]')

# 打印出元素对应的html
print(element.get_attribute('outerHTML'))</code></pre><p>当然，前面可以加上标签名的限制，比如 <code>div[class=&#39;SKnet&#39;]</code> 表示 选择所有 标签名为div，且class属性值为SKnet的元素。</p>
<p>属性值用单引号，双引号都可以。</p>
<p>根据属性选择，还可以不指定属性值，比如 <code>[href]</code> ， 表示选择 所有 具有 属性名 为href 的元素，不管它们的值是什么。</p>
<p>CSS 还可以选择 属性值 <code>包含</code> 某个字符串 的元素</p>
<p>比如， 要选择a节点，里面的href属性包含了 miitbeian 字符串，就可以这样写</p>
<pre><code class="language-css">a[href*="miitbeian"]</code></pre><p>还可以 选择 属性值 以某个字符串 <code>开头</code> 的元素</p>
<p>比如， 要选择a节点，里面的href属性以 http 开头 ，就可以这样写</p>
<pre><code class="language-css">a[href^="http"]</code></pre><p>还可以 选择 属性值 以某个字符串 <code>结尾</code> 的元素</p>
<p>比如， 要选择a节点，里面的href属性以 gov.cn 结尾 ，就可以这样写</p>
<pre><code class="language-css">a[href$="gov.cn"]</code></pre><p>如果一个元素具有多个属性</p>
<pre><code class="language-html"><div>沙漠之鹰</div></code></pre><p>CSS 选择器 可以指定 选择的元素要 同时具有多个属性的限制，像这样 <code>div[class=misc][ctype=gun]</code></p>
<blockquote>
<p>在使用[ ]来使用累不属性的时候如果想根据value获取到对应元素，需要这个元素本生就含有value这个属性，而不是被元素标签包裹的文本内容，如果是后者，那么就不能通过value找到元素。</p>
</blockquote>
<h2>验证选择语法是否输入正确选到了我们需要的元素</h2>
<p>打开开发者工具在元素页面使用快捷键ctrl + f 就可以打开一个输入框，在里面键入Css selector语法就可以验证自己输的对不对。</p>
<h1>CSS表达式进阶</h1>
<p>有点意思</p>
<h2>组选择</h2>
<p>如果想要同时选择class为plant和calss为animal的元素</p>
<p>可以使用逗号 ， 隔开形如：</p>
<p><code>.plant , .animal</code></p>
<h2>按次序选择子节点</h2>
<p>这个我之前犯了很大一个错误，举个例子</p>
<p><code>span:nth-child(2)</code></p>
<p>这句的意思我以前都是认为找span节点的第二个节点</p>
<p>但实际上意思是，找到所有子节点的第二个节点，且该节点元素必须是span</p>
<p>如果想要实现上一句话的操作正确的语句应该是</p>
<p><code>span:nth-of-type(2)</code></p>
<p>感觉这个我还会混淆，重点记忆。</p>
<h3>父元素的倒数第n个子节点</h3>
<p>比如：</p>
<pre><code class="language-html">p:nth-last-child(1)</code></pre><p>就是选择第倒数第1个子元素，并且是p元素</p>
<h3>父元素的第几个某类型的子节点</h3>
<p>就是我上面提了一嘴的，</p>
<p>我们可以指定选择的元素 是父元素的第几个 <code>某类型的</code> 子节点</p>
<p>使用 <code>nth-of-type</code></p>
<p>比如，</p>
<p>我们要选择 唐诗 和宋词 的第一个 作者，</p>
<p>可以像上面那样思考：选择的是 第2个子元素，并且是span类型</p>
<p>所以这样可以这样写 <code>span:nth-child(2)</code> ，</p>
<p>还可以这样思考，选择的是 <code>第1个span类型</code> 的子元素</p>
<p>所以也可以这样写 <code>span:nth-of-type(1)</code></p>
<h3>父元素的倒数第几个某类型的子节点</h3>
<p>当然也可以反过来， 选择父元素的 <code>倒数第几个某类型</code> 的子节点</p>
<p>使用 <code>nth-last-of-type</code></p>
<p>像这样</p>
<pre><code class="language-html">p:nth-last-of-type(2)</code></pre><h3>奇数节点和偶数节点</h3>
<p>如果要选择的是父元素的 <code>偶数节点</code>，使用 <code>nth-child(even)</code></p>
<p>比如</p>
<pre><code class="language-html">p:nth-child(even)</code></pre><p>如果要选择的是父元素的 <code>奇数节点</code>，使用 <code>nth-child(odd)</code></p>
<pre><code class="language-html">p:nth-child(odd)</code></pre><p>如果要选择的是父元素的 <code>某类型偶数节点</code>，使用 <code>nth-of-type(even)</code></p>
<p>如果要选择的是父元素的 <code>某类型奇数节点</code>，使用 <code>nth-of-type(odd)</code></p>
<h3>相邻兄弟节点选择</h3>
<p>上面的例子里面，我们要选择 唐诗 和宋词 的第一个 作者</p>
<p>还有一种思考方法，就是选择 h3 <code>后面紧跟着的兄弟节点</code> span。</p>
<p>这就是一种 相邻兄弟 关系，可以这样写 <code>h3 + span</code></p>
<p>表示元素 紧跟关系的 是 <code>加号</code></p>
<h3>后续所有兄弟节点选择</h3>
<p>如果要选择是 选择 h3 <code>后面所有的兄弟节点</code> span，可以这样写 <code>h3 ~ span</code></p>
<h1>Frame切换</h1>
<p>在Html文档中，frame元素h和iframe元素比较特殊，左右同时<strong>嵌入</strong>另一份Html文档，简单来说就是网页嵌入，在使用Selenium操作网页的时候默认是操作最外层的Html,并不包含被嵌入的Htm文档内容，如果这时候要锁定被嵌入的HTML文档中的元素就要切换操作范围到被嵌入的文档中去</p>
<p>使用WebDriver对象的switch_to属性,</p>
<p><code>wd.switch_to.frame(&#39;frame_reference&#39;)</code></p>
<p>其中， frame_reference 可以是 frame 元素的属性 name 或者 ID 。</p>
<p>比如这里，就可以填写 iframe元素的id &#39;frame1&#39; 或者 name属性值 &#39;innerFrame&#39;。</p>
<p>像这样</p>
<pre><code class="language-py">wd.switch_to.frame('frame1')</code></pre><p>或者</p>
<pre><code class="language-py">wd.switch_to.frame('innerFrame')</code></pre><p>也可以填写frame 所对应的 WebElement 对象。</p>
<p>我们可以根据frame的元素位置或者属性特性，使用find系列的方法，选择到该元素，得到对应的WebElement对象</p>
<p>比如，这里就可以写</p>
<pre><code class="language-py">wd.switch_to.frame(wd.find_element(By.TAG_NAME, "iframe"))</code></pre><p>然后，就可以进行后续操作frame里面的元素了。</p>
<p>上面的例子的正确代码如下</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('https://www.byhy.net/cdn2/files/selenium/sample2.html')


# 先根据name属性值 'innerFrame'，切换到iframe中
wd.switch_to.frame('innerFrame')

# 根据 class name 选择元素，返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')

for element in elements:
    print(element.text)</code></pre><hr>
<p>如果我们已经切换到某个iframe里面进行操作了，那么后续选择和操作界面元素 就都是在这个frame里面进行的。</p>
<p>这时候，如果我们又需要操作 <strong>主html</strong> （我们把最外层的html称之为 <code>主html</code> ） 里面的元素了呢？</p>
<p>怎么切换回原来的 <code>主html</code> 呢？</p>
<p>很简单，写如下代码即可</p>
<pre><code class="language-py">wd.switch_to.default_content()</code></pre><p>例如，在上面 代码 操作完 frame里面的元素后， 需要 点击 <code>主html</code> 里面的按钮，就可以这样写</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('https://www.byhy.net/cdn2/files/selenium/sample2.html')


# 先根据name属性值 'innerFrame'，切换到iframe中
wd.switch_to.frame('innerFrame')

# 根据 class name 选择元素，返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')

for element in elements:
    print(element.text)

# 切换回 最外部的 HTML 中
wd.switch_to.default_content()

# 然后再 选择操作 外部的 HTML 中 的元素
wd.find_element_by_id('outerbutton').click()

wd.quit()</code></pre><h1>窗口切换</h1>
<p>在一些网页中会遇到一些页面跳转按钮，这个时候，WebDriver对象还是老窗口对象，自动化操作也还是在老窗口进行</p>
<p>如果我们想要切换到新的窗口</p>
<p>可以使用Webdriver对象的switch_to属性的 window方法，如下所示：</p>
<pre><code class="language-py">wd.switch_to.window(handle)</code></pre><p>其中，参数handle需要传入什么呢？</p>
<p>WebDriver对象有window_handles 属性，这是一个列表对象， 里面包括了当前浏览器里面<strong>所有的窗口句柄</strong>。</p>
<p>所谓句柄，大家可以想象成对应网页窗口的一个ID，</p>
<p>那么我们就可以通过 类似下面的代码，</p>
<pre><code class="language-py">for handle in wd.window_handles:
    # 先切换到该窗口
    wd.switch_to.window(handle)
    # 得到该窗口的标题栏字符串，判断是不是我们要操作的那个窗口
    if 'Bing' in wd.title:
        # 如果是，那么这时候WebDriver对象就是对应的该该窗口，正好，跳出循环，
        break</code></pre><p>上面代码的用意就是：</p>
<p>我们依次获取 wd.window_handles 里面的所有 句柄 对象， 并且调用 wd.switch_to.window(handle) 方法，切入到每个窗口，</p>
<p>然后检查里面该窗口对象的属性（可以是标题栏，地址栏），判断是不是我们要操作的那个窗口，如果是，就跳出循环。</p>
<hr>
<p>同样的，如果我们在新窗口 操作结束后， 还要回到原来的窗口，该怎么办？</p>
<p>我们可以仍然使用上面的方法，依次切入窗口，然后根据 标题栏 之类的属性值判断。</p>
<p>还有更省事的方法。</p>
<p>因为我们一开始就在 原来的窗口里面，我们知道 进入新窗口操作完后，还要回来，可以事先 保存该老窗口的 句柄，使用如下方法</p>
<pre><code class="language-py"># mainWindow变量保存当前窗口的句柄
mainWindow = wd.current_window_handle</code></pre><p>切换到新窗口操作完后，就可以直接像下面这样，将driver对应的对象返回到原来的窗口</p>
<pre><code class="language-py">#通过前面保存的老窗口的句柄，自己切换到老窗口
wd.switch_to.window(mainWindow)</code></pre><h1>选择框</h1>
<p>常见HTML选择框有：radio、checkbox、select框</p>
<h2>redio</h2>
<p>radio框选择选项，直接用WebElement的click方法，模拟用户点击就可以了。</p>
<p>比如, 我们要在下面的html中：</p>
<ul>
<li>先打印当前选中的老师名字</li>
<li>再选择 小雷老师</li>
</ul>
<pre><code class="language-html"><div>
  &lt;input type="radio" name="teacher" value="小江老师"&gt;小江老师<br>
  &lt;input type="radio" name="teacher" value="小雷老师"&gt;小雷老师<br>
  &lt;input type="radio" name="teacher" value="小凯老师" checked="checked"&gt;小凯老师
</div></code></pre><p>对应的代码如下</p>
<pre><code class="language-python"># 获取当前选中的元素
element = wd.find_element(By.CSS_SELECTOR, 
  '#s_radio input[name="teacher"]:checked')
print('当前选中的是: ' + element.get_attribute('value'))
 
# 点选 小雷老师
wd.find_element(By.CSS_SELECTOR, 
  '#s_radio input[value="小雷老师"]').click()
 </code></pre><p>其中 <code>#s_radio input[name=&quot;teacher&quot;]:checked</code> 里面的 <code>:checked</code> 是CSS伪类选择</p>
<p>表示选择 <code>checked</code> 状态的元素，对 <code>radio</code> 和 <code>checkbox</code> 类型的input有效</p>
<h2>checkbox框</h2>
<p>对checkbox进行选择，也是直接用 WebElement 的 click 方法，模拟用户点击选择。</p>
<p>需要注意的是，要选中checkbox的一个选项，必须 <code>先获取当前该复选框的状态</code> ，如果该选项已经勾选了，就不能再点击。否则反而会取消选择。</p>
<p>比如, 我们要在下面的html中：选中 小雷老师</p>
<pre><code class="language-html"><div>
  &lt;input type="checkbox" name="teachers1" value="小江老师"&gt;小江老师<br>
  &lt;input type="checkbox" name="teachers1" value="小雷老师"&gt;小雷老师<br>
  &lt;input type="checkbox" name="teachers1" value="小凯老师" checked="checked"&gt;小凯老师
</div></code></pre><p>我们的思路可以是这样：</p>
<ul>
<li>先把 已经选中的选项全部点击一下，确保都是未选状态</li>
<li>再点击 小雷老师</li>
</ul>
<p>示例代码</p>
<pre><code class="language-py"># 先把 已经选中的选项全部点击一下
elements = wd.find_elements(By.CSS_SELECTOR, 
  '#s_checkbox input[name="teachers1"]:checked')

for element in elements:
    element.click()

# 再点击 小雷老师
wd.find_element(By.CSS_SELECTOR, 
  "#s_checkbox input[value='小雷老师']").click()</code></pre><h2>select框</h2>
<p>radio框及checkbox框都是input元素，只是里面的type不同而已。</p>
<p>select框 则是一个新的select标签，大家可以对照浏览器网页内容查看一下</p>
<p>对于Select 选择框， Selenium 专门提供了一个 <code>Select类</code> 进行操作，可以这样导入</p>
<pre><code class="language-py">from selenium.webdriver.support.select import Select</code></pre><p>这个类实例化时传入 Select元素对应的 WebElement 对象，比如</p>
<pre><code class="language-py">select = Select(wd.find_element(By.ID, 'ss_single'))</code></pre><p>Select类 提供了如下常用的 属性 和 方法</p>
<ul>
<li>all_selected_options</li>
</ul>
<p>返回所有 <code>当前选中的option元素</code> 对应的 WebElement 对象</p>
<p>比如</p>
<pre><code class="language-py">for ele in select.all_selected_options:
    print(ele.text)</code></pre><ul>
<li>select_by_value</li>
</ul>
<p>根据选项的 <code>value属性值</code> ，选择元素。</p>
<p>比如，下面的HTML，</p>
<pre><code class="language-html">&lt;option value="foo"&gt;Bar&lt;/option&gt;</code></pre><p>就可以根据 foo 这个值选择该选项，</p>
<pre><code class="language-py">select.select_by_value('foo')</code></pre><ul>
<li>select_by_index</li>
</ul>
<p>根据选项的 <code>次序</code> 选择元素</p>
<p>现在Selenium 4 是从 <code>0</code> 开始, 第一个选项 index 为 <code>0</code></p>
<p><code>select_by_index(0)</code> ，选择的是 第 <code>1</code> 个选项，</p>
<p><code>select_by_index(1)</code> ，选择的是 第 <code>2</code> 个选项， 依此类推</p>
<p>以前，老版本的 Seleium，是从 <code>1</code> 开始</p>
<ul>
<li>select_by_visible_text</li>
</ul>
<p>根据选项的 <code>可见文本</code> ，选择元素。</p>
<p>比如，下面的HTML，</p>
<pre><code class="language-html">&lt;option value="foo"&gt;Bar&lt;/option&gt;</code></pre><p>就可以根据 Bar 这个内容，选择该选项</p>
<pre><code class="language-py">select.select_by_visible_text('Bar')</code></pre><ul>
<li>deselect_by_value</li>
</ul>
<p>根据选项的value属性值， <code>去除</code> 选中元素</p>
<ul>
<li>deselect_by_index</li>
</ul>
<p>根据选项的次序，<code>去除</code> 选中元素</p>
<ul>
<li>deselect_by_visible_text</li>
</ul>
<p>根据选项的可见文本，<code>去除</code> 选中元素</p>
<ul>
<li>deselect_all</li>
</ul>
<p><code>去除</code> 选中所有元素</p>
<h3>Select单选框</h3>
<p>对于 select单选框，操作比较简单：</p>
<p>不管原来选的是什么，直接用Select方法选择即可。</p>
<p>例如，选择示例里面的小雷老师，示例代码如下</p>
<pre><code class="language-python"># 导入Select类
from selenium.webdriver.support.select import Select

# 创建Select对象
select = Select(wd.find_element(By.ID, "ss_single"))

# 通过 Select 对象选中小雷老师
select.select_by_visible_text("小雷老师")</code></pre><h3>Select多选框</h3>
<p>对于select多选框，要选中某几个选项，要注意去掉原来已经选中的选项。</p>
<p>例如，我们选择示例多选框中的 小雷老师 和 小凯老师</p>
<p>可以用select类 的deselect_all方法，清除所有 已经选中 的选项。</p>
<p>然后再通过 select_by_visible_text方法 选择 小雷老师 和 小凯老师。</p>
<p>示例代码如下：</p>
<pre><code class="language-python"># 导入Select类
from selenium.webdriver.support.select import Select

# 创建Select对象
select = Select(wd.find_element(By.ID, "ss_multi"))

# 清除所有 已经选中 的选项
select.deselect_all()

# 选择小雷老师 和 小凯老师
select.select_by_visible_text("小雷老师")
select.select_by_visible_text("小凯老师")</code></pre><h1>其他操作</h1>
<p>有别余上面的一些特殊操作方法和技巧</p>
<h2>特殊元素操作</h2>
<p>比如 鼠标右键点击、双击、移动鼠标到某个元素、鼠标拖拽等。</p>
<p>这些操作，可以通过 Selenium 提供的 <code>ActionChains</code> 类来实现。</p>
<p>ActionChains 类 里面提供了 一些特殊的动作的模拟，我们可以通过 ActionChains 类的代码查看到，如下所示</p>
<p>示例代码如下：</p>
<pre><code class="language-python">from selenium import webdriver
from selenium.webdriver.common.by import By
import time

wd = webdriver.Chrome()
wd.implicitly_wait(5)

wd.get('https://www.byhy.net/cdn2/files/selenium/sample4.html')


from selenium.webdriver.common.action_chains import ActionChains
ac = ActionChains(wd)

time.sleep(3)

# 光标悬停 到元素上
ac.move_to_element(
    wd.find_element(By.CSS_SELECTOR, '.navbar-nav &gt; li.dropdown')
).perform()

time.sleep(3)

# 拖放元素
for i in range(1,6):
    ac.drag_and_drop(
        wd.find_element(By.ID, f'course-{i}'),
        wd.find_element(By.ID,'selected-courses')
    ).perform()

    time.sleep(0.5)

input('\n\n按回车退出')</code></pre><h2>直接执行javascript</h2>
<p>我们可以直接让浏览器运行一段javascript代码，并且得到返回值，如下</p>
<pre><code class="language-py"># 直接执行 javascript，里面可以直接用return返回我们需要的数据
nextPageButtonDisabled = driver.execute_script(
    '''
    ele = document.querySelector('.soupager &gt; button:last-of-type');
    return ele.getAttribute('disabled')
    ''')
# 返回的数据转化为Python中的数据对象进行后续处理
if nextPageButtonDisabled == 'disabled': # 是最后一页
    return True
else: # 不是最后一页
    return False</code></pre><p>有时，自动化的网页内容很长，或者很宽，超过一屏显示，</p>
<p>如果我们要点击的元素不在窗口可见区内，新版本的selenium协议， 浏览器发现要操作（比如点击操作）的元素，不在可见区内，往往会操作失败，</p>
<p>出现类似下面的提示</p>
<pre><code class="language-html">element click intercepted: Element <span>这里是元素html</span> 
is not clickable at point (119, 10). 
Other element would receive the click: <div>...</div></code></pre><p>这时，可以调用 <code>execute_script</code> 直接执行js代码，让该元素出现在窗口可见区正中</p>
<pre><code class="language-py">driver.execute_script("arguments[0].scrollIntoView({block:'center',inline:'center'})", job) </code></pre><p>其中 <code>arguments[0]</code> 就指代了后面的第一个参数 <code>job</code> 对应的js对象，</p>
<p>js对象的 <code>scrollIntoView</code> 方法，就是让元素滚动到可见部分</p>
<p><code>block:&#39;center&#39;</code> 指定垂直方向居中</p>
<p><code>inline:&#39;center&#39;</code> 指定水平方向居中</p>
<h2>冻结界面</h2>
<p>一些网站的元素只有在你鼠标悬停的时候才会出现内容，移开内容就消失了，导致无法定位元素，这个时候可以使用下面的方法</p>
<p>在 开发者工具栏 console 里面执行如下js代码</p>
<pre><code class="language-js">setTimeout(function(){debugger}, 5000)</code></pre><p>这句代码什么意思呢？</p>
<p>表示在 5000毫秒后，执行 debugger 命令</p>
<p>执行该命令会 浏览器会进入debug状态。 debug状态有个特性， 界面被冻住， 不管我们怎么点击界面都不会触发事件。</p>
<p>所以，我们可以在输入上面代码并回车 执行后， 立即 鼠标放在界面 右上角 更多产品处。</p>
<p>这时候，就会弹出功能图标。</p>
<p>然后，我们仔细等待 5秒 到了以后， 界面就会因为执行了 debugger 命令而被冻住。</p>
<p>然后，我们就可以点击 开发者工具栏的 查看箭头， 再去 点击 对应的图标 ，查看其属性了。</p>
<h2>弹出对话框</h2>
<p>弹出的对话框有三种类型，分别是 Alert（警告信息）、confirm（确认信息）和prompt（提示输入）</p>
<p>他们不同于页面的弹出对话框，在HTML文档中无法锁定其位置，这个时候需要使用专有的方法来获取信息进行操作。</p>
<h3>Alert</h3>
<p>Alert 弹出框，目的就是显示通知信息，只需用户看完信息后，点击 OK（确定） 就可以了。</p>
<p>那么，自动化的时候，代码怎么模拟用户点击 OK 按钮呢？</p>
<p>selenium提供如下方法进行操作</p>
<pre><code class="language-py">driver.switch_to.alert.accept()</code></pre><blockquote>
<p>👓 注意：如果我们不去点击它，页面的其它元素是不能操作的。</p>
</blockquote>
<p>如果程序要获取弹出对话框中的信息内容， 可以通过 如下代码</p>
<pre><code class="language-py">driver.switch_to.alert.text</code></pre><p>示例代码如下</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('https://www.byhy.net/cdn2/files/selenium/test4.html')


# --- alert ---
driver.find_element(By.ID, 'b1').click()

# 打印 弹出框 提示信息
print(driver.switch_to.alert.text) 

# 点击 OK 按钮
driver.switch_to.alert.accept()</code></pre><h3>Confirm</h3>
<p>Confirm弹出框，主要是让用户确认是否要进行某个操作。</p>
<p>比如：当管理员在网站上选择删除某个账号时，就可能会弹出 Confirm弹出框， 要求确认是否确定要删除。</p>
<p>Confirm弹出框 有两个选择供用户选择，分别是 OK 和 Cancel， 分别代表 确定 和 取消 操作。</p>
<p>那么，自动化的时候，代码怎么模拟用户点击 OK 或者 Cancel 按钮呢？</p>
<p>selenium提供如下方法进行操作</p>
<p>如果我们想点击 OK 按钮， 还是用刚才的 accept方法，如下</p>
<pre><code class="language-py">driver.switch_to.alert.accept()</code></pre><p>如果我们想点击 Cancel 按钮， 可以用 dismiss方法，如下</p>
<pre><code class="language-py">driver.switch_to.alert.dismiss()</code></pre><p>示例代码如下</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)

driver.get('https://www.byhy.net/cdn2/files/selenium/test4.html')

# --- confirm ---
driver.find_element(By.ID, 'b2').click()

# 打印 弹出框 提示信息
print(driver.switch_to.alert.text)

# 点击 OK 按钮 
driver.switch_to.alert.accept()

driver.find_element(By.ID, 'b2').click()

# 点击 取消 按钮
driver.switch_to.alert.dismiss()</code></pre><h3>Prompt</h3>
<p>出现 Prompt 弹出框 是需要用户输入一些信息，提交上去。</p>
<p>比如：当管理员在网站上选择给某个账号延期时，就可能会弹出 Prompt 弹出框， 要求输入延期多长时间。</p>
<p>可以调用如下方法</p>
<pre><code class="language-py">driver.switch_to.alert.send_keys()</code></pre><p>示例代码如下</p>
<pre><code class="language-py">from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('https://www.byhy.net/cdn2/files/selenium/test4.html')


# --- prompt ---
driver.find_element(By.ID, 'b3').click()

# 获取 alert 对象
alert = driver.switch_to.alert

# 打印 弹出框 提示信息
print(alert.text)

# 输入信息，并且点击 OK 按钮 提交
alert.send_keys('web自动化 - selenium')
alert.accept()

# 点击 Cancel 按钮 取消
driver.find_element(By.ID, 'b3').click()
alert = driver.switch_to.alert
alert.dismiss()</code></pre><p>👓 注意:有些弹窗并非浏览器的alert 窗口，而是<strong>html元素</strong>，这种对话框，只需要通过之前介绍的选择器选中并进行相应的操作就可以了。</p>
<h2>小技巧</h2>
<p>一些可能会接触到但是不常用的功能，遇到不会可以上来看复习</p>
<h3>窗口大小</h3>
<p>有时间我们需要获取窗口的属性和相应的信息，并对窗口进行控制</p>
<ul>
<li>获取窗口大小</li>
</ul>
<pre><code class="language-py">driver.get_window_size()</code></pre><ul>
<li>改变窗口大小</li>
</ul>
<pre><code class="language-py">driver.set_window_size(x, y)</code></pre><h3>获取当前窗口标题</h3>
<p>浏览网页的时候，我们的窗口标题是不断变化的，可以使用WebDriver的title属性来获取当前窗口的标题栏字符串。</p>
<pre><code class="language-py">driver.title</code></pre><h3>获取当前窗口URL地址</h3>
<pre><code class="language-py">driver.current_url</code></pre><p>例如，访问网易，并获取当前窗口的标题和URL</p>
<pre><code class="language-py">from selenium import  webdriver

driver = webdriver.Chrome()
driver.implicitly_wait(5)

# 打开网站
driver.get('https://www.163.com')

# 获取网站标题栏文本
print(driver.title) 

# 获取网站地址栏文本
print(driver.current_url) </code></pre><h3>截屏</h3>
<p>有的时候，我们需要把浏览器屏幕内容保存为图片文件。</p>
<p>比如，做自动化测试时，一个测试用例检查点发现错误，我们可以截屏为文件，以便测试结束时进行人工核查。</p>
<p>可以使用 WebDriver 的 get_screenshot_as_file方法来截屏并保存为图片。</p>
<pre><code class="language-py">from selenium import  webdriver

driver = webdriver.Chrome()
driver.implicitly_wait(5)

# 打开网站
driver.get('https://www.baidu.com/')

# 截屏保存为图片文件
driver.get_screenshot_as_file('1.png')</code></pre><h3>手机模式</h3>
<p>我们可以通过 <code>desired_capabilities</code> 参数，指定以手机模式打开chrome浏览器</p>
<p>参考代码，如下</p>
<pre><code class="language-py">from selenium import webdriver

mobile_emulation = { "deviceName": "iPhone 14 Pro Max" }

chrome_options = webdriver.ChromeOptions()

chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)

driver = webdriver.Chrome(options=chrome_options)

driver.get('http://www.baidu.com')

input()
driver.quit()</code></pre><h3>上传文件</h3>
<p>有时候，网站操作需要上传文件。</p>
<p>比如，著名的在线图片压缩网站： <a href="https://tinypng.com/">https://tinypng.com/</a></p>
<p>通常，网站页面上传文件的功能，是通过 <code>type</code> 属性 为 <code>file</code> 的 HTML <code>input</code> 元素实现的。</p>
<p>如下所示：</p>
<pre><code class="language-html">&lt;input type="file" multiple="multiple"&gt;</code></pre><p>使用selenium自动化上传文件，我们只需要定位到该input元素，然后通过 send_keys 方法传入要上传的文件路径即可。</p>
<p>如下所示：</p>
<pre><code class="language-py"># 先定位到上传文件的 input 元素
ele = wd.find_element(By.CSS_SELECTOR, 'input[type=file]')

# 再调用 WebElement 对象的 send_keys 方法
ele.send_keys(r'h:\g02.png')</code></pre><p>如果需要上传多个文件，可以多次调用send_keys，如下</p>
<pre><code class="language-py">ele = wd.find_element(By.CSS_SELECTOR,  'input[type=file]')
ele.send_keys(r'h:\g01.png')
ele.send_keys(r'h:\g02.png')</code></pre><p>但是，有的网页上传，是没有 file 类型 的 input 元素的。</p>
<p>如果是Windows上的自动化，可以采用 Windows 平台专用的方法：</p>
<p>执行</p>
<pre><code class="language-py">pip install pypiwin32</code></pre><p>确保 pywin32 已经安装，然后参考如下示例代码</p>
<pre><code class="language-py"># 找到点击上传的元素，点击
driver.find_element(By.CSS_SELECTOR, '.dropzone').click()

sleep(2) # 等待上传选择文件对话框打开

# 直接发送键盘消息给 当前应用程序，
# 前提是浏览器必须是当前应用
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")

# 输入文件路径，最后的'\n'，表示回车确定，也可能时 '\r' 或者 '\r\n'
shell.Sendkeys(r"h:\a2.png" + '\n')
sleep(1)</code></pre><h1>Xpath选择器</h1>
<p>Xpath选择器和CSS选择器的功能一致，但是css选择器主要是给Htmlcss元素元素进行渲染的，而Xpath还有其他的应用场景，比如爬虫框架，手机app框架，这一部分的内容白夜黑羽大佬写的很详细，我就不多做改动，cv一下。</p>
<h3>绝对路径选择</h3>
<p>从根节点开始的，到某个节点，每层都依次写下来，每层之间用 <code>/</code> 分隔的表达式，就是某元素的 <code>绝对路径</code></p>
<p>上面的xpath表达式 <code>/html/body/div</code> ，就是一个绝对路径的xpath表达式， 类似 css表达式 <code>html&gt;body&gt;div</code></p>
<p>自动化程序要使用Xpath来选择web元素，使用 <code>By.XPATH</code> ，像这样：</p>
<pre><code class="language-py">elements = driver.find_elements(By.XPATH, "/html/body/div/select")</code></pre><h3>相对路径选择</h3>
<p>有的时候，我们需要选择网页中某个元素， <code>不管它在什么位置</code> 。</p>
<p>比如，选择示例页面的所有标签名为 <code>div</code> 的元素，如果使用css表达式，直接写一个 <code>div</code> 就行了。</p>
<p>那xpath怎么实现同样的功能呢？ xpath需要前面加 <code>//</code> , 表示从当前节点往下寻找所有的后代元素,不管它在什么位置。</p>
<p>所以xpath表达式，应该这样写： <code>//div</code></p>
<p>&#39;//&#39; 符号也可以继续加在后面,比如，要选择 所有的 div 元素里面的 所有的 p 元素 ，不管div 在什么位置，也不管p元素在div下面的什么位置，则可以这样写 <code>//div//p</code></p>
<p>对应的自动化程序如下</p>
<pre><code class="language-py">elements = driver.find_elements(By.XPATH, "//div//p")</code></pre><p>如果使用CSS选择器，对应代码如下</p>
<pre><code class="language-py">elements = driver.find_elements(By.CSS_SELECTOR,"div p")</code></pre><p>如果，要选择 所有的 div 元素里面的 直接子节点 p ， xpath，就应该这样写了 <code>//div/p</code></p>
<p>如果使用CSS选择器，则为 <code>div &gt; p</code></p>
<h3>通配符</h3>
<p>如果要选择所有div节点的所有直接子节点，可以使用表达式 <code>//div/*</code></p>
<pre><code class="language-">*` 是一个通配符，对应任意节点名的元素，类似 CSS选择器 `div &gt; *</code></pre><p>代码如下：</p>
<pre><code class="language-py">elements = driver.find_elements(By.XPATH, "//div/*")
for element in elements:
    print(element.get_attribute('outerHTML'))</code></pre><h2>根据属性选择</h2>
<p>Xpath 可以根据属性来选择元素。</p>
<p>根据属性来选择元素 是通过 这种格式来的 <code>[@属性名=&#39;属性值&#39;]</code></p>
<p>注意：</p>
<ul>
<li>属性名注意前面有个@</li>
<li>属性值一定要用引号， 可以是单引号，也可以是双引号</li>
</ul>
<h3>根据id属性选择</h3>
<p>选择 id 为 west 的元素，可以这样 <code>//*[@id=&#39;west&#39;]</code></p>
<h3>根据class属性选择</h3>
<p>选择所有 select 元素中 class为 single_choice 的元素，可以这样 <code>//select[@class=&#39;single_choice&#39;]</code></p>
<p>如果一个元素class 有多个，比如</p>
<pre><code class="language-html"><p>
    北京    
</p></code></pre><p>如果要选 它， 对应的 xpath 就应该是 <code>//p[@class=&quot;capital huge-city&quot;]</code></p>
<p>不能只写一个属性，像这样 <code>//p[@class=&quot;capital&quot;]</code> 则不行</p>
<h3>根据其他属性</h3>
<p>同样的道理，我们也可以利用其它的属性选择</p>
<p>比如选择 具有multiple属性的所有页面元素 ，可以这样 <code>//*[@multiple]</code></p>
<h3>属性值包含字符串</h3>
<p>要选择 style属性值 包含 color 字符串的 页面元素 ，可以这样 <code>//*[contains(@style,&#39;color&#39;)]</code></p>
<p>要选择 style属性值 以 color 字符串 <code>开头</code> 的 页面元素 ，可以这样 <code>//*[starts-with(@style,&#39;color&#39;)]</code></p>
<p>要选择 style属性值 以 某个 字符串 结尾 的 页面元素 ，大家可以推测是 <code>//*[ends-with(@style,&#39;color&#39;)]</code>， 但是，很遗憾，这是xpath 2.0 的语法 ，目前浏览器都不支持</p>
<h2>按次序选择</h2>
<p>前面学过css表达式可以根据元素在父节点中的次序选择， 非常实用。</p>
<p>xpath也可以根据次序选择元素。 语法比css更简洁，直接在方括号中使用数字表示次序</p>
<p>比如</p>
<h3>某类型 第几个 子元素</h3>
<p>比如</p>
<p>要选择 p类型第2个的子元素，就是</p>
<pre><code class="language-py">//p[2]</code></pre><p>注意，选择的是 <code>p类型第2个的子元素</code> ， 不是 <code>第2个子元素，并且是p类型</code> 。</p>
<p>注意体会区别</p>
<p>再比如，要选取父元素为div 中的 p类型 第2个 子元素</p>
<pre><code class="language-py">//div/p[2]</code></pre><h3>第几个子元素</h3>
<p>也可以选择第2个子元素，不管是什么类型，采用通配符</p>
<p>比如 选择父元素为div的第2个子元素，不管是什么类型</p>
<pre><code class="language-py">//div/*[2]</code></pre><h3>某类型 倒数第几个 子元素</h3>
<p>当然也可以选取倒数第几个子元素</p>
<p>比如：</p>
<ul>
<li>选取p类型倒数第1个子元素</li>
</ul>
<pre><code class="language-py">//p[last()]</code></pre><ul>
<li>选取p类型倒数第2个子元素</li>
</ul>
<pre><code class="language-py">//p[last()-1]</code></pre><ul>
<li>选择父元素为div中p类型倒数第3个子元素</li>
</ul>
<pre><code class="language-py">//div/p[last()-2]</code></pre><h3>范围选择</h3>
<p>xpath还可以选择子元素的次序范围。</p>
<p>比如，</p>
<ul>
<li>选取option类型第1到2个子元素</li>
</ul>
<pre><code class="language-py">//option[position()&lt;=2]</code></pre><p>或者</p>
<pre><code class="language-py">//option[position()&lt;3]</code></pre><ul>
<li>选择class属性为multi_choice的前3个子元素</li>
</ul>
<pre><code class="language-py">//*[@class='multi_choice']/*[position()&lt;=3]</code></pre><ul>
<li>选择class属性为multi_choice的后3个子元素</li>
</ul>
<pre><code class="language-py">//*[@class='multi_choice']/*[position()&gt;=last()-2]</code></pre><p>为什么不是 <code>last()-3</code> 呢？ 因为</p>
<p><code>last()</code> 本身代表最后一个元素</p>
<p><code>last()-1</code> 本身代表倒数第2个元素</p>
<p><code>last()-2</code> 本身代表倒数第3个元素</p>
<h2>组选择、父节点、兄弟节点</h2>
<h3>组选择</h3>
<p>css有组选择，可以同时使用多个表达式，多个表达式选择的结果都是要选择的元素</p>
<p>css 组选择，表达式之间用 <strong>逗号</strong> 隔开</p>
<p>xpath也有组选择， 是用 <strong>竖线</strong> 隔开多个表达式</p>
<p>比如，要选所有的option元素 和所有的 h4 元素，可以使用</p>
<pre><code class="language-bash">//option | //h4</code></pre><p>等同于CSS选择器</p>
<pre><code class="language-css">option , h4</code></pre><p>再比如，要选所有的 class 为 single_choice 和 class 为 multi_choice 的元素，可以使用</p>
<pre><code class="language-perl"> //*[@class='single_choice'] | //*[@class='multi_choice']</code></pre><p>等同于CSS选择器</p>
<pre><code class="language-undefined">.single_choice , .multi_choice</code></pre><h3>选择父节点</h3>
<p>xpath可以选择父节点， 这是css做不到的。</p>
<blockquote>
<p>小贴士：实际上现在Css也可以选择父节点使用has()，就可以选择父节点，括号里面写，要找那个节点的父节点，使用起来没有Xpath方便，如果不限定反问，他会选择子节点的父节点的父节点....</p>
</blockquote>
<p>某个元素的父节点用 <code>/..</code> 表示</p>
<p>比如，要选择 id 为 china 的节点的父节点，可以这样写 <code>//*[@id=&#39;china&#39;]/..</code> 。</p>
<p>当某个元素没有特征可以直接选择，但是它有子节点有特征， 就可以采用这种方法，先选择子节点，再指定父节点。</p>
<p>还可以继续找上层父节点，比如 <code>//*[@id=&#39;china&#39;]/../../..</code></p>
<h3>兄弟节点选择</h3>
<p>前面学过 css选择器，要选择某个节点的后续兄弟节点，用 <strong>波浪线</strong></p>
<p>xpath也可以选择 后续 兄弟节点，用这样的语法 <code>following-sibling::</code></p>
<p>比如，要选择 class 为 single_choice 的元素的所有后续兄弟节点 <code>//*[@class=&#39;single_choice&#39;]/following-sibling::*</code></p>
<p>等同于CSS选择器 <code>.single_choice ~ *</code></p>
<p>如果，要选择后续节点中的div节点， 就应该这样写 <code>//*[@class=&#39;single_choice&#39;]/following-sibling::div</code></p>
<p>xpath还可以选择 <code>前面的</code> 兄弟节点，用这样的语法 <code>preceding-sibling::</code></p>
<p>比如，要选择 class 为 single_choice 的元素的 <code>所有</code> 前面的兄弟节点，这样写</p>
<pre><code class="language-bash">//*[@class='single_choice']/preceding-sibling::*</code></pre><p>要选择 class 为 single_choice 的元素的</p>
<p>前面最靠近的兄弟节点 , 这样写</p>
<pre><code class="language-bash">//*[@class='single_choice']/preceding-sibling::*[1]</code></pre><p>前面第2靠近的兄弟节点 , 这样写</p>
<pre><code class="language-bash">//*[@class='single_choice']/preceding-sibling::*[2]</code></pre><p>而 CSS选择器 目前还没有方法选择 <code>前面的</code> 兄弟节点</p>
<p>要了解更多Xpath选择语法，可以<a href="http://www.w3school.com.cn/xpath/index.asp">点击这里，打开Xpath选择器参考手册</a></p>
<h2>selenium 注意点</h2>
<p>我们来看一个例子</p>
<p>我们的代码：</p>
<ul>
<li>先选择示例网页中，id是china的元素</li>
<li>然后通过这个元素的WebElement对象，使用find_elements_by_xpath，选择里面的p元素，</li>
</ul>
<pre><code class="language-py"># 先寻找id是china的元素
china = wd.find_element(By.ID, 'china')

# 再选择该元素内部的p元素
elements = china.find_elements(By.XPATH, '//p')

# 打印结果
for element in elements:
    print('----------------')
    print(element.get_attribute('outerHTML'))</code></pre><p>运行发现，打印的 不仅仅是 china内部的p元素， 而是所有的p元素。</p>
<p>要在某个元素内部使用xpath选择元素， 需要 <code>在xpath表达式最前面加个点</code> 。</p>
<p>像这样</p>
<pre><code class="language-py">elements = china.find_elements(By.XPATH, './/p')</code></pre><p>完活。</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/posts/codeRecoder/Selenium#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69a16200c464ea83fd820fae</guid>
  <category>posts</category>
<category>技术upup!</category>
 </item>
  <item>
    <title>折腾随身WiFi的一些经历</title>
    <link>https://geekrain.site/posts/diyRecord/wifi</link>
    <pubDate>Mon, 23 Feb 2026 08:40:14 GMT</pubDate>
    <description>非教程贴，记录一下我当时为了省钱，想用少量的钱来支撑满足我在校多设备上网需求，达到能流畅观看流媒体视</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/posts/diyRecord/wifi'>https://geekrain.site/posts/diyRecord/wifi</a></blockquote>
      <blockquote>
<p>非教程贴，记录一下我当时为了省钱，想用少量的钱来支撑满足我在校多设备上网需求，达到能流畅观看流媒体视频的状态。</p>
</blockquote>
<h2>起因</h2>
<p>源自于学校超级烂的校园网，最低价位一年160左右，却只有20兆的宽带的网速，最恶心的是只支持1台设备，并且切换设备需要重新登录，我使用过程中需要频繁的在电脑和手机之间切换，每此都要重新输密码。全覆盖也是假的，在食堂这种我刷新率最高的地方，竟然不支持使用，所以在大一傻不啦叽的用了一个学期之后，我开始思考其他解决方案。</p>
<h2>经过</h2>
<p>在网上搜罗了一番，最终尝试使用随身WiFi这个方案，如果你也有使用随身WiFi的想法，我建议你确定你居住地周围的基站，三大运营商那个信号最好，虽然现在的esim卡好像都是多网通可切换的，但是为了后面的进阶操作，还是测试确定一下比较好。  </p>
<p>当时已经决定使用随身WiFi了，但是经过我观察，一些大厂的随身WiFi流量包的价格也是非常让本站长肉疼的，为了继续压缩成本，我继续检索，最终来到了我梦开始的地方，酷安的随身WiFi板块。由此便打开潘多拉魔盒，看到这里的小伙伴，如果自己的动手能力很强，信息检索能力也不错的话就可上这个板块，吸收经验准备进化了。</p>
<p>在我刚入门的阶段，市面上的随身WiFi都是非常之便宜的，如果有什么活动，就能在板块看到，跟着买能买到很多几块钱的棒子，社区的大佬也非常多，当时比较流行的是影腾品牌的WiFi棒子，其实就是不带电池，形状类似于一个小U盘形似棒子所以就这么称呼了，有电池的可以带出去的使用的则称为电池机。</p>
<p>在权衡了一下之后我选择了在当时版块里比较火的纽曼电池机进行我的正式尝试，因为当时的纽曼电池机没有云控，还自带卡槽(不带卡槽想移植就要自己焊了)。云控就是一些厂商为了防止你乱折腾不使用官方的套餐，会定时检测，发现你乱搞，就给你限速断网之类的恶心操作。而说到卡槽，就要引入随身WiFi中最好玩的一点，就是</p>
<h3>移植</h3>
<p>移植的作用简单来说，就是为了吧更便宜套餐商家的随身WiFi中的esim卡移植到设备性能更好但是，自身套餐昂贵的设备上，最终形成完美的毕业级随身WiFi。</p>
<hr>
<p>然而当时本站长丝毫不理解，退而求其次，选择更稳重的方案，使用专门的大流量套餐卡，插到我的随身WiFi上来使用，这时候也许就有小伙伴要问了：</p>
<p><strong>站长，站长，既然你都要办卡了为什么不买一个5G二手机，直接插在二手手机当热点机不就能解决你的问题了吗？</strong></p>
<p>我的回答是还真是，如果你为了图方便，就这样一下子就解决了，但是，我更喜欢折腾的快乐，虽然我有一个旧的手机是红米k30pro，但是那部手机作为本人的第一部“旗舰”机，6年的服役期实在让我不忍让他继续燃烧，就只是为了给我开个热点，况且他现在已经有了新的作用，最近按照b站上的教程我把他改造了一下装上很多模拟器，用来再无聊的时候玩玩老游戏。</p>
<p>好了继续说回正题，办理一张了19块钱280G的流量卡，当然这种流量卡一般都是很坑的，半年或者一年之后套餐价格会发生变化，为了更持久绿色的发展，我自己办卡自己吃佣金配合首充送话费，以及拉着几个舍友办理流量卡，自己吃了300多的佣金，四舍五入大一下半年，也没在网费上花钱，但是还是太麻烦了。隔一段时间就要去注销重新办卡，不适合我。</p>
<p>这个时候我才真正开始尝试随身WiFi的高阶玩法，纽曼随身WiFi刚到手的时候，adb连接开启之后，使用板块内大佬开发的去控工具简单去除一下控制，登录后台，输入切卡密码就可以使用外置卡了。也幸好赶上当时最火的厂商套餐，影腾的3.9一个月1500G的黄金时代，当时还不会移植，就去万能的闲鱼购买了一张电信影腾的移植卡当时是20块钱，一直用到现在，没错，真的是一个月3.9，但是流量肯定是虚标了的，实际上超过300个g,就会限速无法使用，不过对于我来说绰绰有余了。</p>
<p>后面一年半的时间，从来就没有担心过网络的问题，每周六中午12点启动b站看4k的凡人修仙传都不卡，这样舒坦的日子，一直持续到了去年11月份，影腾开始大规模的取消之前的老套餐，清算用户（再次之前也清理过很多移植用户，不过我运气好没有被清算）。我也被限速了，在上工信部投诉重新给我放开速度之后，我意识到迟早一天用不了，开始查找一些替代方案，并且也意识到了现在4G随身WiFi的网速有时候满足不了我的要求了，迟早是打工人的命，未来在小小的出租屋里我也想能安稳使用我的随身wifi,于是一部到位购入了中兴的f50随身WiFi。</p>
<p>在我购入时候，市面上已经有了成熟的折腾技术，包括加装散热，外接小路由器实现更强大的网速和信号。还有酷友开源了，非常好使的可视化管理后台。随身WiFi本质上就是一个阉割的只剩下热点功能的手机，在一些棒子里装的骁龙410处理器，刷好Linux之后可以跑一些简单的小工具，有时间了打算买一个玩玩看看能不能吧我的博客部署上去。自此，我的上网需求应该是解决了，你说为什么不拉宽带，因为我现在还没出去上过班呢，也没具体了解宽带的资费，但感觉如果租的房子没有带宽带，也还没确定好自己的工作，随身WiFi的方案很便捷的可以快捷的解决我的上网需求，带走也方便。我现在的设备就是老的纽曼电池机，配上影腾的低费卡，先用着。中兴f50作为候补，用于未来的高速网络需求，5G卡方法我有一张之前入手的飞猫的物联卡，资费稍贵，29，1500但是以后如果上班了，应该是可以支撑的，这张卡是伪5G,调整一下信道，所在地区支持的情况下可以跑到5G的网速。</p>
<h2>最后</h2>
<p>如果你也有和我相似的需求，自己喜欢探索的话，现在就可下个酷安自己研究了，不想折腾的话，我建议你一步到位，咸鱼上买插卡版的f50插上流量卡，就能美美使用了，其实还有很多玩法，一些机子甚至可以破解免费使用网络，不过这些我都没有去细致的研究，心有余，而力不足了感觉，呜呜呜。另外，现在的酷安随身WiFi板块，经过了整治，很多好的文章被删除，所以现在很多大佬不在上面分享自己的工具了，转战群聊，在qq群里分享技术。但是板块里仍存在这不少精品教程，自行检索，如果你想自己操作但是连qq群都找不到的话，还是不要搞了，直接一步到位就好。</p>
<p>另外在了解了这些之后，我发现人类啊，非常的神奇，总有一些人能在一个领域发光发热，成为被称为大佬的存在，所以我感觉当你对一个比较小众或者从未涉及过得领域感兴趣的时候，多上网搜搜，找到相关社群你就成功一半了，之后就一口一个大佬的问，一个个问题就迎刃而解了。</p>
<p>感兴趣的话可以在留言板提问，在力所能及的范围内我会尽力解答的，因为我也是小白捏。</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/posts/diyRecord/wifi#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">699c126ec464ea83fd81bb7f</guid>
  <category>posts</category>
<category>DIY日常</category>
 </item>
  <item>
    <title>绪</title>
    <link>https://geekrain.site/notes/4</link>
    <pubDate>Sun, 15 Feb 2026 13:47:36 GMT</pubDate>
    <description>除夕的前夜，也代表着我的寒假即将结束了，过的真是快呀，又多了很多的思考，最近一直在准备年后面试，希望</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/notes/4'>https://geekrain.site/notes/4</a></blockquote>
      <p>除夕的前夜，也代表着我的寒假即将结束了，过的真是快呀，又多了很多的思考，最近一直在准备年后面试，希望我可以找到一个还不错的实习吧。</p>
<p>这两天我侄子也回来了，刚刚上一年级，真好啊，我感觉我的记忆真正的起点就是从一年级开始的，看着他无忧无虑，也明白了为什么小时候和小伙伴玩总有几个大人会远远的盯着看很久了。时间过得可真是快啊，我妈竟也开始给我催婚了，真是奇妙的感觉，她说让我在大四毕业之前找个订婚的对象。哈哈，我也只能笑笑回应了，对我来说就像天方夜谭，不过我也不着急，只的是把母亲的话就当一个玩笑，慢慢来吧。</p>
<p>在我侄子回来之后，我对家中两个处于人类生命进程极端的人进行了一番观察，我的小侄子，和我的外婆。一个是新生的绿芽，一个则宛如风中残烛。一个对一切都充满好奇，未来也不太会有外界的施压被迫劳动和吃不饱饭的情况(人类之间不乱搞的情况下)。一个经历过过文革土改，那些最艰难的岁月,饱经风霜，他们也应该是对生活改善感知最大的一代人了吧。我的外婆，对新事物的接受能力还算可以，我教会了她用手机拍照，还有刷抖音，不过也只是简单的上下刷。我的小侄子也会用手机刷抖音，也是如此。还挺神奇，突然灵感枯竭不知道怎么继续往下写了，不知道我要是活到我外婆那个岁数不知道会怎么去想，稍稍的期待一下吧。</p>
<p>害，长大咋感觉自己反而变弱鸡了，想象力也不行了，在之前，别人讲故事，脑子里就自动脑补，跟放电影一样。现在就很难做到了，不过感知力倒是提升了，以前看不懂的动画里的情节，现在通通都能看懂，也有了更深的思考，引起更多的共鸣，这究竟算好还是坏呢。本想酣畅淋漓的大写特写一笔，但手指放上去的一刹那，脑子一下就空白了。也罢就这样草草结束也算是一种风格嘛，我妈希望我毕业能找个对象，而我对我的期望就是毕业圆一个大厂梦。</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/notes/4#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">6991ce78c464ea83fd817f11</guid>
  <category>notes</category>
false
 </item>
  <item>
    <title>性能测试涉及到的一些指标解释</title>
    <link>https://geekrain.site/posts/codeRecoder/perftest01</link>
    <pubDate>Sat, 14 Feb 2026 07:31:02 GMT</pubDate>
    <description>性能测试前提
确认指标，在做性能测试之前，应该有产品的性能指标，其次性能测试不能说是越早测越好，因为</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/posts/codeRecoder/perftest01'>https://geekrain.site/posts/codeRecoder/perftest01</a></blockquote>
      <h1>性能测试前提</h1>
<p>确认指标，在做性能测试之前，应该有产品的性能指标，其次性能测试不能说是越早测越好，因为性能测试的需求指标，会随着产品的开发产生变动，合适的测试时间应该是在彩票功能测试完成几轮，确定产品相对稳定之后，在启动性能测试。当然测试人员的预先做一些准备也是十分有必要的，比如对测试工具的熟悉，基础相关知识的学习。</p>
<h1>TPS</h1>
<p>TPS(transaction per second)是<strong>服务端</strong>每秒处理的请求的数量</p>
<p>TPS最直观的反映了系统的处理能力，是重要的性能指标之一。  </p>
<p>大致的计算公式如下：</p>
<p><code>TPS = 并发用户数\div平均响应时间</code> </p>
<p>与TPS相关的名词：</p>
<ul>
<li><p>RPS(request per second)是测试工具每秒发送的请求的数量</p>
<p>RPS 和 TPS 概念不同，前者是每秒发出的请求数量。后者是处理完成的请求数量。</p>
<p>但是显然，RPS 是决定 TPS 的重要因素。</p>
<p>TPS 是由 RPS 、网络延迟 、服务端本身的处理速度 这3个因素决定的。</p>
<p>一个性能表现良好的系统，TPS和RPS几乎是相同的  </p>
</li>
<li><p>EPS (error per second) 是 服务端 每秒处理出错的数量，也包含在TPS中。</p>
<p>一个性能表现良好的系统，EPS 应该一直为0</p>
</li>
<li><p>TOPS (timeout per second) 是 服务端 每秒处理超时的数量</p>
<p>超时时间具体是多少，应该由产品需求定义。</p>
<p>一个性能表现良好的系统，TOPS 应该一直为0</p>
</li>
</ul>
<p>服务端本身的处理速度 就是我们要测试的，测试时，我们要保证的是其他两个因素：RPS 和 网络延迟。</p>
<p>做 性能/压力测试 时， 被测系统 和 加压系统， 应该 在一个 带宽网速 比较理想的环境中，首先保证网络延迟没有问题。</p>
<p>然后，性能测试工具 要 测试 TPS 能否达到 ， 主要就是设置每秒发送请求的数量，也就是RPS。</p>
<p>RPS 是由测试工具决定的。</p>
<p>一个压测工具本身的加压性能也很重要。</p>
<p>否则，如果TPS指标比较高，工具本身做不到，就没法测试了。</p>
<p>Jmeter受制于机器性能，单台几期可能支持最大的RPS在几千到几百之间，所以在涉及到大型的高并发测试往往需要使用分布式来实现性能实施，需要一台控制机，指挥压力机来实现。</p>
<h1>响应时长</h1>
<p>响应时长就是服务器处理的请求耗费的时间</p>
<h1>平均响应时长</h1>
<p>平均响应时长 就是 服务端 处理请求的平均耗费时间。</p>
<p>这是影响用户体验的重要指标。设想一下如果 TPS 很高，但是，很多请求要很长时间才得到反应，用户体验就会很差。</p>
<h1>相应时长区段统计</h1>
<p>光看平均响应时长，往往是不全面的。</p>
<p>可能 有些请求会耗时特别长，严重影响用户体验。但是被平均了就看不出来。</p>
<p>响应时长不能两极分化。</p>
<p>这就像 国民收入，不能只看平均值，也要关注挣扎在贫困线上的人有多少。</p>
<p>响应时长区段统计 就是查看是否 两极分化的 衡量指标。</p>
<h1>并发连接和并发用户</h1>
<p>并发连接数 是 服务端 和客户端 建立的 <code>TCP连接的数量</code></p>
<p>并发用户数 是 服务端 同时服务的 <code>用户的数量</code> 。</p>
<p>用户的一个操作可能引发多个并发连接。</p>
<h2>并发链接</h2>
<p>通常，并发连接数指标，适用于 测试 面向客户端程序的 API服务系统，比如 云服务。</p>
<p>和 TPS 对系统性能的衡量侧重点不同 ，并发连接数指标 衡量 系统 能 <code>同时处理</code> 客户的能力。</p>
<p>两者的区别 用一个比方 来解释，就像银行服务：</p>
<p>并发连接数，就像有多少个服务窗口</p>
<p>TPS， 就像每个窗口 服务员的处理速度</p>
<p>每个窗口服务员的处理速度即使很快，但是同时来了很多人，也必须开多个窗口，否则就会有人得不到服务。 </p>
<p>测试工具 创建这么多的并发连接， 目的就是为了测试 服务端 是否能支持 指定的并发连接数量。</p>
<p>服务端支持并发连接的数量，是由很多因素决定的：集群系统设置、服务端运行硬件配置、服务端系统软件配置、应用程序设置。</p>
<p>做性能测试时，被测服务系统 一定要按照 性能测试的要求进行部署（模拟真实的运行环境），否则是没有测试意义的。</p>
<h2>并发用户</h2>
<p>通常，并发用户数指标，适用于 测试 面向真实用户的 系统，比如 淘宝。</p>
<p>一个用户的一个操作可能引发多个并发连接</p>
<p>单独说 并发用户数 这个指标没有意义， 必须指定是 <code>哪种性能测试场景</code> 下的并发用户数。</p>
<p>因为用户的操作行为不一样，对服务端的 请求数量 和 并发连接数也不一样。</p>
<p>而且并发用户指标 是 <code>一段时间</code> 内 的，说某个时间点的 并发用户数 也没有意义，因为该点上，很多用户可能没有任何操作。</p>
<p>比如，一个 商城系统，我们要测试 晚高峰典型压力下，系统的性能表现。</p>
<p>可以像这样写测试用例</p>
<ul>
<li><p>系统数据环境</p>
<p>系统中，多少注册用户，多少商品数量等等</p>
</li>
<li><p>单个用户的操作行为</p>
<ul>
<li>先登录，1分钟后</li>
<li>随机浏览25种商品，每次浏览间隔1分钟，</li>
<li>把5个商品加入购物车，间隔1分钟</li>
<li>购买2种商品，间隔1分钟</li>
</ul>
</li>
<li><p>晚高峰的压力模拟（ 7:00-10:00 ）</p>
<ul>
<li>每秒10个用户登录消费，持续30分钟后</li>
<li>每秒20个用户登录消费，持续30分钟后</li>
<li>每秒30个用户登录消费，持续60分钟后</li>
<li>每秒20个用户登录消费，持续30分钟后</li>
<li>每秒10个用户登录消费，持续30分钟</li>
</ul>
</li>
</ul>
<p>上述测试场景下，并发用户数量是变动的，大概是</p>
<ul>
<li>每秒10个用户登录消费，持续30分钟后 （阶段1）</li>
</ul>
<p>并发用户每秒10个递增，30分钟后达到 18000 左右。</p>
<p>注意随后这些用户以每秒10个 不断减少（因为该用户结束了）</p>
<ul>
<li>每秒20个用户登录消费，持续30分钟后 （阶段2）</li>
</ul>
<p>并发用户每秒20个递增，但是算上阶段1用户每秒10个递减，总用户数仍然是每秒10个递增。</p>
<p>30分钟后达到 36000 左右。 这时，系统中的用户全是 阶段2 产生的用户，随后这些用户以每秒20个 不断减少</p>
<ul>
<li>每秒30个用户登录消费，持续60分钟后 （阶段3）</li>
</ul>
<p>前30分钟 算上阶段2用户每秒20个递减，总用户数仍然是每秒10个递增。</p>
<p>30分钟后达到 54000 左右 。 这时，系统中的用户全是 阶段3前30秒 产生的用户，随后这些用户以每秒30个 不断减少。</p>
<p>后30分钟 增加和用户和 阶段3前30分钟 的用户下线速度 数量相同，所以维持 54000 左右不变</p>
<p>到了阶段2的末尾，系统中的用户数 为54000 左右，而且全是 阶段3后30秒 产生的用户，随后这些用户以每秒30个 不断减少。</p>
<ul>
<li>每秒20个用户登录消费，持续30分钟后 （阶段4）</li>
</ul>
<p>和 阶段3 后30分钟 的用户下线速度 相抵，以每秒10个 速度递减。</p>
<p>30分钟后减少到 36000 左右。 这时，系统中的用户全是 阶段4 产生的用户，随后这些用户以每秒20个 不断减少</p>
<ul>
<li>每秒10个用户登录消费，持续30分钟 （阶段5）</li>
</ul>
<p>和 阶段4 的用户下线速度 相抵，以每秒10个 速度递减。</p>
<p>30分钟后减少到 18000 左右。</p>
<p>这时，系统中的用户全是 阶段5 产生的用户，随后这些用户以每秒10个 不断减少，再过30分钟，也就是到了10:30，并发用户数量减少到0</p>
<p>可以看出上述过程中，并发用户数量是不断变化的，巅峰数量为 54000 左右 维持半小时</p>
<h1>CPU/内存/磁盘/网络 负载</h1>
<p>我们做性能测试时，不能只看 TPS、响应时长 等指标是否达到，也要看被测系统在达到这些指标时，机器本身的负载情况。</p>
<p>所谓负载情况，主要是： CPU占用率， 内存使用，磁盘IO、磁盘使用率。</p>
<p>在性能测试分析时，我们主要关注这两点</p>
<ul>
<li>是否接近满负荷</li>
</ul>
<p>如果在达到这些指标时，机器已经处于满负荷状态：CPU使用率 接近 100%， 内存几乎用光，那也是不行的。 因为随时系统可能出问题。</p>
<p>就是说再加点压力，或者再持续一段时间，就很可能出现响应超时甚至响应错误的情况。</p>
<ul>
<li>是否资源使用持续上升</li>
</ul>
<p>这点特别体现在 内存使用率 上。</p>
<p>如果系统资源使用图上，内存使用率是一个斜线不断上升，的情况，那么很可能被测系统存在内存泄露。</p>
<p>这样只要再持续一段时间，就很可能出现系统因内存耗尽而奔溃的现象。</p>
<p>出现这样的图表，就应该添加测试用例，做一个较长时间的性能测试（longevity testing），观查系统的行为。</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/posts/codeRecoder/perftest01#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">699024b6c464ea83fd817738</guid>
  <category>posts</category>
<category>技术upup!</category>
 </item>
  <item>
    <title>躁</title>
    <link>https://geekrain.site/notes/3</link>
    <pubDate>Sun, 08 Feb 2026 14:04:51 GMT</pubDate>
    <description>转眼间寒假已经去了一半，当初的那股子心气也消了一半，我是一个爱在假期之前给自己规划的人，也是一个喜欢</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/notes/3'>https://geekrain.site/notes/3</a></blockquote>
      <p>转眼间寒假已经去了一半，当初的那股子心气也消了一半，我是一个爱在假期之前给自己规划的人，也是一个喜欢接受自己摆烂带来的负罪感，也不做出改变的人，所以说人真是复杂，甚至有时候连自己的看不太懂自己。</p>
<p>这段日子我做了些什么呢，刚放假那阵心血来潮很喜欢ps1风格的动画，自己在油管上找了个视频学了一下建模，用自己的照片捏了一个自己，哈哈，绑定了骨架，搞了几个动作，看着自己动起来了还是蛮好玩的，之后就停滞了。也确定了未来一定会尝试去做的爱好，就是ps1风格动画，还有像素画，不知道为什么我就是很喜欢那种感觉，然后就是搭建了这个博客，再之后就是又重新的认识了一遍自己。  </p>
<p>现在是晚上，耳边隐隐传来老人逝去村里面办葬礼放的哀乐，哀乐衬哀情，思绪飘飘。  </p>
<p>最近左眼皮老跳，但其实左右眼跳我都有破解之法，左眼跳我会想：左眼跳跳，不是升官就是发财了。这句歌词，但如果是右眼跳我就会在心里想：左眼跳灾，右眼跳财。  </p>
<p>突然想打鲁迅中笔中的阿Q了，这招不是他的心理胜利法吗。啊，不能再这样懒惰麻木下去了，接下来的时间好好把握。  </p>
<p>（今天写的啥玩意）</p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/notes/3#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69889803c464ea83fd813f3d</guid>
  <category>notes</category>
false
 </item>
  <item>
    <title>Python学习日记(二)</title>
    <link>https://geekrain.site/posts/codeRecoder/python02</link>
    <pubDate>Tue, 03 Feb 2026 14:30:14 GMT</pubDate>
    <description>进度有点慢，还是不能够像在学校里一样把自己管的很好，不多说了
#今日总结
##一.常用数据结构
上一</description>
    <content:encoded><![CDATA[
      <blockquote>该渲染由 marked 生成，可能存在排版问题，最佳体验请前往：<a href='https://geekrain.site/posts/codeRecoder/python02'>https://geekrain.site/posts/codeRecoder/python02</a></blockquote>
      <p>进度有点慢，还是不能够像在学校里一样把自己管的很好，不多说了
#今日总结
##一.常用数据结构
上一篇元组之后，还有一些数据结构没有提及，记到这里，字符串就不专门记录了，因为其中多涉及的都是对字符串的操作方法，再遇到对应情况自行查阅<a href="https://github.com/jackfrued/Python-100-Days/blob/master/Day01-20/11.%E5%B8%B8%E7%94%A8%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E5%AD%97%E7%AC%A6%E4%B8%B2.md">Python字符串讲解</a>。
##1.元组
列表和元组都是容器型的数据类型，即一个变量可以保存多个数据，而且它们都是按一定顺序组织元素的有序容器。列表是可变数据类型，元组是不可变数据类型，<strong>创建之后其中的元素不能在添加或删除</strong>，所以列表可以添加元素、删除元素、清空元素、排序反转，但这些操作对元组来说是不成立的。列表和元组都可以支持拼接运算、成员运算、索引运算、切片运算等操作:</p>
<pre><code class="language-python"># 定义一个三元组
t1 = (35, 12, 98)
# 定义一个四元组
t2 = ('鱼', 45, True, '陕西安康')

# 查看变量的类型
print(type(t1))  # &lt;class 'tuple'&gt;
print(type(t2))  # &lt;class 'tuple'&gt;

# 查看元组中元素的数量
print(len(t1))  # 3
print(len(t2))  # 4

# 索引运算
print(t1[0])    # 35
print(t1[2])    # 98
print(t2[-1])   # 陕西安康

# 切片运算
print(t2[:2])   # ('鱼', 43)
print(t2[::3])  # ('鱼', '陕西安康')

# 循环遍历元组中的元素
for elem in t1:
    print(elem)

# 成员运算
print(12 in t1)         # True
print(99 in t1)         # False
print('Hao' not in t2)  # True

# 拼接运算
t3 = t1 + t2
print(t3)  # (35, 12, 98, '鱼', 43, True, '陕西安康')

# 比较运算
print(t1 == t3)            # False
print(t1 &gt;= t3)            # False
print(t1 &lt;= (35, 11, 99))  # False</code></pre><p>注意点：如果元组中只有一个元素，需要加上一个逗号，不然就会被认为是改变优先级的圆括号举个例子，(&#39;hello&#39;, )和(100, )才是一元组，而(&#39;hello&#39;)和(100)只是字符串和整数。几个元素就是几元组。
###打包解包
多个元素赋值给一个变量的时候，会自动打包为一个元组。把一个元组赋值给多个变量的时候，元组会自动解包然后赋值给对应的变量，解包元素个数和变量不能对应的时候会报错，太少或者太多都会报错，示例如下：</p>
<pre><code class="language-python"># 打包操作
a = 1, 10, 100
print(type(a))  # &lt;class 'tuple'&gt;
print(a)        # (1, 10, 100)
# 解包操作
i, j, k = a
print(i, j, k)  # 1 10 100
#解包元素不足或过多会报错
a = 1, 10, 100, 1000
# i, j, k = a             # ValueError: too many values to unpack (expected 3)
# i, j, k, l, m, n = a    # ValueError: not enough values to unpack (expected 6, got 4)</code></pre><p>###列表生成式补充
上一篇这一部分没太用心直接用的原文档中的内容，这里在细致的记录一下，因为确实方便：<br>普通for循环：</p>
<pre><code class="language-python">#普通情况
result = []
for x in range(5):
    result.append(x * x)  # 核心逻辑在这里
#带筛选功能
result = []
for x in range(10):
    if x % 2 == 0:        # 筛选条件
        result.append(x * x)
#带if...else</code></pre><p>列表生成式写法：</p>
<pre><code class="language-python">#普通情况
#      [ 要在这个列表里放什么     循环逻辑 ]
result = [      x * x           for x in range(5) ]
#带筛选功能
#        [ 结果      循环逻辑            筛选条件 ]
result = [ x * x    for x in range(10)  if x % 2 == 0 ]</code></pre><p>口诀：</p>
<pre><code class="language-">[ 我想要的结果 for 变量 in 可迭代对象 ]</code></pre><p>需要用到if...else的情况就把判断语句（三元运算符）放在for之前就ok了，理解了之后还是挺简单的，演示如下：  </p>
<pre><code class="language-python"># 任务：把数字转成单词
#普通写法
result = []
for x in range(3):
    if x % 2 == 0:
        result.append("Even")
    else:
        result.append("Odd")

# 生成式写法
#        [   三元表达式(结果)        循环逻辑 ]
result = [ "Even" if x % 2 == 0 else "Odd"  for x in range(3) ]</code></pre><p>##2.集合
集合具有三个特点：  </p>
<ul>
<li>无序性：一个集合中，每个元素的地位都是相同的，元素之间是无序的。</li>
<li>互异性：一个集合中，任何两个元素都是不相同的，即元素在集合中只能出现一次。</li>
<li>确定性：给定一个集合和一个任意元素，该元素要么属这个集合，要么不属于这个集合，二者必居其一，不允许有模棱两可的情况出现。</li>
</ul>
<pre><code class="language-">set1 = {1, 2, 3, 3, 3, 2}
print(set1)

set2 = {'banana', 'pitaya', 'apple', 'apple', 'banana', 'grape'}
print(set2)

set3 = set('hello')
print(set3)

set4 = set([1, 2, 2, 3, 3, 3, 2, 1])
print(set4)

set5 = {num for num in range(1, 20) if num % 3 == 0 or num % 7 == 0}
print(set5)</code></pre><p>Python 中的集合类型是一种无序容器，不允许有重复运算，由于底层使用了哈希存储，集合中的元素必须是hashable类型。集合与列表最大的区别在于集合中的元素没有顺序、所以不能够通过索引运算访问元素、但是集合可以执行交集、并集、差集等二元运算，也可以通过关系运算符检查两个集合是否存在超集、子集等关系。</p>
<p>在这里引出一个小知识，在我学习文档的时候，提示说集合底层使用了哈希存储，作者提醒不是相关专业的可以放放，但是计算机相关专业不懂这个概念难以原谅，看的我是羞愧难当，因为我在之前也是些微的了解，忘的也差不多了，唉，然后我手头上刚好在图书馆借了Hello算法的书，所以就复习了一下，在这里做个总结。<br>###哈希表
哈希表又称散列表，通过建立key和value之间的映射实现高效的元素查询，在遇到元素比较多的情况下，可以在O(1)的时间内获取对应的value,本质上是一种空间换时间的数据结构。
####实现原理（简化）
输入一个<code>key</code>，通过哈希哈数得到该key对应的兼职对在数组中的存储位置，可分为如下两步：  </p>
<ol>
<li>通过哈希算法将key转化为一个哈希值</li>
<li>通过这个哈希值对桶(假设一个简单的哈希表由数组构成，数组中的空位可以称为桶，一个桶可以存放一个键值对.)的数量(数组长度）取模，从而得到<code>key</code>对应的索引<code>index</code></li>
</ol>
<p><code>index = hash(key) % capacity(容量)</code><br>####哈希冲突与扩容
哈希表中映射关系是把key构成的输入空间映射到数组所有索引构成的输出空间，而输入空间往往大于输出空间，所以会纯在多个输入对应相同输出结果的情况，就是两个要放在一个哈希表中的数据，最后计算的<code>index</code>结果相同，减少哈希冲突的方法就是通过扩容哈希表。
####哈希表结构改良方法
开放寻址和链式地址，后者是java使用的优化方法，前者是python的优化方法，简单的说一下。<br>链式地址就是数组加链表的形式进行数据存储，当遇到哈希冲突的情况，就把数据加到对应数组索引链表下，查询数据就是先查数组，没有就再查对应链表，java在链表达到一定长度时还引入了红黑树，来优化结构。</p>
<p>开放寻址，分为三种方法，线性探测，平方探测，多次哈希，简单的说一下前两个，当插入元素遇到哈希冲突时，从冲突位置向后线性遍历，步长通常为1，直到找到空桶，放入元素，查询也是，遇到空桶就说明元素不在哈希表中返回None,需要注意的是，使用开放寻址结构的哈希不能随意删除元素，删除会让数组中产生一个空桶，查询元素时，访问到空桶就会返回，这个空桶之后的元素就会变得无法访问，解决之个问题要使用懒删除，即删除元素使用一个常量标记这个桶，遇到这个常量的时候不返回继续向下寻找，直到找到对应元素，当删除元素多之后会拖慢搜索时间，为了优化当遇到常量标记的桶时，记录其索引，在找到正确的元素之后调换二者位置，让正常的桶更靠近探测起始点。</p>
<p>##3.字典
Python 程序中的字典跟现实生活中字典非常像，允许我们以键值对的形式保存数据，再通过键访问对应的值。字典是一种非常有利于数据检索的数据类型，但是需要再次提醒大家，字典中的键必须是不可变类型，列表、集合、字典等类型的数据都不能作为字典的键。</p>
<pre><code class="language-python">xinhua = {
    '麓': '山脚下',
    '路': '道，往来通行的地方；方面，地区：南～货，外～货；种类：他俩是一～人',
    '蕗': '甘草的别名',
    '潞': '潞水，水名，即今山西省的浊漳河；潞江，水名，即云南省的怒江'
}
print(xinhua)
person = {
    'name': '王大锤',
    'age': 55,
    'height': 168,
    'weight': 60,
    'addr': '成都市武侯区科华北路62号1栋101', 
    'tel': '13122334455',
    'emergence contact': '13800998877'
}
print(person)</code></pre><p>需要注意的就是字典的键必须是不可变类型，因为可变类型因为内容的改变最后的哈希值也会变，集合也是储存在其中的内容只能为不可变类型，其底层也是哈希表实现的嘛，另外就是再给python字典类型赋值的时候如果字点中没有这个键值对数据python就会自动创建这个键值对存入到其中，其他的就不多赘述了。
##4.函数
Pyrhon函数的定义可以通过下图来了解：</p>
<p></p>
<p>###函数的参数
位置参数：在调用函数按照顺序从左到右依次传入参数</p>
<pre><code class="language-python">def make_judgement(a, b, c):
    """判断三条边的长度能否构成三角形"""
    return a + b &gt; c and b + c &gt; a and a + c &gt; b
print(make_judgement(1, 2, 3))  # False
print(make_judgement(4, 5, 6))  # True</code></pre><p>关键字参数：通过参数明 = 参数值的形式传入参数</p>
<pre><code class="language-python">print(make_judgement(b=2, c=3, a=1))  # False
print(make_judgement(c=6, b=4, a=5))  # True</code></pre><p>可变参数：Python 语言中可以通过星号表达式语法让函数支持可变参数。所谓可变参数指的是在调用函数时，可以向函数传入0个或任意多个参数，双星号代表通过“参数名=参数值”的形式传入若干个参数。</p>
<pre><code class="language-python"># 参数列表中的**kwargs可以接收0个或任意多个关键字参数
# 调用函数时传入的关键字参数会组装成一个字典（参数名是字典中的键，参数值是字典中的值）
# 如果一个关键字参数都没有传入，那么kwargs会是一个空字典
def foo(*args, **kwargs):
    print(args)
    print(kwargs)

foo(3, 2.1, True, name='骆昊', age=43, gpa=4.95)</code></pre><p>###函数进阶
函数传参可以传方法，实现解耦和，逻辑如下：</p>
<pre><code class="language-python">#原本形式
def calc(*args, **kwargs):
    items = list(args) + list(kwargs.values())
    result = 0
    for item in items:
        if type(item) in (int, float):
            result += item
    return result
#进阶
def calc(init_value, op_func, *args, **kwargs):
    items = list(args) + list(kwargs.values())
    result = init_value
    for item in items:
        if type(item) in (int, float):
            result = op_func(result, item)
    return result
  
def add(x, y):
    return x + y


def mul(x, y):
    return x * y
#如果要做求和的运算，我们可以按照下面的方式调用calc函数。
print(calc(0, add, 1, 2, 3, 4, 5))  # 15
#如果要做求乘积运算，我们可以按照下面的方式调用calc函数。
print(calc(1, mul, 1, 2, 3, 4, 5))  # 120 </code></pre><p>###lambda函数
在使用高阶函数的时候，如果作为参数或者返回值的函数本身非常简单，一行代码就能够完成，也不需要考虑对函数的复用，那么我们可以使用 lambda 函数。Python 中的 lambda 函数是没有的名字函数，所以很多人也把它叫做匿名函数，lambda 函数只能有一行代码，代码中的表达式产生的运算结果就是这个匿名函数的返回值。之前的代码中，我们写的is_even和square函数都只有一行代码，我们可以考虑用 lambda 函数来替换掉它们，代码如下所示。</p>
<pre><code class="language-python">old_nums = [35, 12, 8, 99, 60, 52]
new_nums = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, old_nums)))
print(new_nums)  # [144, 64, 3600, 2704]</code></pre><p>通过上面的代码可以看出，定义 lambda 函数的关键字是lambda，后面跟函数的参数，如果有多个参数用逗号进行分隔；冒号后面的部分就是函数的执行体，通常是一个表达式，表达式的运算结果就是 lambda 函数的返回值，不需要写return 关键字。</p>
<p>前面我们说过，Python 中的函数是“一等函数”，函数是可以直接赋值给变量的。在学习了 lambda 函数之后，前面我们写过的一些函数就可以用一行代码来实现它们了，大家可以看看能否理解下面的求阶乘和判断素数的函数。</p>
<pre><code class="language-python">import functools
import operator

# 用一行代码实现计算阶乘的函数
fac = lambda n: functools.reduce(operator.mul, range(2, n + 1), 1)

# 用一行代码实现判断素数的函数
is_prime = lambda x: all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))

# 调用Lambda函数
print(fac(6))        # 720
print(is_prime(37))  # True</code></pre><blockquote>
<p>提示1：上面使用的reduce函数是 Python 标准库functools模块中的函数，它可以实现对一组数据的归约操作，类似于我们之前定义的calc函数，第一个参数是代表运算的函数，第二个参数是运算的数据，第三个参数是运算的初始值。很显然，reduce函数也是高阶函数，它和filter函数、map函数一起构成了处理数据中非常关键的三个动作：过滤、映射和归约。</p>
</blockquote>
<blockquote>
<p>提示2：上面判断素数的 lambda 函数通过range函数构造了从 2 到 
x
 的范围，检查这个范围有没有x的因子。all函数也是 Python 内置函数，如果传入的序列中所有的布尔值都是True，all函数返回True，否则all函数返回False。</p>
</blockquote>
<p>##5.装饰器
这个是Python的特色语法，说是这么说但是在java中使用Springboot框架，里面的Aop切面编程功能和这个装饰器的功能基本一致，非常相像，所以我这里不过多赘述可以直接访问地址仔细了解<a href="https://github.com/jackfrued/Python-100-Days/blob/master/Day01-20/17.%E5%87%BD%E6%95%B0%E9%AB%98%E7%BA%A7%E5%BA%94%E7%94%A8.md">函数高级应用-装饰器和递归</a></p>

      <p style='text-align: right'>
      <a href='https://geekrain.site/posts/codeRecoder/python02#comments'>看完了？说点什么呢</a>
      </p>
    ]]>
    </content:encoded>
  <guid isPermaLink="false">69820676c464ea83fd80fdd2</guid>
  <category>posts</category>
<category>技术upup!</category>
 </item>
  
</channel>
</rss>