当前位置: 首页 > news >正文

Playwright自动化测试:滚动到元素的核心方法与实战技巧

1. 项目概述:为什么我们需要“滚动到元素”?

在自动化测试的日常工作中,我们经常会遇到一个看似简单却让人头疼的问题:页面元素明明就在那里,但脚本就是找不到它,直接抛出一个ElementNotVisibleError或者TimeoutError。尤其是在处理那些需要滚动才能加载内容的单页应用(SPA)或者长页面时,这个问题尤为突出。你可能会想,我用了page.wait_for_selector,也设置了超时,为什么还是不行?问题的核心往往在于,Playwright 的定位器(Locator)默认只在当前的“视口”(Viewport)范围内查找元素。如果一个按钮、一个输入框或者一段文本,位于当前屏幕显示区域之外,那么 Playwright 的定位器就会认为这个元素“不可交互”或“不可见”,从而导致操作失败。

这就是我们今天要解决的痛点:如何让 Playwright 自动滚动页面,直到目标元素出现在可视区域内,然后再进行后续操作?这个功能在自动化测试中至关重要,它直接关系到脚本的稳定性和健壮性。想象一下,你要测试一个电商网站的“加入购物车”按钮,这个按钮可能位于商品详情页的很下方。如果脚本不先滚动下去,点击操作注定会失败。手动在脚本里写一堆page.evaluate(‘window.scrollBy(0, 500)’)不仅代码丑陋,而且非常脆弱——页面布局一变,滚动距离就得重调。

因此,掌握“自动滚动到元素出现的位置”这项技术,是构建可靠自动化测试用例的基石。它让你的脚本能像真人用户一样,“看到”页面后再操作,而不是对着一个静态的、局部的页面快照发号施令。接下来,我将结合我多年的实战经验,带你从原理到实践,彻底搞懂并玩转 Playwright 的滚动功能。

2. 核心思路与方案选型:不止一种方法到达目的地

在 Playwright 中,实现“滚动到元素”的目标,主要有三种核心思路,每种都有其适用场景和细微差别。理解这些差异,能帮助你在不同情况下做出最佳选择。

2.1 方案一:利用locator.scroll_into_view_if_needed()

这是最直接、最推荐的内置方法。Locator对象提供了这个方法,其行为与 Web 标准中的Element.scrollIntoView()非常相似。它的逻辑是:检查该元素是否已经在视口内。如果在,则什么都不做;如果不在,则自动滚动页面,直到该元素至少有一部分进入视口。

为什么这是首选?

  1. 语义清晰:方法名直接表达了意图——“如果需要,就滚动到视图中”。
  2. 行为可靠:它模拟了浏览器原生行为,滚动通常比较平滑,且会考虑页面布局(如固定定位的头部导航栏),避免元素被遮挡。
  3. 内置等待:在尝试滚动前,该方法会隐含地等待元素在 DOM 中变得“可操作”(actionable),这在一定程度上整合了等待逻辑。

基本用法:

# 先定位到元素,然后滚动到它 buy_button = page.locator(‘button:has-text(“立即购买”)’) buy_button.scroll_into_view_if_needed() # 现在可以安全地点击了 buy_button.click()

2.2 方案二:使用locator.click()locator.fill()等操作方法的force参数

Playwright 的所有元素操作(如click,fill,hover)都接受一个force参数。当force=True时,Playwright 会跳过可操作性检查(包括元素是否在视口内、是否被其他元素覆盖等),直接执行操作。

什么时候用这个?

  • 元素绝对存在但无法滚动:极少数情况下,页面本身的 CSS 样式(如overflow: hidden)可能阻止了滚动,或者元素在一个无法滚动的容器内。此时scroll_into_view_if_needed可能无效,而force=True可以绕过这个限制。
  • 需要执行操作但不在乎UI状态:例如,你只想触发一个元素的click事件,而不关心用户是否真的能看到它。

警告:滥用force=True是危险的。它违背了测试“模拟真实用户”的初衷。一个真实的用户无法点击一个看不见的按钮。因此,这应该作为最后的手段,而不是常规做法。它可能导致测试通过,但实际用户体验却是失败的。

用法示例:

# 不推荐作为滚动替代品,仅在特殊情况下使用 page.locator(‘#hidden-submit’).click(force=True)

2.3 方案三:组合使用page.evaluate()执行 JavaScript 滚动

这是最灵活、也是最底层的方法。通过page.evaluate(),你可以直接向浏览器上下文注入 JavaScript 代码,执行任何滚动操作。

适用场景:

  1. 精确控制滚动行为:比如,你不想滚动到元素刚好出现,而是想滚动到元素上方 100 像素的位置,以便更好地查看上下文。
  2. 滚动容器而非整个页面:目标元素可能在一个具有独立滚动条(overflow: auto)的div容器内。scroll_into_view_if_needed通常也能处理,但直接操作容器更精确。
  3. 实现复杂滚动逻辑:例如,先缓慢滚动到某个位置,等待一些懒加载的内容出现,再继续滚动。

基础示例:滚动到页面底部

# 滚动到页面最底部 page.evaluate(‘window.scrollTo(0, document.body.scrollHeight)’)

滚动到特定元素的进阶示例:

element = page.locator(‘.target-class’) # 方案A:使用 scrollIntoView,可配置行为 page.evaluate(‘element => element.scrollIntoView({behavior: “smooth”, block: “center”})’, element) # 方案B:计算位置并滚动 bounding_box = element.bounding_box() if bounding_box: page.evaluate(f‘window.scrollTo(0, {bounding_box[“y”] - 100})’) # 滚动到元素上方100像素

方案选型总结:对于 95% 的日常自动化测试场景,方案一(scroll_into_view_if_needed)是你的最佳选择。它简单、可靠、符合用户行为。将方案三作为你的“瑞士军刀”,用于解决那些特殊、复杂的滚动需求。至于方案二,请把它锁在工具箱的最底层,非万不得已不要动用。

3. 实战演练:构建一个健壮的自动滚动测试用例

理论说得再多,不如动手实操。让我们构建一个完整的测试用例,来模拟一个真实场景:测试一个博客网站,需要滚动到页面底部的“评论表单”进行填写和提交。

3.1 环境准备与用例设计

首先,确保你的环境已经就绪。我们需要一个能展示长内容的页面进行测试。这里我们使用一个在线的测试网站https://the-internet.herokuapp.com/infinite_scroll,它模拟了一个无限滚动的页面。

测试目标:

  1. 打开无限滚动演示页面。
  2. 连续多次自动滚动,触发内容加载。
  3. 定位到页面中某个特定段落(比如第20次加载后出现的某个标题)。
  4. 滚动到该元素,并高亮它(通过执行JS修改其样式),以证明我们成功定位并滚动到了正确位置。
  5. 最后,我们回到顶部。

项目结构:

project/ ├── conftest.py # Pytest 配置,定义浏览器Fixture ├── test_scroll_to_element.py # 我们的主测试文件 └── requirements.txt # 依赖文件

requirements.txt内容:

pytest playwright pytest-playwright

conftest.py内容:

import pytest from playwright.sync_api import Page @pytest.fixture(scope=“function”) def page(browser): # 创建一个新的上下文和页面,确保测试隔离 context = browser.new_context(viewport={‘width’: 1920, ‘height’: 1080}) page = context.new_page() yield page context.close() @pytest.fixture(scope=“session”) def browser(playwright): # 启动一个Chromium浏览器实例,可改为 ‘firefox’ 或 ‘webkit’ browser = playwright.chromium.launch(headless=False) # 调试时设为False yield browser browser.close()

3.2 核心测试代码实现

现在,我们来编写主测试文件test_scroll_to_element.py

import re from playwright.sync_api import Page, expect import time def test_auto_scroll_to_element_and_highlight(page: Page): “”” 测试用例:自动滚动到无限滚动页面中的特定元素,并高亮显示。 “”” # 1. 导航到测试页面 page.goto(‘https://the-internet.herokuapp.com/infinite_scroll’) # 等待初始内容加载 page.wait_for_load_state(‘networkidle’) print(“页面加载完成,开始滚动…”) # 2. 模拟多次滚动,触发内容加载 # 我们的目标是找到包含特定数字的标题,比如 ‘Content block #25’ target_text_pattern = r‘Content block #2[0-9]’ # 匹配20-29 target_locator = None max_scroll_attempts = 15 found = False for attempt in range(max_scroll_attempts): print(f“滚动尝试 {attempt + 1}/{max_scroll_attempts}”) # 方法A:使用 evaluate 滚动到页面底部 # page.evaluate(‘window.scrollTo(0, document.body.scrollHeight)’) # 方法B(更优):使用 Playwright 内置的鼠标滚轮模拟,更接近用户行为 page.mouse.wheel(0, 1500) # 向下滚动1500像素 # 等待一小段时间,让新内容可能加载出来 page.wait_for_timeout(1000) # 网络较慢时可适当增加 # 3. 尝试在当前的DOM中查找目标元素 # 使用 locator 和 get_by_text 结合正则表达式 all_elements = page.locator(‘.jscroll-added’).all() # 这个页面新增内容都有这个类 for elem in all_elements: # 获取元素的文本内容进行检查 text_content = elem.text_content() if text_content and re.search(target_text_pattern, text_content): # 找到了!我们使用这个元素的某个子标题作为精确目标 # 假设目标文本在一个 h3 标签里 target_locator = elem.locator(‘h3’).filter(has_text=re.compile(target_text_pattern)) if target_locator.count() > 0: found = True print(f“找到目标元素,文本内容包含: {target_text_pattern}”) break if found: break # 4. 断言我们找到了目标元素 assert found, f“在 {max_scroll_attempts} 次滚动后未找到匹配 ‘{target_text_pattern}’ 的元素” assert target_locator is not None # 5. 核心步骤:滚动到该元素 print(“正在滚动到目标元素…”) target_locator.scroll_into_view_if_needed() # 6. 验证元素确实在视口中(可选但推荐) # 我们可以通过检查元素的 bounding box 的 y 坐标是否在视口高度内来粗略判断 bounding_box = target_locator.bounding_box() viewport_size = page.viewport_size assert bounding_box is not None # 元素顶部应小于视口底部,且元素底部应大于视口顶部(即至少有一部分可见) assert bounding_box[‘y’] < viewport_size[‘height’], “元素似乎没有滚动到视口内(顶部在下方)” assert bounding_box[‘y’] + bounding_box[‘height’] > 0, “元素似乎没有滚动到视口内(底部在上方)” # 7. 高亮元素(通过注入JS)以提供视觉反馈,便于调试 page.evaluate(”“” element => { element.style.border = ‘3px solid red’; element.style.backgroundColor = ‘yellow’; // 滚动到元素中心点,使其更突出 element.scrollIntoView({behavior: ‘smooth’, block: ‘center’}); } “”“, target_locator.element_handle()) print(“元素已高亮显示。”) page.wait_for_timeout(2000) # 暂停2秒,方便人工观察 # 8. 可选:滚动回顶部 print(“滚动回页面顶部。”) page.evaluate(‘window.scrollTo({top: 0, behavior: “smooth”})’) page.wait_for_timeout(1000) print(“测试完成!”)

3.3 代码逐行解析与避坑指南

  1. 页面加载与等待(page.wait_for_load_state(‘networkidle’)):

    • 为什么用networkidle对于单页应用或动态加载页面,domcontentloaded可能过早,页面主体HTML加载完但JS资源或异步数据还没来。networkidle等待网络活动基本停止,更适合现代网页。但要注意,有些页面会有长期活跃的连接(如WebSocket),可能导致一直等不到networkidle。此时可以结合page.wait_for_selector等待一个关键元素出现。
  2. 滚动触发加载(page.mouse.wheel(0, 1500)):

    • 为什么用mouse.wheel而不是evaluate直接设置scrollTopmouse.wheel模拟了真实的用户鼠标滚轮滚动,会触发更多的浏览器事件(如scroll,wheel),而这些事件正是许多无限滚动库(如jscroll)监听用来加载更多内容的。直接设置scrollTop可能不会触发这些事件,导致内容无法加载。这是一个非常重要的实战技巧!
  3. 动态查找元素(page.locator(‘.jscroll-added’).all()):

    • 我们不是用静态选择器直接等一个可能还不存在的元素,而是先找到所有已加载的内容块容器,然后遍历检查其文本。这是因为在无限滚动中,你无法预知目标元素在第几批加载中。这种“先获取集合,后过滤”的模式在动态内容查找中非常有用。
    • 注意locator.all()的使用:它会返回一个当前匹配到的所有元素的列表快照。在遍历这个列表时,如果页面DOM正在更新(比如又滚动了),这个快照可能会过时。因此,我们在一个相对稳定的时机(一次滚动和等待之后)进行查找和遍历。
  4. 核心滚动方法(target_locator.scroll_into_view_if_needed()):

    • 这是本教程的核心。一行代码解决问题。Playwright 会处理好一切:等待元素稳定,计算位置,执行滚动。
    • 它和page.wait_for_selector(selector, state=“visible”)有什么区别?wait_for_selector(state=“visible”)会等待元素在视口中可见,但它不会主动滚动。如果元素在视口外,它会一直等待直到超时。所以,通常你需要先滚动,或者结合使用。
  5. 验证滚动结果

    • 我们通过bounding_box()获取元素的位置和大小,然后与视口尺寸对比。这是一个双重验证,确保我们的滚动操作确实生效了,增加了测试的可靠性。
  6. 高亮与调试(page.evaluate(…)):

    • 在自动化测试中,尤其是运行在无头模式时,我们看不到页面发生了什么。通过注入JS临时修改元素样式(比如加个红框),并在关键步骤后wait_for_timeout,我们可以在调试时(headless=False)清晰地看到脚本的执行路径和定位结果。这是调试Playwright脚本的黄金技巧。
  7. 关于page.wait_for_timeout

    • 在自动化测试中,硬编码的sleep(如time.sleep(2)page.wait_for_timeout(2000))通常被认为是“反模式”,因为它降低了测试速度且不可靠。应该优先使用wait_for_selector,wait_for_function等基于条件的等待。
    • 那么这里为什么用了?这里的wait_for_timeout有两个目的:一是给动态加载内容一点网络请求时间(在复杂网络中,networkidle可能不够精确);二是为了人工观察调试。在最终稳定的测试脚本中,第一个等待应该被更精确的等待替代(例如等待某个加载指示器消失),第二个等待(高亮后)通常可以移除。

4. 高级技巧与场景扩展

掌握了基础方法后,我们来看看一些更复杂但常见的场景及其解决方案。

4.1 场景一:处理固定定位的导航栏/页脚

很多网站有固定在顶部或底部的导航栏。当你使用scroll_into_view_if_needed()时,浏览器默认的block参数可能是“start”,这可能导致元素滚动到视口顶部时,被固定导航栏遮挡。

解决方案:我们可以结合page.evaluate()和原生的scrollIntoView选项,进行更精细的控制。

element = page.locator(‘#submit-button’) # 滚动到元素,并让元素位于视口中央,避免被固定栏遮挡 page.evaluate(‘’‘ element => element.scrollIntoView({ behavior: ‘smooth’, block: ‘center’, // ‘start’, ‘center’, ‘end’, ‘nearest’ inline: ‘center’ }) ‘’‘, element.element_handle())

参数解释:

  • behavior:“auto”“smooth”“smooth”为平滑滚动。
  • block: 垂直方向的对齐方式。“center”将元素对齐到视口垂直中央,通常能完美避开顶部和底部的固定栏。
  • inline: 水平方向的对齐方式。

4.2 场景二:滚动到可滚动容器内的元素

有时,目标元素不在主文档流中,而是在一个设置了overflow: autooverflow: scrolldiv容器内。

解决方案:你需要先定位到那个可滚动的容器,然后针对该容器执行滚动操作。

# 假设容器有一个ID ‘scrollable-container’ scrollable_container = page.locator(‘#scrollable-container’) target_inside_container = scrollable_container.locator(‘.target-item’) # 方法A:使用 evaluate 操作容器的 scrollTop bounding_box = target_inside_container.bounding_box() container_box = scrollable_container.bounding_box() if bounding_box and container_box: # 计算目标相对于容器的位置,并设置容器的滚动位置 scroll_top = bounding_box[‘y’] - container_box[‘y’] page.evaluate(‘’‘(container, scrollTop) => container.scrollTop = scrollTop‘’‘, scrollable_container.element_handle(), scroll_top) # 方法B(更简单):如果容器支持,也可以直接对目标元素使用 scroll_into_view。 # 因为 scroll_into_view 会查找最近的滚动祖先元素。 target_inside_container.scroll_into_view_if_needed() # 通常方法B就足够了,它更符合标准。

4.3 场景三:实现“滚动直到某个条件满足”

这是一种更高级的模式,比如滚动直到某个特定的元素出现,或者某个文本显示出来。

def scroll_until_condition(page: Page, condition_locator, max_scrolls=20): “”” 持续滚动页面,直到指定的定位器匹配到元素。 Args: page: Playwright page 对象 condition_locator: 一个 Playwright Locator 对象,用于检查条件 max_scrolls: 最大滚动次数,防止无限循环 Returns: bool: 如果找到元素返回True,否则返回False “”” for _ in range(max_scrolls): # 检查条件是否满足 if condition_locator.count() > 0: return True # 条件不满足,向下滚动一屏 page.mouse.wheel(0, page.viewport_size[‘height’] * 0.8) # 滚动80%的视口高度 page.wait_for_timeout(500) # 等待可能的内容加载 return False # 使用示例:滚动直到“加载完毕”的提示出现 loading_finished = page.locator(‘text=没有更多内容了’) if scroll_until_condition(page, loading_finished): print(“已滚动到页面底部,所有内容加载完毕。”) else: print(“已达到最大滚动次数,可能还有内容未加载。”)

5. 常见问题排查与实战心得

即使掌握了上面的方法,在实际项目中你还是会遇到各种稀奇古怪的问题。下面是我总结的一些常见“坑”和解决思路。

5.1 问题:scroll_into_view_if_needed后元素仍然“不可交互”

现象:滚动后,立即执行click()还是失败了,报错Element is not visibleElement is detached from DOM

排查思路:

  1. 检查是否滚动成功:在滚动语句后添加一个高亮元素的调试语句(如之前所述),用headless=False模式运行,亲眼看看页面是否真的滚动到了正确位置。
  2. 检查元素状态:滚动后,元素可能还在进行动画(如淡入、滑动)。此时它虽然在视口内,但CSS属性(如opacity: 0,display: none)可能使其不可交互。
    • 解决方案:使用page.wait_for_selector(selector, state=“attached”)或更精确的state=“visible”。但注意,“visible”要求元素在视口内且没有隐藏。更好的方法是等待某个特定的CSS类或属性出现。
    element.scroll_into_view_if_needed() # 等待元素具有一个表示“可点击”的类,或者 opacity 变为 1 page.wait_for_function(‘’‘ selector => { const el = document.querySelector(selector); return el && el.style.opacity === ‘1’; } ‘’‘, ‘.my-button’) element.click()
  3. 检查元素是否被覆盖:可能有另一个透明或看不见的元素(如弹窗的遮罩层、广告)覆盖在了目标元素之上。
    • 解决方案:使用 Playwright 的调试工具page.pause(),或者在脚本中执行page.screenshot({fullPage: true})并保存图片,查看截图。也可以使用page.evaluate检查元素的pointer-events样式。

5.2 问题:在iframe内的元素无法滚动

现象:你的目标元素位于一个iframe中,直接对主页面page进行滚动操作无效。

解决方案:你必须先切换到iframe的上下文。

# 通过名称、URL或选择器定位 iframe frame = page.frame(name=‘my-frame’) # 或 page.frame(url=‘...’) 或 page.frame_locator(‘iframe’).content_frame() # 现在在 frame 上下文中操作 element_in_frame = frame.locator(‘button’) element_in_frame.scroll_into_view_if_needed()

5.3 问题:页面使用了复杂的视差滚动或滚动监听库

现象:使用mouse.wheelscrollIntoView后,页面内容没有按预期更新,或者滚动行为很奇怪。

解决方案:有些JS库(如fullPage.js,locomotive-scroll)接管了原生的滚动事件。这时,模拟原生滚动可能无效。

  1. 尝试触发库的API:如果该库暴露了JavaScript API,可以通过page.evaluate调用它。这需要你研究该库的文档。
  2. 模拟更真实的交互:尝试用page.locator(‘.next-section-button’).click()来触发库的翻页,而不是直接滚动。
  3. 终极方案:如果自动化测试对此类页面不是必须的,可以考虑与开发沟通,在测试环境中禁用这些复杂的滚动效果,或者为关键元素添加更容易定位和交互的测试ID。

5.4 实战心得:让滚动测试更稳定

  1. 优先使用scroll_into_view_if_needed():这是Playwright团队为你封装好的最佳实践,在绝大多数情况下都工作良好。不要自己重复造轮子。
  2. 结合智能等待:滚动前后,配合使用page.wait_for_load_state(),page.wait_for_selector(),page.wait_for_function()等,而不是简单的time.sleep
  3. 增加重试和超时:对于网络不稳定或加载慢的环境,在滚动和查找元素时,使用playwright.sync_api中的expect(locator).to_be_visible(timeout=10000)并设置合理的超时时间。
  4. 调试时多用截图和高亮page.screenshot(path=‘debug.png’)和注入JS高亮元素,是定位问题最快的方式。
  5. 视口大小很重要:在browser.new_context中设置一个固定的、合理的视口大小(如 1920x1080)。不同的视口大小可能导致布局变化,从而影响元素位置和滚动行为。

滚动,这个用户在浏览网页时最自然不过的动作,在自动化测试中却需要精心处理。通过深入理解scroll_into_view_if_needed的工作原理,并熟练掌握其与各种等待、定位方法的组合拳,你就能写出既能精准定位、又稳定可靠的自动化测试脚本。记住,好的自动化测试,应该像一位有耐心的用户,在正确的时机,与正确的元素进行交互。而自动滚动,正是确保这个“正确时机”的关键一步。

http://www.jsqmd.com/news/1050871/

相关文章:

  • 上海家电维修 / 家电清洗|本地避坑指南,满分五星平台 | 2026首选一步到家 - 一步到家
  • 咸宁职业技术学院王牌专业主要学什么课程?未来的培养方向和就业领域是什么? - 寻茫精选
  • 3步解锁QQ音乐加密格式:让你的音乐在任何设备上自由播放
  • 警惕AI领域虚假模型名:GPT-5.5并不存在
  • MAKER系统:用原子化流程实现LLM百万步零错误执行
  • Deepseek本地部署实战:MoE架构与SiLU激活函数的工程落地
  • 广东农工商职业技术学院的王牌专业有哪些?哪些专业性价比最高、最值得报? - 寻茫精选
  • 2026年亲测三家京东E卡回收正规平台综合评分:折扣差异解析与安全变现操作全流程 - 鼎鼎收礼品卡回收
  • 2026年铸铝门品牌实力盘点:十家值得关注的厂家与选购参考 - 门业观察
  • 2026抖店拍单工具闭眼入:合规为王,一站式代发首选【抖掌柜】深度实测推荐 - 抖掌柜
  • MATLAB雷达CFAR检测仿真包:含多种算法实现、可视化结果与完整说明
  • 一个目录一个 Agent,Vercel Eve 的这套架构设计太舒服了!
  • 淮南师范学院的王牌专业有没有实验班 / 卓越班 / 本硕连读班?怎么报考? - 寻茫精选
  • 2026寄大件怎么省钱?个人寄件全流程+5折技巧 - 快递物流资讯
  • Agent Skills工作流:AI工程化落地的核心方法论
  • Claude Opus 4.6实测:前端工程化能力与认知写作方法论深度解析
  • AI协同开发:从代码生成到全流程智能化的实践
  • 安徽水利水电职业技术学院的王牌专业有没有校企合作项目?实习和实训机会多不多? - 寻茫精选
  • Maya glTF插件实战:从专业3D创作到Web 3D展示的完整工作流
  • 零知识证明-FFS身份鉴别方案
  • 2026年6月评价高的锌铝镁抗震支架/综合抗震支架厂家推荐,库存充足快速响应工程急单 - 品牌鉴赏师
  • 咸宁职业技术学院的王牌专业有没有实验班 / 卓越班 / 本硕连读班?怎么报考? - 寻茫精选
  • 百度网盘提速教程?聊聊多线程配置与第三方客户端调度调优
  • 对口升学本科靠谱吗?推荐合肥理工学校! - 教育为先
  • 解放双手!D3KeyHelper暗黑3智能连点器完全指南:自动化战斗助你轻松冲层
  • 广东农工商职业技术学院王牌专业的师资强不强?有没有院士、国家级教学名师? - 寻茫精选
  • 2026年安徽省初三中考成绩差考不上高中适合上什么学校??——推荐合肥理工学校! - 教育为先
  • OpenSSH权限提升漏洞CVE-2021-41617修复实战与安全加固指南
  • 元学习与合成任务:破解小数据黑盒优化难题
  • 淮南师范学院王牌专业近三年的录取分数线大概是多少?位次要求是什么? - 寻茫精选