当前位置:   article > 正文

建议收藏!超详细的selenium爬虫使用教程!你想知道的都在这里!_爬虫工具selenium的使用方法

爬虫工具selenium的使用方法

1. 引言

在数字化的时代,网络已经成为了我们获取信息、数据和知识的首选渠道。而对于经管类的科研工作者而言,获取和处理来自网络的数据不仅仅是一种能力,更是研究的必要环节。但面对复杂多变的网络环境,传统的爬虫技术可能已经无法满足我们的需求,特别是在遇到需要模拟用户交互或数据动态加载的网站时。那么,有没有一种工具可以轻松、高效地解决这些问题呢?答案是肯定的——那就是Selenium。

为什么选择Selenium?

Selenium不同于常规的爬虫工具。它最初是为了网页自动化测试而设计的,这意味着它可以模拟几乎所有的用户行为:点击、滚动、键入、拖放等。这使得Selenium能够轻松应对各种复杂的网页结构和交互场景。对于那些数据是通过JavaScript动态加载,或者需要登录后才能访问的网站,Selenium都能够胜任。

Selenium与其他爬虫工具的对比

与传统的爬虫工具比如BeautifulSoup或Scrapy相比,Selenium的优势在于它可以模拟真实的浏览器环境,这意味着我们可以直接看到爬虫的行为和结果,也更容易调试。而且,由于Selenium支持多种编程语言和浏览器,它的适用范围非常广泛。当然,这也意味着使用Selenium可能需要更多的资源和学习成本,但对于需要模拟用户交互或处理动态加载数据的任务,这些成本是值得的。

2. 环境配置

在开始使用Selenium进行爬取之前,确保你的环境配置无误是非常重要的。一个良好的开发环境可以大大提高效率,减少在后续开发中可能出现的问题。

Python环境的搭建:

Python,作为一种广泛使用的编程语言,其强大的库支持和友好的语法使得它成为爬虫开发的首选。

  • 安装Python: 如果你还没有安装Python,可以访问Python官网下载合适的版本。推荐使用Python3,因为它拥有更多的特性和更好的支持。

  • 使用Anaconda: Anaconda是一个流行的Python发行版,它内置了大量的科学计算库,非常适合数据分析和科研工作。安装Anaconda后,你可以利用conda命令轻松管理Python环境和包。

Selenium库的安装:

有了Python,接下来就是安装Selenium库。这一步非常简单,只需使用pip命令即可:

pip install selenium  

  • 1
  • 2

这会安装Selenium的最新版本。安装完成后,你可以使用以下命令来验证是否安装成功:

import selenium  
print(selenium.__version__)  

  • 1
  • 2
  • 3

WebDriver的选择与配置:

为了让Selenium控制浏览器,我们需要一个名为WebDriver的中间件。根据你的浏览器选择相应的WebDriver。

  • Chrome: 对于Chrome浏览器,你需要下载ChromeDriver。这里我们再给大家提供一个更方便的下载途径:https://googlechromelabs.github.io/chrome-for-testing/#stable

  • Firefox: 对于Firefox浏览器,选择GeckoDriver。

  • 其他: Selenium还支持Safari、Edge等浏览器,但Chrome和Firefox是最常用的。

下载完成后,确保WebDriver的路径已添加到系统的PATH中,或者在初始化例如webdriver.Chrome()时指定其路径。例如:

from selenium import webdriver  
  
driver = webdriver.Chrome(executable_path='/path/to/chromedriver')  

  • 1
  • 2
  • 3
  • 4

完成上述步骤后,你的Selenium环境应该已经配置完毕,接下来可以开始编写你的第一个Selenium爬虫!

3. Selenium基础

掌握Selenium的基础操作是进行任何爬虫项目的基础。这部分内容将带你了解如何使用Selenium控制浏览器进行常见的操作,如启动、关闭浏览器,页面导航,以及窗口和标签页的管理。

启动浏览器与关闭浏览器:

使用Selenium的第一步是启动一个浏览器实例。以下是如何做到这一点的指南:

  • 启动Chrome浏览器:

    from selenium import webdriver  
      
    driver = webdriver.Chrome(executable_path='/path/to/chromedriver')  
    
    
    • 1
    • 2
    • 3
    • 4
  • 启动Firefox浏览器:

    from selenium import webdriver  
      
    driver = webdriver.Firefox(executable_path='/path/to/geckodriver')  
    
    
    • 1
    • 2
    • 3
    • 4

当你完成爬虫任务后,别忘了关闭浏览器。这有两种方式:

  1. driver.close(): 这将关闭当前浏览器窗口。

  2. driver.quit(): 这将退出浏览器进程,关闭所有相关窗口。

页面导航:前进、后退、刷新

在许多情况下,我们需要进行页面导航,如访问一个URL、后退或刷新页面。

  • 访问URL:

    driver.get("https://www.example.com")  
    
    
    • 1
    • 2
  • 后退:

    driver.back()  
    
    
    • 1
    • 2
  • 前进:

    driver.forward()  
    
    
    • 1
    • 2
  • 刷新页面:

    driver.refresh()  
    
    
    • 1
    • 2

窗口与标签页的管理:

当你进行爬取任务时,可能会遇到多个窗口或标签页。Selenium提供了方法来切换和管理这些窗口。

  • 获取所有窗口句柄:

    windows = driver.window_handles  
    
    
    • 1
    • 2

    windows 是一个列表,其中包含所有窗口的句柄。

  • 切换到另一个窗口:

    driver.switch_to.window(windows[1])  
    
    
    • 1
    • 2

    以上代码切换到第二个窗口。

  • 关闭当前窗口并切换回原始窗口:

    driver.close()  
    driver.switch_to.window(windows[0])  
    
    
    • 1
    • 2
    • 3

这只是Selenium的冰山一角。通过这些基础操作,你已经可以进行大部分的页面导航和浏览器控制。在接下来的章节中,我们将学习如何与网页上的元素进行交互,这是爬虫的核心内容。

4. 元素定位策略

成功进行爬虫任务的关键在于准确地定位和操作页面上的元素。Selenium提供了多种方法来定位页面元素,以满足不同的需求和场景。在这一部分,我们将详细介绍这些方法,以及如何在Selenium 4中使用它们。

ID、Name、Class

这些是最基本且常用的元素定位方法,大多数网页元素都有这些属性。

  • 通过ID定位:

    在Selenium 4中,find_elements_by_id的方法已经不再推荐使用,而是建议使用以下方式:

    from selenium.webdriver.common.by import By  
      
    element = driver.find_element(By.ID, "your_element_id")  
    
    
    • 1
    • 2
    • 3
    • 4
  • 通过Name定位:

    element = driver.find_element(By.NAME, "your_element_name")  
    
    
    • 1
    • 2
  • 通过Class Name定位:

    element = driver.find_element(By.CLASS_NAME, "your_element_class_name")  
    
    
    • 1
    • 2

XPath与CSS Selector

对于更复杂的页面结构,特别是当元素没有ID、Name或Class时,XPath和CSS Selector是非常有用的工具。

  • 通过XPath定位:

    XPath是一种定义在XML文档中查找信息的语言。在网页爬虫中,它常用于定位没有明确标识的元素。

    element = driver.find_element(By.XPATH, "//tagname[@attribute='value']")  
    
    
    • 1
    • 2
  • 通过CSS Selector定位:

    CSS Selector是另一种非常强大的定位方法,特别是在需要与样式表交互的时候。

    element = driver.find_element(By.CSS_SELECTOR, "tagname[attribute='value']")  
    
    
    • 1
    • 2

复合定位策略

在某些情况下,一个单一的定位策略可能不足以准确找到一个元素。此时,可以使用复合策略来结合多个条件。

例如,如果需要找到一个同时满足特定ID和特定Class的元素,可以使用以下的XPath:

element = driver.find_element(By.XPATH, "//*[@id='your_id' and @class='your_class_name']")  

  • 1
  • 2

使用By对象进行定位

如前所述,在Selenium 4中,推荐使用By对象进行元素的定位。它不仅提供了一种更一致和简洁的方式来定位元素,还支持多种定位策略,如ID、Name、XPath、CSS Selector等。

5. 页面交互操作

成功定位页面元素后,真正的魔法就是与这些元素进行交互,从而实现数据抓取、自动填表、自动点击等操作。这一部分将教你如何使用Selenium执行这些交互。

元素的点击、输入、清除

一旦你定位到一个元素,你可以执行许多操作,如点击、输入和清除。

  • 点击元素:

    如果元素是一个按钮或链接,你可以使用click方法来点击它:

    element = driver.find_element(By.ID, "button_id")  
    element.click()  
    
    
    • 1
    • 2
    • 3
  • 输入文本:

    对于文本框,你可以使用send_keys方法来输入数据:

    textbox = driver.find_element(By.NAME, "textbox_name")  
    textbox.send_keys("Hello, Selenium!")  
    
    
    • 1
    • 2
    • 3
  • 清除文本框内容:

    如果你需要先清除文本框的当前内容再输入,可以使用clear方法:

    textbox.clear()  
    textbox.send_keys("New Text Here!")  
    
    
    • 1
    • 2
    • 3

下拉框、单选框、复选框操作

对于这些特殊的输入元素,Selenium提供了专门的方法来处理它们。

  • 下拉框:

    使用Select类来处理下拉框元素:

    from selenium.webdriver.support.ui import Select  
      
    dropdown = Select(driver.find_element(By.ID, "dropdown_id"))  
    dropdown.select_by_visible_text("Option Text")  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 单选框和复选框:

    单选框和复选框的操作与普通按钮类似,但你可能需要先判断它的状态:

    checkbox = driver.find_element(By.ID, "checkbox_id")  
      
    if not checkbox.is_selected():  
      checkbox.click()  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

执行JavaScript代码

有时,Selenium的内置方法可能不足以满足你的需要,或者某些操作在Selenium中难以实现。在这种情况下,直接执行JavaScript代码可能是一个好方法:

driver.execute_script("arguments[0].scrollIntoView();", element)  

  • 1
  • 2

上面的代码会滚动页面直到element元素出现在视窗中。

另一个例子,如果你需要更改页面上元素的属性,例如隐藏的文本框:

driver.execute_script("document.getElementById('hidden_textbox').style.display = 'block';")  

  • 1
  • 2

6. 等待策略

在自动化过程中,等待是一个非常关键的环节。由于网络延迟、服务器处理时间、客户端渲染时间等原因,页面上的元素可能不会立即可用。因此,正确的等待策略是确保爬虫稳定性的关键。Selenium提供了几种等待方法,以适应各种场景。

隐式等待

隐式等待会指定一个固定的时间,让WebDriver在查找元素时等待指定的时间。如果在这段时间内元素可用,则进行下一步;否则,它会抛出一个NoSuchElementException

driver.implicitly_wait(10)  # 10秒  

  • 1
  • 2

这里,浏览器会在尝试找到任何元素之前等待最多10秒。如果在10秒内元素出现了,它会立即继续;否则,到达10秒后,将抛出异常。

显式等待

与隐式等待相反,显式等待允许你为某个特定的条件设定等待时间。这是一种更智能的等待方法,因为它会等待某个条件成立,而不仅仅是固定的时间。

为了使用显式等待,你需要结合使用WebDriverWait和预期条件(expected_conditions):

from selenium.webdriver.common.by import By  
from selenium.webdriver.support.ui import WebDriverWait  
from selenium.webdriver.support import expected_conditions as EC  
  
element = WebDriverWait(driver, 10).until(  
    EC.presence_of_element_located((By.ID, "myElementId"))  
)  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面的代码会等待10秒,直到ID为myElementId的元素出现在页面上。

自定义等待条件

除了Selenium提供的预期条件外,你还可以定义自己的等待条件:

def element_has_css_class(css_class):  
    def predicate(driver):  
        element = driver.find_element(By.ID, "myElementId")  
        return css_class in element.get_attribute("class")  
    return predicate  
  
element = WebDriverWait(driver, 10).until(element_has_css_class("myClass"))  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面的代码定义了一个自定义等待条件,它会等待元素具有特定的CSS类。

7. 处理特殊页面元素

在Web自动化的过程中,你可能会遇到一些特殊的页面元素和场景,如iFrames、弹出窗口、警告框和Cookies。正确地处理这些元素是确保自动化脚本稳定运行的关键。在本节中,我们将介绍如何使用Selenium处理这些情境。

处理IFrame与弹出窗口

  • IFrames:

    如果一个元素位于iFrame内部,直接定位这个元素会失败。首先,你需要切换到这个iFrame:

    driver.switch_to.frame("iframe_name_or_id")  
    
    
    • 1
    • 2

    或者,如果你有这个iFrame的WebElement:

    iframe_element = driver.find_element(By.TAG_NAME, "iframe")  
    driver.switch_to.frame(iframe_element)  
    
    
    • 1
    • 2
    • 3

    完成iFrame中的操作后,切换回主内容:

    driver.switch_to.default_content()  
    
    
    • 1
    • 2
  • 弹出窗口:

    当点击某些链接或按钮时,可能会打开新的浏览器窗口或选项卡。你可以使用以下方式在它们之间切换:

    # 切换到新打开的窗口  
    driver.switch_to.window(driver.window_handles[-1])  
      
    # 切换回原始窗口  
    driver.switch_to.window(driver.window_handles[0])  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

处理Alerts和确认框

  • Alerts:

    网页上的警告框(或确认框)是特殊的对话框,不能像普通元素那样进行交互。你可以这样操作它们:

    alert = driver.switch_to.alert  
    alert_text = alert.text  
    alert.accept()  # 点击"OK"  
    
    
    • 1
    • 2
    • 3
    • 4
  • 确认框:

    对于确认框,除了接受外,你还可以选择取消:

    alert.dismiss()  # 点击"Cancel"  
    
    
    • 1
    • 2

处理Cookies

Cookies是网站存储在浏览器上的小片数据。Selenium允许你读取和设置这些Cookies。

  • 获取所有Cookies:

    all_cookies = driver.get_cookies()  
    
    
    • 1
    • 2
  • 添加一个Cookie:

    driver.add_cookie({  
      "name": "my_cookie_name",  
      "value": "my_cookie_value"  
    })  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 删除一个Cookie:

    driver.delete_cookie("my_cookie_name")  
    
    
    • 1
    • 2
  • 删除所有Cookies:

    driver.delete_all_cookies()  
    
    
    • 1
    • 2

8. 动态加载数据的处理

在现代网站中,数据经常是动态加载的,而不是与页面一起立即加载。这通常是通过AJAX技术实现的。为了有效地抓取这些数据,我们需要了解如何处理这些动态加载的内容。

理解AJAX

AJAX(异步JavaScript和XML)是一种技术,允许Web页面在不重新加载整个页面的情况下从服务器请求额外的数据。这意味着当你用Selenium访问一个页面时,即使页面似乎已经加载完成,某些数据可能仍然在后台加载。

例如,许多网站的滚动功能是基于AJAX的:当你滚动到页面底部时,更多内容会自动加载。或者,当你点击一个按钮或选项卡时,内容可能会动态更改,而不是导航到新的页面。

WebDriverWait与expected_conditions的结合使用

为了有效地处理AJAX加载的数据,我们需要结合使用WebDriverWaitexpected_conditions,这样我们可以等待特定的元素或条件变得可用。

  1. 等待元素出现

    当新的内容被动态加载到页面时,我们可以等待这些元素出现:

    from selenium.webdriver.support.ui import WebDriverWait  
    from selenium.webdriver.support import expected_conditions as EC  
    from selenium.webdriver.common.by import By  
      
    element = WebDriverWait(driver, 10).until(  
       EC.presence_of_element_located((By.ID, "newlyLoadedElementId"))  
    )  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  2. 等待元素可见

    有时元素可能存在于DOM中,但不可见。在这种情况下,我们需要等待元素变得可见:

    element = WebDriverWait(driver, 10).until(  
       EC.visibility_of_element_located((By.ID, "newlyLoadedElementId"))  
    )  
    
    
    • 1
    • 2
    • 3
    • 4
  3. 其他常用条件

  • 等待元素变得可以点击:

    python element = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, "buttonId")) )

  • 等待所有元素都加载完成(对于列表):

    python elements = WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CLASS_NAME, "itemClass")) )

9. Selenium高级话题

在对Selenium有了一定的熟悉之后,你可能会想探索一些高级技巧来增强你的自动化脚本或爬虫的功能。在本节中,我们将讨论两个高级主题:使用代理和WebDriver的并发执行。

使用Proxy代理

使用代理服务器可以为你的Selenium爬虫提供多种好处,如隐藏真实IP地址、绕过地区限制或分布式爬取。以下是如何配置Selenium使用代理:

  1. 使用Chrome:

    from selenium import webdriver  
      
    PROXY = "127.0.0.1:8080"  # 示例代理IP和端口  
      
    chrome_options = webdriver.ChromeOptions()  
    chrome_options.add_argument(f"--proxy-server={PROXY}")  
      
    driver = webdriver.Chrome(options=chrome_options)  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  2. 使用Firefox:

    from selenium import webdriver  
      
    PROXY = "127.0.0.1:8080"  
      
    firefox_capabilities = webdriver.DesiredCapabilities.FIREFOX  
    firefox_capabilities['marionette'] = True  
      
    firefox_capabilities['proxy'] = {  
        "proxyType": "MANUAL",  
        "httpProxy": PROXY,  
        "ftpProxy": PROXY,  
        "sslProxy": PROXY  
    }  
      
    driver = webdriver.Firefox(capabilities=firefox_capabilities)  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

WebDriver的并发执行

当你需要在多个浏览器实例中同时执行任务时,可以使用Python的多线程或多进程。但请注意,大量并发可能会导致系统变慢或不稳定。

  1. 使用threading模块:

    import threading  
    from selenium import webdriver  
      
    def task():  
        driver = webdriver.Chrome()  
        driver.get("http://example.com")  
        # ... 其他操作  
        driver.quit()  
      
    threads = []  
      
    for _ in range(5):  # 创建5个线程  
        t = threading.Thread(target=task)  
        threads.append(t)  
        t.start()  
      
    for thread in threads:  
        thread.join()  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  2. 使用concurrent.futures模块:

    更现代的方法是使用concurrent.futures,它提供了一个更高级的接口来管理线程和进程:

    from concurrent.futures import ThreadPoolExecutor  
    from selenium import webdriver  
      
    def task():  
        driver = webdriver.Chrome()  
        driver.get("http://example.com")  
        # ... 其他操作  
        driver.quit()  
      
    with ThreadPoolExecutor(max_workers=5) as executor:  
        for _ in range(5):  
            executor.submit(task)  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

9. Selenium高级话题

随着你在Selenium自动化领域的深入,你可能会想要探索更多的高级特性来增强你的脚本。这一节将深入探讨如何使用代理、并发执行WebDriver实例以及如何进行截图和录屏。

使用Proxy代理

代理是Web请求的中介,它可以帮助你隐藏真实IP、绕过地理限制或加速请求。要在Selenium中设置代理,你可以使用webdriver.Proxy()

示例(对于Chrome浏览器):

from selenium import webdriver  
  
PROXY = "your_proxy_ip:port"  
  
chrome_options = webdriver.ChromeOptions()  
chrome_options.add_argument(f"--proxy-server={PROXY}")  
  
driver = webdriver.Chrome(options=chrome_options)  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这样,所有通过Selenium发出的请求都将通过指定的代理服务器。

WebDriver的并发执行

并发执行是同时运行多个WebDriver实例的技术。Python的threadingmultiprocessing模块都可以实现这一点。但要记住,过多的并发操作可能导致你的机器过载或被目标网站封锁。

简单的示例使用threading

import threading  
from selenium import webdriver  
  
def start_browser(url):  
    driver = webdriver.Chrome()  
    driver.get(url)  
    # ...其他操作...  
    driver.quit()  
  
urls = ["http://example.com", "http://anotherexample.com"]  
  
threads = []  
for url in urls:  
    thread = threading.Thread(target=start_browser, args=(url,))  
    threads.append(thread)  
    thread.start()  
  
for thread in threads:  
    thread.join()  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

截图

  • 截图:

    Selenium提供了一个简单的方法来截取当前浏览器窗口的屏幕截图:

    driver.get("http://example.com")  
    driver.save_screenshot("screenshot.png")  
    
    
    • 1
    • 2
    • 3

10. 结语

在我们深入探讨Selenium的多个方面之后,很重要的一点是认识到,像所有工具一样,Selenium在使用时需要遵循一些基本的注意事项和最佳实践。此外,网页抓取和自动化不仅受到技术限制,还受到法律和道德的约束。

注意事项与最佳实践

  • 尊重Robots.txt: 大多数网站都有一个robots.txt文件,其中指定了哪些页面可以爬取,哪些不可以。尽管Selenium不像其他爬虫库那样自动遵循它,但你仍应当遵守其规定。

  • 不过度请求: 大量的快速连续请求可能会导致服务器过载,甚至可能导致你的IP被封锁。合理地添加延迟和限制请求频率是一个好习惯。

  • 错误处理: 网页结构的更改、临时的网络问题或其他外部因素都可能导致你的脚本出错。确保在你的脚本中添加适当的错误处理机制,如try-except块。

  • 保存数据: 确保经常保存你的爬取结果,以避免由于意外中断而丢失数据。

法律与道德约束

  • 法律约束: 在许多国家和地区,无授权的数据抓取可能是非法的,特别是当这些数据用于商业目的时。确保你了解和遵守相关的数据抓取和隐私法规。

  • 道德问题: 即使技术上可以执行某个操作,也不意味着应该这样做。爬取个人数据、滥用资源或其他潜在的伤害行为都应当避免。

  • 获取许可: 如果你不确定是否可以抓取某个网站,与该网站的管理员联系并请求许可可能是最好的办法。


回首本篇教程,我们深入探讨了Selenium的各种功能和高级话题。但值得注意的是,知识和工具只是起点。真正的技能和理解来自于实际的应用和实践。在探索网页自动化和数据抓取的世界时,请始终保持警惕,遵循最佳实践,并尊重法律和道德准则。祝您学习愉快,探索无尽!

因为爬虫这种技术,既不需要你系统地精通一门语言,也不需要多么高深的数据库技术。

我这里准备了详细的Python资料,除了为你提供一条清晰的学习路径,我甄选了最实用的学习资源以及庞大的实例库。短时间的学习,你就能够很好地掌握爬虫这个技能,获取你想得到的数据。

01 专为0基础设置,小白也能轻松学会

我们把Python的所有知识点,都穿插在了漫画里面。

在Python小课中,你可以通过漫画的方式学到知识点,难懂的专业知识瞬间变得有趣易懂。
在这里插入图片描述
在这里插入图片描述
你就像漫画的主人公一样,穿越在剧情中,通关过坎,不知不觉完成知识的学习。

02 无需自己下载安装包,提供详细安装教程

在这里插入图片描述

03 规划详细学习路线,提供学习视频

在这里插入图片描述
在这里插入图片描述

04 提供实战资料,更好巩固知识

在这里插入图片描述

05 提供面试资料以及副业资料,便于更好就业

在这里插入图片描述
在这里插入图片描述
这份完整版的Python全套学习资料已经上传CSDN,朋友们如果需要也可以扫描下方csdn官方二维码或者点击主页和文章下方的微信卡片获取领取方式,【保证100%免费】

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/111658
推荐阅读
相关标签
  

闽ICP备14008679号