ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python Selenium实战:从零搭建到动态网页数据采集

Python Selenium实战:从零搭建到动态网页数据采集 1. 为什么会选择Seleniumrequests做不到的事1.1 从一次数据采集翻车说起我之前一直习惯用Python写requests采集脚本接口直接返回JSON速度快、逻辑清爽。直到有一天我需要抓一个数据报表页面打开网页源码一看里面根本没有数据只有一个空的div容器。真正的数据是页面加载之后由一段JavaScript去后端拉取、再通过DOM操作渲染出来的。requests拿到的是“毛坯房”而你要的数据是“精装修”之后才出现的。那段时间我试过自己解析接口参数翻Network面板找XHR请求结果发现请求头里带了一个滑动签名每次生成的逻辑都不一样。纯靠requests模拟成本越滚越高。后来我一个做测试的朋友提了一嘴“你为什么不直接用浏览器跑呢”这句话点醒了我。Python Selenium能把真实的浏览器拉起来像人一样打开URL、输入文本、点击按钮、翻页再把渲染完的网页内容交给你。浏览器自动化这个词听起来很高端本质其实就是“让代码替你把键盘和鼠标干了”。这篇内容包括环境搭建、元素定位、等待机制、综合实战和反爬边界我把从零摸索过程中踩过的坑都写进去了。适合刚接触Python、想用Selenium做网页自动化、写采集脚本或者跑UI自动化验证的新手。如果你只是想要一个能跑通的demo可以直接跳到第五节如果你想少走弯路建议从头到尾读一遍。1.2 Selenium和requests的本质区别很多人搞不清Selenium和requests到底该用哪个其实它们的定位完全不同。requests是HTTP客户端它模拟的是浏览器向服务器发送请求、接收响应但它不负责解析JavaScript、不渲染页面。Selenium是浏览器自动化框架它通过WebDriver协议去驱动真实的浏览器内核Chrome打开页面之后所有的脚本都执行完了你再拿到的DOM就是用户真正看到的内容。对比维度requestsSelenium工作原理发送HTTP请求拿到HTML/JSON驱动真实浏览器执行完整页面处理JS动态内容需要自己找接口、模拟签名天然支持页面渲染完再读取运行速度快毫秒级慢秒级起步资源开销低高每个driver都占内存适用场景公开API、静态页、接口调试动态渲染、表单操作、UI验证用生活化的类比requests像一个派去取快递的人他只负责把包裹拿回来不管里面是什么材质、怎么包装Selenium则像个跑腿小哥他会帮你一层层拆开包装、打开盒子、把里面的东西摆好再拍照发给你。前者轻快但只能拿到表面的东西后者笨重但能触及最终形态。选择上我的经验是能直接抓到接口就优先requests速度和稳定性都更好一旦发现数据是JS动态渲染、需要登录态维持、有复杂交互操作就别硬刚直接上Selenium你会发现世界一下子清净了。1.3 Selenium适合谁、不适合谁我知道很多人是因为采集动态页面才认识Selenium的但它能做的事远不止这些。我身边有做测试的朋友用它跑回归用例每天晚上自动登录系统、点击菜单、校验数据有做运营的朋友用它定时巡检竞品公开页面抓价格变化还有做数据分析的同事用它把内部报表系统的导出操作自动化省去了每天手动点十几次按钮的重复劳动。但Selenium不是万能的。如果你的目标是每天采集几百万条公开数据用Selenium会很吃力——开浏览器占内存、网络开销又大远不如用官方API或者接口采集划算。还有一些场景比如验证码识别、极验滑块这类对抗型需求我的个人建议是不要碰技术上折腾成本非常高法律风险和道德风险也不可控。Selenium最舒适的区间是“低频、中等规模、强交互”的自动化任务。2. 环境配置中的三个大坑Python、pip和WebDriver版本匹配2.1 安装Python与Selenium库搭建环境的第一步是装Python。这里有一个非常常见的问题很多新手直接去官网下载了最新版Python结果在终端敲python命令弹出来的是Windows应用商店的跳转页面或者系统自带了Python但版本特别老。我的建议是去python.org下载官方安装包安装时务必勾选“Add Python to PATH”否则后面pip和python命令都会找不到。装完之后在命令行里跑python --version能正常打印版本号说明环境没问题。接下来安装Selenium命令非常简单pip install selenium如果你用的是Python 3.12以上的版本或者之前装过旧版本我建议顺手升级一下pip install --upgrade selenium顺便说一句现在Selenium已经出到4.x版本了。4.x和3.x在API上有一个很大的区别以前写find_element_by_id这种老方法已经废弃统一改成了driver.find_element(By.ID, xxxx)所以你搜索中文教程时如果看到旧写法最好手动替换成新写法否则会有DeprecationWarning虽然暂时能跑但总有天会被移除。2.2 WebDriver版本匹配新手卡关重灾区装好selenium之后你写的第一段代码大概率长这样from selenium import webdriver driver webdriver.Chrome() driver.get(https://www.baidu.com) print(driver.title) driver.quit()然后你运行结果报了一堆红字最常见的是selenium.common.exceptions.SessionNotCreatedException: This version of ChromeDriver only supports Chrome version 116这个报错的意思很明确你下载的ChromeDriver和本机Chrome版本对不上。WebDriver本质上是一个翻译官它负责把Selenium的命令翻译成浏览器能听懂的操作。如果翻译官和浏览器的语言版本不一致整个通信就会失败。很多新手在这里心态直接崩了以为是自己代码写错了折腾半天发现是版本问题。解决办法分两步。第一步打开Chrome菜单里的“关于Chrome”看当前版本号比如你看到的是133.0.6943.126那就要找对应主干版本133的ChromeDriver。第二步去ChromeDriver的下载页面或者国内镜像站下载同版本的zip包把里面的chromedriver.exe放到一个固定目录然后在代码里指定路径from selenium import webdriver from selenium.webdriver.chrome.service import Service service Service(rD:\tools\chromedriver\chromedriver.exe) driver webdriver.Chrome(serviceservice)这里还藏着一个小细节很多教程让你把chromedriver.exe放到和Python同一个目录或者加到系统PATH里。这么做不是不行但多个项目共用一个driver只要版本一更新全部脚本跟着遭殃。更稳妥的做法是每个项目单独维护一个driver/目录版本锁定互不干扰。2.3 更省心的WebDriver管理方案手动画版本匹配确实麻烦而且Chrome有时候会悄悄自动升级版本一升你的driver就废了。这里给你推荐一个我一直在用的库webdriver-manager。安装一行命令pip install webdriver-manager然后代码变成这样from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice)它会自动检测你本机Chrome的版本去下载匹配的ChromeDriver然后在本地做缓存。第二次启动时如果浏览器没升级就直接用缓存省去重复下载。这个库解决的不只是ChromeFirefox、Edge也能管。另外提一嘴Selenium 4.6以上版本内置了Selenium Manager理论上你什么都不配置直接webdriver.Chrome()就能自动找driver。但有坑——首次运行时它要去官方仓库下载国内网络经常卡住。所以我给你的后备方案仍然是手动指定Service路径两条路都要会出了问题才不会无路可走。3. 定位元素的底层逻辑8种定位方式怎么选3.1 八种定位方式概览Selenium所有操作的前提是先找到元素。官方最常用的定位方式一共有八种对应关系如下定位方式写法适用场景IDBy.ID元素有唯一id优选方案NAMEBy.NAME表单输入框的name属性CLASS_NAMEBy.CLASS_NAME使用class属性定位TAG_NAMEBy.TAG_NAME按标签名如div、a定位CSS_SELECTORBy.CSS_SELECTOR灵活、速度快推荐掌握XPATHBy.XPATH万能兜底支持文本和层级LINK_TEXTBy.LINK_TEXT精确匹配链接文字PARTIAL_LINK_TEXTBy.PARTIAL_LINK_TEXT模糊匹配链接文字如果你用Chrome的开发者工具F12审查元素会发现每个页面的HTML结构都不一样但大多数情况下你只需要掌握三个ID、CSS_SELECTOR、XPATH。ID是最简单的但很多元素没有idCSS_SELECTOR简洁高效XPATH虽然慢一点但表达能力强到没有边界。我的习惯是能用ID就用ID否则用CSS只有面临复杂结构时才上XPATH。3.2 一个能少走弯路的定位器编写顺序很多新手一上来就照抄浏览器“复制XPath”功能结果代码看起来一团乱麻稍微改版就跑不起来。比如Chrome复制出来可能是这种//*[idapp]/div[2]/div/div[1]/div/div[2]/form/div[1]/div/div/span/input这种绝对路径中间任何一层结构变动定位就失效。我自己写定位器时遵循下面几条规则第一优先选靠近实际功能的属性。比如一个搜索框它的id或者placeholder往往比它外层div的class更有意义。你定位的是“搜索框”这个操作对象不是它的祖辈容器。第二避免依赖索引数字。div[2]这种写法很脆弱。如果页面在上面新增了一个块索引就变了脚本立马崩。尽量用能表达特征的路径比如# 差的做法 driver.find_element(By.XPATH, //form/div[1]/div/input) # 好的做法 driver.find_element(By.XPATH, //input[placeholder请输入关键词])第三文字内容谨慎使用。页面上的文案经常变动而且按钮文案、链接文字对国际化不友好。能用属性定位就不用文本内容。第四学会用contains()和and解决动态属性。很多元素的id里带了随机数比如user_abc123这时候可以写成driver.find_element(By.XPATH, //button[contains(id, user_) and contains(class, submit)])这些规则看着简单但能帮你省掉后面大量的维护时间。3.3 用百度首页做一次定位实战我们拿百度首页练练手。打开首页F12审查搜索框可以看到它的id是kw搜索按钮的id是su。于是“搜索Python”这个动作可以写成from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys driver webdriver.Chrome() driver.get(https://www.baidu.com) search_box driver.find_element(By.ID, kw) search_box.send_keys(Python) search_box.send_keys(Keys.ENTER) # 等待页面加载 driver.implicitly_wait(3) print(driver.title) driver.quit()如果你不想用id也可以换成CSS选择器#kw或者XPATH//input[idkw]。三种写法效果一样选哪种纯粹是个人习惯。我推荐你至少熟悉CSS和XPATH各一种写法将来遇到没有id的页面才能灵活应付。这里还有一个新手特别容易犯的错send_keys是在输入框内输入内容但如果你先点了其他地方再回来输入焦点会丢。稳妥的做法是输入前先click一下输入框。有些页面还有默认占位内容最好先清空再输入search_box.click() search_box.clear() search_box.send_keys(Python)4. 等待的艺术为什么脚本总在半夜才报错4.1 三种等待方式别再用sleep糊弄Selenium脚本最常见的问题就是“时好时坏”今天跑得好好的明天报找不到元素在家跑得好好的到公司就崩。99%的原因是页面加载是异步的你的代码却没等它加载完。新手最容易犯的错是到处乱加time.sleep(2)靠固定时间碰运气。网络慢的时候2秒不够网络快的时候白白浪费2秒。官方给的方案有三层# 1. 隐式等待设定一个上限轮询查找元素 driver.implicitly_wait(10) # 2. 显式等待针对特定条件等待 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 10) element wait.until(EC.presence_of_element_located((By.ID, kw))) # 3. 强制等待能不用就不用 time.sleep(2)implicitly_wait就像一个默认超时时间每次你调用find_element时如果元素没立刻出现它会最多等10秒。优点是省事但隐藏的问题是它会同时作用于所有查找操作某些情况反而掩盖了真正的错误影响调试。WebDriverWait才是工程上最推荐的方式。它结合expected_conditions可以非常精确地表达需求比如等待元素“可见”用visibility_of_element_located等待按钮“可以点击”用element_to_be_clickable。这种做法的核心不是磨时间而是“等到指定条件满足再继续”网络慢就多等网络快就少等效率最高。4.2 点击无效和找不到元素的真实原因写Selenium的日子里我总结出点击“无效”的几大原因基本都是玄学问题的根源第一种页面加载了但目标元素被遮挡。这种情况常常发生在弹窗、广告、浮层盖住了按钮。即使你定位到了元素点击事件实际点在了别的元素上。解决方法是先关掉浮层或者模拟键盘操作。第二种元素在iframe框架里。你直接driver.find_element找不到它必须先切换进去driver.switch_to.frame(frame-name) # 操作里面元素 driver.switch_to.default_content()第三种元素本身是隐藏的。有些网站用display:none控制元素等用户操作后才显示。通过By.XPATH能找到但不可见此时可以考虑先操作它的触发条件。第四种页面发生了跳转或局部刷新你之前持有的元素引用已经失效了。遇到这种就重新定位一次别一直攥着旧引用不放。调试这类问题我在实践中最喜欢用的方法是在定位前打印当前页面源码然后搜索目标特征可以快速定位是“元素没出现”还是“元素不可操作”。4.3 把等待封装成工具函数与其在每一段业务代码里写重复的WebDriverWait不如抽出一个工具模块。这是我封装过的一套常用等待函数分享出来直接抄from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def wait_element(driver, by, value, timeout10): 等待元素出现在DOM中不要求可见 return WebDriverWait(driver, timeout).until( EC.presence_of_element_located((by, value)) ) def wait_clickable(driver, by, value, timeout10): 等待元素可见且可点击 return WebDriverWait(driver, timeout).until( EC.element_to_be_clickable((by, value)) ) def wait_visible(driver, by, value, timeout10): 等待元素可见 return WebDriverWait(driver, timeout).until( EC.visibility_of_element_located((by, value)) ) def safe_click(driver, by, value, timeout10): 安全点击先等待可点击再滚动到位再点击 element wait_clickable(driver, by, value, timeout) driver.execute_script(arguments[0].scrollIntoView();, element) element.click() return element我在实际项目中基本只用safe_click和wait_visible这两个函数。有了它们脚本的稳定性会大幅提升。关键是理解一个点等待不是时间长度的博弈而是条件的博弈。你等待的一定是一个“能表明页面确实进入期望状态”的信号而不是一个固定秒数。5. 综合实战一个完整的登录-翻页-采集流程5.1 演示站点与流程拆解理论讲太多容易飘我拿两个公开的练习站点演示完整流程。登录部分用SauceDemo这是一个专门给UI测试使用的演示商城账号密码都是公开的随便造很适合练手。翻页采集部分用BooksToScrape一个专门给爬虫练习的书籍列表站有50页数据。整个流程拆成四步打开登录页输入用户名和密码点击登录按钮等待登录后页面元素出现确认登录成功进入列表页循环采集当前页数据点击“下一页”把采集结果输出到CSV文件幂等性很重要每一步都在上一个状态的确定结果上继续绝不靠猜。下面一步步来。5.2 登录模块用SauceDemo演示账号流程登录页的地址是https://www.saucedemo.com账号standard_user密码secret_sauce。核心代码如下from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from utils import wait_clickable, wait_visible # 使用前面封装好的工具 service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice) driver.implicitly_wait(5) driver.get(https://www.saucedemo.com) # 输入账号密码 username wait_visible(driver, By.ID, user-name) username.send_keys(standard_user) password wait_visible(driver, By.ID, password) password.send_keys(secret_sauce) # 点击登录 wait_clickable(driver, By.ID, login-button).click() # 验证是否登录成功等待商品列表标题出现 wait_visible(driver, By.CLASS_NAME, inventory_container) print(登录成功当前标题为, driver.title)这里有个要点登录按钮在DOM里一直存在但只有表单填完后它才真正可以点击所以用wait_clickable比wait_visible更合适。验证登录成功我选择等待商品容器出现而不是等几秒再看URL因为等待真正的结果元素才是最可靠的。如果发现自己脚本登录不上去第一件事是打开一个真实浏览器手动走一遍流程看看有没有弹窗、验证码、新手指引之类干扰元素。Selenium登录失败九成都是被这类“人类验证”卡住了。5.3 翻页与采集BooksToScrape数据抓取登录这个环节在SauceDemo上跑通之后我们来到BooksToScrape实现翻页采集。这个站的页面结构非常稳定每个书籍条目都在article.product_pod里书名在h3 a的title属性价格在p.price_color翻页按钮是li.next a。核心循环代码import csv import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice) driver.implicitly_wait(5) driver.get(https://books.toscrape.com) with open(books.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([书名, 价格]) while True: # 采集当前页数据 books driver.find_elements(By.CSS_SELECTOR, article.product_pod) for book in books: title book.find_element(By.CSS_SELECTOR, h3 a).get_attribute(title) price book.find_element(By.CSS_SELECTOR, p.price_color).text writer.writerow([title, price]) print(f已抓取{title} - {price}) # 找下一页按钮 next_buttons driver.find_elements(By.CSS_SELECTOR, li.next a) if not next_buttons: break next_buttons[0].click() time.sleep(1) # 等待新一页数据渲染 driver.quit() print(采集完成)这里我想特别说一下while True这种循环写法。它靠“找不到下一页按钮就break”结束而不是提前硬编码页数。这样做的好处是如果站点临时增加了新数据页脚本会自动多翻几页不用改代码。采集逻辑里我故意每页结束后加了time.sleep(1)理由不是为了等加载而是避免请求太快给服务器压力。实际生产中这个延时应该做成随机的比如random.uniform(0.8, 1.5)。5.4 异常兜底和日志输出跑单页demo很容易真正难的是脚本在无人值守环境下连续跑几小时不出问题。我在实践中总结了一套兜底机制import logging import traceback logging.basicConfig( levellogging.INFO, filenameauto_run.log, format%(asctime)s %(levelname)s %(message)s ) def safe_run(): driver None try: # driver初始化与业务逻辑 pass except Exception as e: logging.error(f发生异常{e}) if driver: # 出问题时候截图永远是最有用的排查手段 driver.save_screenshot(error.png) logging.info(已保存错误截图 error.png) finally: if driver: driver.quit() if __name__ __main__: safe_run()日志的作用是让你第二天早上复盘时知道脚本到底死在哪一步。截图则是直接把当时的页面状态保留下来。我的习惯是在每一个关键节点都打一行日志比如“开始登录”“登录成功开始翻页”“当前第3页采集完成”。这样一旦挂了看日志就知道是哪个环节出的问题不用靠猜。另外整段逻辑里千万不要遗漏driver.quit()。开发调试的时候没感觉一旦挂后台跑浏览器进程会越积越多最终把内存吃光。6. 关于反爬新手最容易踩的边界6.1 网站凭什么判断你是Selenium很多新手在公开网站上采集数据时都会遇到一个问题明明用的是Chrome为什么站点好像知道你是自动化脚本这里面的核心机制之一就是浏览器暴露的自动化标记。就拿Chrome来举例Selenium启动的浏览器进程里navigator.webdriver属性默认会被设为true。普通用户手动打开的浏览器这个值是false或者不存在。很多站点会在前端脚本里检查这个标记一旦发现就认为你不是真人。除此之外还有一套更隐蔽的检测维度浏览器指纹和用户行为模型。比如你的鼠标有没有移动轨迹、点击速度是不是均匀、滚动页面的时候是不是一滑到底、两次访问的间隔是不是精确到秒这些在服务器端都能形成特征。我在做合规采集时也研究过这些知识但这里要特别提醒一句理解反爬机制的初衷是为了让你知道哪些事不该做而不是教你破解。尤其是涉及登录账号、验证码、付费内容的站点任何“绕过检测”的手段在法律和平台条款上都有风险。把技术用在合法授权的数据采集上才是正路。6.2 合规操作的正确姿势所谓合规我个人的判断标准就三条第一目标网站允许爬虫访问公开数据或者你已经获得了授权第二遵守网站的robots.txt第三采集行为不会给对方服务器造成明显压力。在这个前提下你可以通过下面这些正常途径提高脚本的稳定性而不是去对抗站点的风控。最简单也是最有效的是限制频率。把上面那个time.sleep(1)改成随机延时import random time.sleep(random.uniform(1, 3))随机延时的意义在于模拟人类操作的自然间隔也避免固定节奏被服务端统计成特征。另外可以设置更真实的User-Agent和浏览器窗口大小options webdriver.ChromeOptions() options.add_argument(user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36) options.add_argument(window-size1920,1080)还有一个我一直在用的好习惯在采集之前先把页面里的图片加载关掉、禁用JavaScript之类的操作可以大幅提升性能且减少指纹暴露面。不过这类优化要看具体场景如果网站用JS渲染数据就绝对不能关JavaScript。6.3 一个小方法检查自己是否“可被识别”如果你想验证自己的Selenium脚本在目标网站上是否容易被识别可以直接在浏览器控制台里看一个值。我用一段很小的检测脚本帮新手理解这件事driver.get(https://example.com) result driver.execute_script(return navigator.webdriver;) print(result) # 大多数默认配置下会输出 True如果输出True说明浏览器把这个自动化标记暴露给了网页。但这并不意味着你要去伪造它而是说你知道了站点有办法识别自动化脚本做任何采集前都必须评估风险。我的建议是碰到反爬严格的网站优先去找官方API或者看看有没有授权接口。如果都没有那就换一个数据源不要跟反爬系统硬碰硬——时间和精力花在正经渠道上回报率高得多。7. 我在无数次失败后才想明白的几件事7.1 无头模式不是免费的午餐很多采集脚本追求“无头模式”就是不显示浏览器窗口在后台悄悄跑。听起来很美既像黑客又省资源但实际用下来坑很多。某些网站会根据浏览器是否支持无头模式返回不同内容甚至直接拒绝服务。Selenium的旧版无头模式还有一个问题它和真实有头模式在navigator对象上有细微差异更容易被站点识别。如果你确实需要无头运行新版Chrome已经支持了--headlessnew模式Selenium 4里可以这样写options webdriver.ChromeOptions() options.add_argument(--headlessnew) driver webdriver.Chrome(optionsoptions)但我的建议是开发调试阶段千万不要开无头。你盯着真实浏览器跑一遍能看到它每一步在干什么、卡在哪了、弹了什么窗排错效率会高很多。等你把脚本调稳了再决定要不要用无头模式上生产环境。7.2 浏览器资源管理与程序退出Selenium在单机上的资源消耗比大多数人想象得大。一个Chrome实例基本占200到500MB内存如果你为了省时间开了多个浏览器实例并发采集16GB内存也架不住几个进程一起跑。而且ChromeDriver的进程在崩溃之后经常残留不手动杀掉第二天你会发现系统莫名其妙卡顿。为了保证程序正常退出我强烈建议你用try/finally或者with语句包住主流程。更简单粗暴的办法是在脚本最后写driver.quit()但注意quit()和close()是两回事。close()只关闭当前标签页窗口还在quit()才是关闭整个浏览器并清理driver进程。排错时如果发现进程残留就检查一下自己是不是写成了close()。Windows下实在杀不掉就任务管理器手工处理或者用taskkill /F /IM chromedriver.exe。7.3 Selenium之外你还可以关注什么浏览器自动化这个方向不只Selenium一个选项。如果你现在还是零基础学Selenium入门完全够用知识点通用。等你熟练了可以考虑去看Playwright和Puppeteer。Playwright的优势是API设计更现代、支持多浏览器、自动等待做得更好安装时还会自动下载适配的浏览器内核省去了WebDriver匹配的烦恼。但Playwright的生态相对年轻中文资料没Selenium多。我的看法是工具永远是服务于业务的先把手头问题解决再去追新。Selenium学了之后转Playwright并不会浪费因为定位、等待、浏览器原理这些都是通用的。浏览器自动化的核心从来不是某个库的API背得有多熟而是你愿不愿意在真实环境里反复调试、把细节抠明白。最后分享一个小技巧如果某天你的脚本在日本网站上跑出乱码或者CSV文件用Excel打开中文全变了多半是编码问题。写文件时养成好习惯encodingutf-8读网页时用driver.page_source后再配合BeautifulSoup解析效果往往比纯Selenium遍历DOM更清晰。学到这里你已经能写一个像样的浏览器自动化脚本了接下来就是多练、多踩坑、多总结没有别的捷径。
返回列表