如何 select 从下拉列表中选择一个选项 select
How to select an option from dropdown select
我可以单击 select 或者但我的问题是如何 select 从下拉列表中选择一个选项?
await page.click('#telCountryInput > option:nth-child(4)')
单击选项使用 CSS select或不起作用。
例如,select 来自如下列表的国家/地区代码:
事实证明这比我想象的要容易,因为下拉列表不是原生的 HTML 选择和选项组合,因此,我实际上可以使用下面的代码来 select 我想要的目标。
await page.click('#telCountryInput')
await page.click('#select2-telCountryInput-results > li:nth-child(4)')
对于本机选择框,我的解决方案是在页面本身上执行一些 JS:
await page.evaluate(() => {
document.querySelector('select option:nth-child(2)').selected = true;
})
Puppeteer v0.13.0 有 page.select() 方法,正是这样做的。你只需要把它的值赋给 select。因此,假设您的 <select>
:
中有一个 <option value="my-value">
await page.select('#telCountryInput', 'my-value')
对于下拉组件,我觉得应该考虑2种情况:
- 原生HTML
select
元素
- JS编写的组件,由按钮和选项列表组成,以bootstrap dropdown为例
第二种情况,我觉得click
可以解决问题
对于第一种情况,我找到了两种方法:
page.select
elementHandle.type
(通知于 2018 年 4 月 27 日更新)
page.select
是v0.12.0新增的功能。
例如你有一个 select 元素:
<label>Choose One:
<select name="choose1">
<option value="val1">Value 1</option>
<option value="val2">Value 2</option>
<option value="val3">Value 3</option>
</select>
</label>
您有两种方法 select 第二个选项 'Value 2'。
// use page.select
await page.select('select[name="choose1"]', 'val2');
// use elementHandle.type
const selectElem = await page.$('select[name="choose1"]');
await selectElem.type('Value 2');
通常elementHandle.type
用于在输入文本框中输入文本,但由于它
Focuses the element, and then sends a keydown, keypress/input, and keyup event for each character in the text.
select
HTML 元素有输入事件,所以这个方法有效。
我个人认为 elementHandle.type
更好,因为它不需要知道选项值属性,只需要 label/name 人可以看到的内容。
2018 年 4 月 27 日更新
我之前只在 Mac OSX 上使用过 elementHandle.type
。最近,我的同事报告了一个与此相关的错误。他正在使用 Linux/Win。另外,我们都在使用 puppeteer v1.3.0.
经过反复试验,我们发现这个elementHandle.type
可以给<select>
元素赋值,但是不会触发元素的change
事件。
所以我不再建议在 <select>
.
上使用 elementHandle.type
最后,我们按照this comment手动派发更改事件。就像:
// use manually trigger change event
await page.evaluate((optionElem, selectElem) => {
optionElem.selected = true;
const event = new Event('change', {bubbles: true});
selectElem.dispatchEvent(event);
}, optionElem, selectElem);
Page.select 并不总是适合我,page.type 也不可靠。今天我想到了:
await page.evaluate((css, text) => {
let sel = document.querySelector(css)
for(let option of [...document.querySelectorAll(css + ' option')]){
if(text === option.text){
sel.value = option.value
}
}
}, '#telCountryInput', 'my-value')
我是从有人询问如何 select 下拉列表中的第一个选项的消息来到这里的。这就是我刚刚想出的方法:
await page.click('.select-input');
await page.waitFor(300);
await page.keyboard.press('ArrowDown');
await page.keyboard.press('Enter');
以上代码先select进行相关输入。然后我等待,因为我的加载速度不够快。然后我使用键盘按键向下导航到第一个选项。
在pyppeteer中,当select通过文本时,我可以这样做:
带有 fastapi 服务器的示例页面
"""
filename: example.py
Note:
When run this example, recommend create a virtualenv by tools, like pipenv. And install dependencies.
Install dependencies:
```shell
pipenv install fastapi uvicorn python-multipart
```
Run server:
```shell
pipenv run python example.py
# pipenv run uvicorn --reload example:app
```
"""
import logging
import uvicorn
from fastapi import FastAPI, Form
from pydantic import BaseModel
from starlette.responses import HTMLResponse
HTML = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>example</title>
</head>
<body>
<form id="add" method="post" action="/add">
<label for="title"></label>
<input id="title" name="title">
<label for="tag">Tag</label>
<select id="tag" name="tag">
<option>java</option>
<option>python</option>
<option>kotlin</option>
</select>
</form>
<button id="submit" onclick="submitHandle()">Submit</button>
<script>
const submitHandle = () => {
document.getElementById('add').submit()
}
</script>
</body>
</html>
"""
console_handler = logging.StreamHandler()
console_handler.setLevel(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
logger.addHandler(console_handler)
app = FastAPI()
class PostModel(BaseModel):
title: str
tag: str
@app.get('/posts')
def posts():
return HTMLResponse(content=HTML)
@app.post('/add')
def detail(title: str = Form(...), tag: str = Form(...)) -> PostModel:
post = PostModel(title=title, tag=tag)
logger.info(f'Add a blog. Detail: "{post.json()}"')
return post
if __name__ == '__main__':
uvicorn.run(app) # noqa
示例python蜘蛛代码
import asyncio
import logging
from pyppeteer import launch
console_handler = logging.StreamHandler()
console_handler.setLevel(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
logger.addHandler(console_handler)
async def post_spider():
"""Open page and add value in form, then submit."""
browser = await launch(headless=False)
try:
page = await browser.newPage()
await page.goto('http://127.0.0.1:8000/posts')
expect_value = 'python'
title_element = await page.querySelector('#title')
await title_element.type('I love python, and python love me.')
# # If it does not work.
# await page.select('#tag', expect_value)
tag_element = await page.querySelector('#tag')
# #Extract all options value
# options_text = await page.querySelectorAllEval(
# '#tag > option',
# 'options => options.map(option => option.value)'
# )
options_text = await tag_element.querySelectorAllEval(
'option',
'options => options.map(option => option.value)'
)
# # Check expect value in options
if expect_value in options_text:
# # Use JavaScript set select element value that in options.
await page.querySelectorEval('#tag', f'element => element.value = "{expect_value}"')
tag_selected_value = await page.querySelectorEval('#tag', 'element => element.value')
logger.info(f'Selected tag element value is "{tag_selected_value}"')
submit_ele = await page.querySelector('#submit')
await submit_ele.click()
finally:
await browser.close()
if __name__ == '__main__':
asyncio.run(post_spider())
注:
您可以使用 evaluate a JavaScript 将其中一个选项文本设置为其 select,如果文本不在选项中,则 select 的值不会改变。
这是python的例子,它的用法和puppeteer差不多,记录在这里帮助更多的人。
我的环境:
- Python: 3.10
- pyppeteer: 0.2.6
@花岗
你的想法很棒,我扩展了value属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>example</title>
</head>
<body>
<form id="add" method="post" action="/detail">
<label for="title"></label>
<input id="title" name="title">
<label for="tag">Tag</label>
<select id="tag">
<option value="1">java</option>
<option value="2">python</option>
<option value="3">kotlin</option>
</select>
</form>
<button id="submit" onclick="submitHandle()">Submit</button>
<script>
const submitHandle = () => {
document.getElementById('add').submit()
}
</script>
</body>
</html>
expect_value = '3'
select_tag = '#tag'
# extract all options value
option_texts = []
for option_ele in await page.querySelectorAll(f'{select_tag} > option'):
text = await page.evaluate('(element) => ({"value":element.value,"text":element.textContent})', option_ele)
option_texts.append(text)
value = ''
for v in option_texts:
if v.get('text') == expect_value:
value = v.get('value')
break
await page.select(select_tag, value)
我合并了 2 个答案并将它们包装在一个函数中:
async function selectByText(page, selector, value) {
return await page.evaluate(
(css, text) => {
let sel = document.querySelector(css)
for (let option of [...document.querySelectorAll(css + ' option')]) {
if (text === option.text) {
sel.value = option.value
}
}
const event = new Event('change', { bubbles: true })
sel.dispatchEvent(event)
},
selector,
value,
)
}
我可以单击 select 或者但我的问题是如何 select 从下拉列表中选择一个选项?
await page.click('#telCountryInput > option:nth-child(4)')
单击选项使用 CSS select或不起作用。
例如,select 来自如下列表的国家/地区代码:
事实证明这比我想象的要容易,因为下拉列表不是原生的 HTML 选择和选项组合,因此,我实际上可以使用下面的代码来 select 我想要的目标。
await page.click('#telCountryInput')
await page.click('#select2-telCountryInput-results > li:nth-child(4)')
对于本机选择框,我的解决方案是在页面本身上执行一些 JS:
await page.evaluate(() => {
document.querySelector('select option:nth-child(2)').selected = true;
})
Puppeteer v0.13.0 有 page.select() 方法,正是这样做的。你只需要把它的值赋给 select。因此,假设您的 <select>
:
<option value="my-value">
await page.select('#telCountryInput', 'my-value')
对于下拉组件,我觉得应该考虑2种情况:
- 原生HTML
select
元素 - JS编写的组件,由按钮和选项列表组成,以bootstrap dropdown为例
第二种情况,我觉得click
可以解决问题
对于第一种情况,我找到了两种方法:
page.select
elementHandle.type
(通知于 2018 年 4 月 27 日更新)
page.select
是v0.12.0新增的功能。
例如你有一个 select 元素:
<label>Choose One:
<select name="choose1">
<option value="val1">Value 1</option>
<option value="val2">Value 2</option>
<option value="val3">Value 3</option>
</select>
</label>
您有两种方法 select 第二个选项 'Value 2'。
// use page.select
await page.select('select[name="choose1"]', 'val2');
// use elementHandle.type
const selectElem = await page.$('select[name="choose1"]');
await selectElem.type('Value 2');
通常elementHandle.type
用于在输入文本框中输入文本,但由于它
Focuses the element, and then sends a keydown, keypress/input, and keyup event for each character in the text.
select
HTML 元素有输入事件,所以这个方法有效。
我个人认为 elementHandle.type
更好,因为它不需要知道选项值属性,只需要 label/name 人可以看到的内容。
2018 年 4 月 27 日更新
我之前只在 Mac OSX 上使用过 elementHandle.type
。最近,我的同事报告了一个与此相关的错误。他正在使用 Linux/Win。另外,我们都在使用 puppeteer v1.3.0.
经过反复试验,我们发现这个elementHandle.type
可以给<select>
元素赋值,但是不会触发元素的change
事件。
所以我不再建议在 <select>
.
elementHandle.type
最后,我们按照this comment手动派发更改事件。就像:
// use manually trigger change event
await page.evaluate((optionElem, selectElem) => {
optionElem.selected = true;
const event = new Event('change', {bubbles: true});
selectElem.dispatchEvent(event);
}, optionElem, selectElem);
Page.select 并不总是适合我,page.type 也不可靠。今天我想到了:
await page.evaluate((css, text) => {
let sel = document.querySelector(css)
for(let option of [...document.querySelectorAll(css + ' option')]){
if(text === option.text){
sel.value = option.value
}
}
}, '#telCountryInput', 'my-value')
我是从有人询问如何 select 下拉列表中的第一个选项的消息来到这里的。这就是我刚刚想出的方法:
await page.click('.select-input');
await page.waitFor(300);
await page.keyboard.press('ArrowDown');
await page.keyboard.press('Enter');
以上代码先select进行相关输入。然后我等待,因为我的加载速度不够快。然后我使用键盘按键向下导航到第一个选项。
在pyppeteer中,当select通过文本时,我可以这样做:
带有 fastapi 服务器的示例页面
"""
filename: example.py
Note:
When run this example, recommend create a virtualenv by tools, like pipenv. And install dependencies.
Install dependencies:
```shell
pipenv install fastapi uvicorn python-multipart
```
Run server:
```shell
pipenv run python example.py
# pipenv run uvicorn --reload example:app
```
"""
import logging
import uvicorn
from fastapi import FastAPI, Form
from pydantic import BaseModel
from starlette.responses import HTMLResponse
HTML = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>example</title>
</head>
<body>
<form id="add" method="post" action="/add">
<label for="title"></label>
<input id="title" name="title">
<label for="tag">Tag</label>
<select id="tag" name="tag">
<option>java</option>
<option>python</option>
<option>kotlin</option>
</select>
</form>
<button id="submit" onclick="submitHandle()">Submit</button>
<script>
const submitHandle = () => {
document.getElementById('add').submit()
}
</script>
</body>
</html>
"""
console_handler = logging.StreamHandler()
console_handler.setLevel(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
logger.addHandler(console_handler)
app = FastAPI()
class PostModel(BaseModel):
title: str
tag: str
@app.get('/posts')
def posts():
return HTMLResponse(content=HTML)
@app.post('/add')
def detail(title: str = Form(...), tag: str = Form(...)) -> PostModel:
post = PostModel(title=title, tag=tag)
logger.info(f'Add a blog. Detail: "{post.json()}"')
return post
if __name__ == '__main__':
uvicorn.run(app) # noqa
示例python蜘蛛代码
import asyncio
import logging
from pyppeteer import launch
console_handler = logging.StreamHandler()
console_handler.setLevel(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
logger.addHandler(console_handler)
async def post_spider():
"""Open page and add value in form, then submit."""
browser = await launch(headless=False)
try:
page = await browser.newPage()
await page.goto('http://127.0.0.1:8000/posts')
expect_value = 'python'
title_element = await page.querySelector('#title')
await title_element.type('I love python, and python love me.')
# # If it does not work.
# await page.select('#tag', expect_value)
tag_element = await page.querySelector('#tag')
# #Extract all options value
# options_text = await page.querySelectorAllEval(
# '#tag > option',
# 'options => options.map(option => option.value)'
# )
options_text = await tag_element.querySelectorAllEval(
'option',
'options => options.map(option => option.value)'
)
# # Check expect value in options
if expect_value in options_text:
# # Use JavaScript set select element value that in options.
await page.querySelectorEval('#tag', f'element => element.value = "{expect_value}"')
tag_selected_value = await page.querySelectorEval('#tag', 'element => element.value')
logger.info(f'Selected tag element value is "{tag_selected_value}"')
submit_ele = await page.querySelector('#submit')
await submit_ele.click()
finally:
await browser.close()
if __name__ == '__main__':
asyncio.run(post_spider())
注:
您可以使用 evaluate a JavaScript 将其中一个选项文本设置为其 select,如果文本不在选项中,则 select 的值不会改变。
这是python的例子,它的用法和puppeteer差不多,记录在这里帮助更多的人。
我的环境:
- Python: 3.10
- pyppeteer: 0.2.6
@花岗
你的想法很棒,我扩展了value属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>example</title>
</head>
<body>
<form id="add" method="post" action="/detail">
<label for="title"></label>
<input id="title" name="title">
<label for="tag">Tag</label>
<select id="tag">
<option value="1">java</option>
<option value="2">python</option>
<option value="3">kotlin</option>
</select>
</form>
<button id="submit" onclick="submitHandle()">Submit</button>
<script>
const submitHandle = () => {
document.getElementById('add').submit()
}
</script>
</body>
</html>
expect_value = '3'
select_tag = '#tag'
# extract all options value
option_texts = []
for option_ele in await page.querySelectorAll(f'{select_tag} > option'):
text = await page.evaluate('(element) => ({"value":element.value,"text":element.textContent})', option_ele)
option_texts.append(text)
value = ''
for v in option_texts:
if v.get('text') == expect_value:
value = v.get('value')
break
await page.select(select_tag, value)
我合并了 2 个答案并将它们包装在一个函数中:
async function selectByText(page, selector, value) {
return await page.evaluate(
(css, text) => {
let sel = document.querySelector(css)
for (let option of [...document.querySelectorAll(css + ' option')]) {
if (text === option.text) {
sel.value = option.value
}
}
const event = new Event('change', { bubbles: true })
sel.dispatchEvent(event)
},
selector,
value,
)
}