> For the complete documentation index, see [llms.txt](https://developers.oxylabs.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.oxylabs.io/products/cn/web-scraper-api/features/js-rendering-and-browser-control.md).

# JS 渲染与浏览器控制

学习如何使用 render 参数在网页爬虫API中定义浏览器指令，从而抓取复杂的动态页面。

## JavaScript 渲染

如果你要抓取的页面使用 JavaScript 将数据加载到 DOM 中，请添加 `render` 参数到请求中。然后在返回结果前，页面会完全渲染，结果有以下两种格式之一：

<table><thead><tr><th width="162.625">render 值</th><th>你会得到</th></tr></thead><tbody><tr><td><code>html</code></td><td>完整渲染页面的原始 HTML</td></tr><tr><td><code>png</code></td><td>渲染页面的 Base64 编码截图（PNG）</td></tr></tbody></table>

{% hint style="info" %}
如果你想抓取图片并下载，请参考 [**此部分**](/products/cn/web-scraper-api/features/result-processing-and-storage/output-types/download-images.md)**.**
{% endhint %}

### 请求示例

{% tabs %}
{% tab title="cURL" %}

```shell
curl --user "USERNAME:PASSWORD" \
'https://realtime.oxylabs.io/v1/queries' \
-H "Content-Type: application/json" \\
-d '{"source": "universal", "url": "https://www.example.com", "render": "html"}'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
from pprint import pprint

# Structure payload.
payload = {
    'source': 'universal',
    'url': 'https://www.example.com',
    'render': 'html',
}

# Get response.
response = requests.request(
    'POST',
    'https://realtime.oxylabs.io/v1/queries',
    auth=('USERNAME', 'PASSWORD'),
    json=payload,
)

# Instead of response with job status and results url, this will return the
# JSON response with the result.
pprint(response.json())
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$params = [
    'source' => 'universal',
    'url' => 'https://www.example.com',
    'render' => 'html',
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://realtime.oxylabs.io/v1/queries");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_USERPWD, "USERNAME" . ":" . "PASSWORD");

$headers = array();
$headers[] = "Content-Type: application/json";
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);
echo $result;

if (curl_errno($ch)) {
    echo 'Error:' . curl_error($ch);
}
curl_close($ch);
```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace OxyApi
{
    class Program
    {
        static async Task Main()
        {
            const string Username = "YOUR_USERNAME";
            const string Password = "YOUR_PASSWORD";

            var parameters = new Dictionary<string, string>()
            {
                { "source", "universal" },
                { "url", "https://www.example.com" },
                { "render" : "html" },
            };


            var client = new HttpClient();

            Uri baseUri = new Uri("https://realtime.oxylabs.io");
            client.BaseAddress = baseUri;

            var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/v1/queries");
            requestMessage.Content = JsonContent.Create(parameters);

            var authenticationString = $"{Username}:{Password}";
            var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
            requestMessage.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);

            var response = await client.SendAsync(requestMessage);
            var contents = await response.Content.ReadAsStringAsync();

            Console.WriteLine(contents);
        }
    }
}
```

{% endtab %}

{% tab title="Golang" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	const Username = "YOUR_USERNAME"
	const Password = "YOUR_PASSWORD"

	payload := map[string]string{
		"source": "universal",
		"url": "https://www.example.com",
        	"render": "html",
	}

	jsonValue, _ := json.Marshal(payload)

	client := &http.Client{}
	request, _ := http.NewRequest("POST",
		"https://realtime.oxylabs.io/v1/queries",
		bytes.NewBuffer(jsonValue),
	)

	request.SetBasicAuth(Username, Password)
	response, _ := client.Do(request)

	responseText, _ := ioutil.ReadAll(response.Body)
	fmt.Println(string(responseText))
}
```

{% endtab %}

{% tab title="Java" %}

```java
package org.example;

import okhttp3.*;
import org.json.JSONObject;

public class Main implements Runnable {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    public static final String USERNAME = "YOUR_USERNAME";
    public static final String PASSWORD = "YOUR_PASSWORD";

    public void run() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("source", "universal");
        jsonObject.put("url", "https://www.example.com");
        jsonObject.put("render", "html");

        Authenticator authenticator = (route, response) -> {
            String credential = Credentials.basic(USERNAME, PASSWORD);

            return response
                    .request()
                    .newBuilder()
                    .header(AUTHORIZATION_HEADER, credential)
                    .build();
        };

        var client = new OkHttpClient.Builder()
                .authenticator(authenticator)
                .build();

        var mediaType = MediaType.parse("application/json; charset=utf-8");
        var body = RequestBody.create(jsonObject.toString(), mediaType);
        var request = new Request.Builder()
                .url("https://realtime.oxylabs.io/v1/queries")
                .post(body)
                .build();

        try (var response = client.newCall(request).execute()) {
            assert response.body() != null;
            System.out.println(response.body().string());
        } catch (Exception exception) {
            System.out.println("Error: " + exception.getMessage());
        }

        System.exit(0);
    }

    public static void main(String[] args) {
        new Thread(new Main()).start();
    }
}
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
import fetch from 'node-fetch';

const username = 'YOUR_USERNAME';
const password = 'YOUR_PASSWORD';
const body = {
  'source': 'universal',
  'url': 'https://www.example.com',
  'render': 'html'
};
const response = await fetch('https://realtime.oxylabs.io/v1/queries', {
  method: 'post',
  body: JSON.stringify(body),
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'),
  }
});

console.log(await response.json());
```

{% endtab %}

{% tab title="JSON" %}

```json
{
    "source": "universal", 
    "url": "https://www.example.com", 
    "render": "html"
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
JavaScript 渲染抓取页面需要更多时间。如果你使用 Realtime 或 Proxy Endpoint 集成方式，请在客户端将超时时间设置为 180 秒。
{% endhint %}

{% hint style="warning" %}
为确保流量消耗最低，我们的系统在页面渲染期间不会加载不必要的资源。
{% endhint %}

## 在特定页面上强制渲染

为了成功抓取，某些特定域名的页面类型由于其动态内容需要渲染。对于这些页面，我们的系统 **会自动强制渲染** 即使你没有设置 `render` 参数，因此你总能从这些通常较难处理的页面获得准确、可靠的数据。

{% hint style="warning" %}
请注意，渲染任务相比未渲染任务会消耗更多流量。
{% endhint %}

受影响的目标完整列表维护在下面的文件中。我们希望用户在抓取以下页面时对此有充分了解：

{% file src="/files/115d4431d76710fc6ad64799a0caaaabd39b3cf1" %}

如果你想禁用渲染，可以在请求中添加以下参数：

```
"render": ""
```

## 浏览器指令

当页面需要交互时，例如点击按钮、输入搜索词或滚动加载更多项目，你可以定义自己的 `browser_instructions` 在页面渲染时运行。

{% hint style="success" %}
构建浏览器指令最简单的方法是使用 [网页爬虫API Playground](https://dashboard.oxylabs.io/?route=/api-playground)中的 AI 可视化构建器。有关详细信息请阅读 [此处](/products/cn/web-scraper-api/web-scraper-api-playground/oxycopilot.md#browser-instruction-builder).
{% endhint %}

### 快速开始

首先，浏览器指令需要 `render` 参数，既可以是 `html` 或者 `png`，并作为列表提供在 `browser_instructions` 字段中。列表中的每个项都会按顺序执行。

假设你想在网站上搜索 `pizza boxes` ——将该词输入搜索框，点击搜索按钮，然后等待 5 秒让结果加载：

```json
{
    "source": "universal",
    "url": "https://www.etsy.com/",
    "render": "html",
    "browser_instructions": [
        {
            "type": "input",
            "value": "pizza boxes",
            "selector": {
                "type": "css",
                "value": "#global-enhancements-search-query"
            }
        },
        {
            "type": "click",
            "selector": {
                "type": "css",
                "value": "button[data-id='gnav-search-submit-button']"
            }
        },
        {
            "type": "wait",
            "wait_time_s": 5
        }
    ]
}
```

结果包含指令执行后的 HTML：

```json
{
  "results": [
    {
      "content": "<!DOCTYPE html><html lang=\"en-US\">  Search results for \"pizza boxes\"  </html>",
      "created_at": "2026-08-19 12:23:06",
      "updated_at": "2026-08-19 12:23:24",
      "page": 1,
      "url": "https://www.etsy.com/search?q=pizza+boxes&ref=search_bar&instant_download=false",
      "job_id": "7495817623108602881",
      "is_render_forced": false,
      "type": "raw",
      "status_code": 200
    }
  ]
}
```

抓取到的 HTML 应如下所示：

<figure><img src="https://3714446197-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBQ7Zf9paoN3FTeGcyfY1%2Fuploads%2Fgit-blob-c2baf702d27346d7b1f44fb7493f478456ba8520%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

#### 获取浏览器资源 <a href="#fetching-browser-resources" id="fetching-browser-resources"></a>

我们提供一个独立的浏览器指令，用于获取浏览器资源。

函数定义如下：

使用 `fetch_resource` 会使任务返回与所提供格式匹配的第一个 Fetch/XHR 资源，而不是目标 HTML。

假设我们想定位一个在浏览器中自然访问产品页面时抓取的 GraphQL 资源。我们将提供如下任务信息：

```json
{
    "source": "universal",
    "url": "https://petstore.swagger.io/",
    "render": "html",
    "browser_instructions": [
        {
            "type": "fetch_resource",
            "filter": "/v2/swagger.json"
        }
    ]
}
```

这些指令会产生如下结果：

```json
{
    "results": [
        {
            "content": "{\"swagger\":\"2.0\",\"info\":{\"description\":\"This is a sample server Petstore server. ...",
            "created_at": "2026-09-17 12:27:43",
            "updated_at": "2026-09-17 12:28:29",
            "page": 1,
            "url": "https://petstore.swagger.io/v2/swagger.json",
            "job_id": "7506328031753955329",
            "status_code": 200
        }
    ],
    "job": {...}
}
```

## 支持的浏览器指令列表 <a href="#list-of-supported-browser-instructions" id="list-of-supported-browser-instructions"></a>

### 通用参数

下面定义的所有指令都使用一致的参数集。参数如下。

#### `type` <a href="#type" id="type"></a>

* **类型**: `枚举["click", "input", "scroll", "scroll_to_bottom", "wait", "wait_for_element", "fetch_resource"]`
* **说明：** 浏览器指令类型。
* **必填**: `true`

#### `timeout_s` <a href="#timeout_s" id="timeout_s"></a>

* **类型**: `整数`
* **说明：** 如果未在规定时间内完成，动作会在多久后跳过。
* **限制**: 0 < `timeout_s` <= 60
* **默认值**: 5

#### `wait_time_s` <a href="#wait_time_s" id="wait_time_s"></a>

* **类型**: `整数`
* **说明：** 在执行下一动作前等待多长时间。
* **限制**: 0 < `wait_time_s` <= 60
* **默认值**: 0

#### `on_error` <a href="#on_error" id="on_error"></a>

* **类型**: `枚举["error", "skip"]`
* **说明：** 指示当此指令失败时该如何处理指令：
  * `"error"`：停止执行浏览器指令。
  * `"skip"`：继续执行下一条指令。
* **默认值**: `"error"`

#### 通用参数示例

```json
{
    "type": "wait_for_element",
    "selector": {
        "type": "text",
        "value": "Load More Items"
    },
    "timeout_s": 5,
    "wait_time_s": 2,
    "on_error": "skip"

}
```

### 指令 <a href="#click" id="click"></a>

#### `点击` <a href="#click" id="click"></a>

* **说明**：点击一个元素，并等待设定的秒数。
* **参数：**
  * `type: 字符串 = "click"`
  * `selector: 字典`
    * `type: 枚举["xpath", "css", "text"]`
    * `value: 字符串`

**示例**:

```json
{
    "type": "click",
    "selector": {
        "type": "xpath",
        "value": "//button"
    }
}
```

#### `输入` <a href="#input" id="input"></a>

* **说明**：向选定元素中输入文本。
* **参数：**
  * `type: 字符串 = "input"`
  * `selector: 字典`
    * `type: 枚举["xpath", "css", "text"]`
    * `value: 字符串`
  * `value: 字符串`

**示例：**

```json
{
    "type": "input",
    "selector": {
        "type": "xpath",
        "value": "//input"
    },
    "value": "pizza boxes"
}
```

#### `滚动` <a href="#scroll" id="scroll"></a>

* **说明**：滚动设定数量的像素。
* **参数：**
  * `type: 字符串 = "scroll"`
  * `x: 整数`
  * `y: 整数`

**示例：**

```json
{
    "type": "scroll",
    "x": 0,
    "y": 100
}
```

#### `滚动到底部` <a href="#scroll_to_bottom" id="scroll_to_bottom"></a>

* **说明**：在设定的秒数内滚动到底部。
* **参数：**
  * `type: 字符串 = "scroll_to_bottom"`

**示例**:

```json
{
    "type": "scroll_to_bottom",
    "timeout_s": 10
}
```

#### `等待` <a href="#wait" id="wait"></a>

* **说明**：等待设定的秒数。
* **参数：**
  * `type: 字符串 = "wait"`

**示例**:

```json
{
    "type": "wait",
    "wait_time_s": 2
}
```

#### `等待元素` <a href="#wait_for_element" id="wait_for_element"></a>

* **说明**：等待元素在设定的秒数内加载完成。
* **参数：**
  * `type: 字符串 = "wait_for_element"`
  * `selector: 字典`
    * `type: 枚举["xpath", "css", "text"]`
    * `value: 字符串`

**示例：**

```json
{
    "type": "wait_for_element",
    "selector": {
        "type": "text",
        "value": "Load More Items"
    },
    "timeout_s": 5
}
```

#### `fetch_resource` <a href="#fetch_resource" id="fetch_resource"></a>

{% hint style="warning" %}
该 `fetch_resource` 指令必须是浏览器指令列表中的最后一条；之后的任何指令都不会执行。
{% endhint %}

* **说明**：获取与设定模式匹配的第一个 Fetch/XHR 资源。
* **参数：**
  * `type: 字符串 = "fetch_resource"`
  * `filter: 字符串（正则表达式）`
  * `on_error: 枚举["error", "skip"]`

**示例：**

```json
{
    "type": "fetch_resource",
    "filter": "/graphql/item/"
}
```

### 指令校验

任何与指令格式相关的不一致都会导致一个 `400` 状态码和相应的错误消息。

例如，负载如下：

```json
{
    "source": "universal",
    "url": "https://www.example.com/",
    "render": "html",
    "browser_instructions": [
        {
            "type": "unsupported-wait",
            "wait_time_s": 5
        }
    ]
}
```

将导致：

```json
{    
    "errors": {
        "message": "不支持的操作类型 `unsupported-wait`，可从 'click,fetch_resource,input,scroll,scroll_to_bottom,wait,wait_for_element' 中选择"
    }
}
```

## 故障排查 <a href="#status-codes" id="status-codes"></a>

### 状态码 <a href="#status-codes" id="status-codes"></a>

请参见我们列出的响应码 [**此处**](/products/cn/web-scraper-api/response-codes.md). 与指令校验相关的状态码已记录在文档中 [**此处**](#instruction-validation).

### 错误和警告

如果你的浏览操作产生了错误或警告，你会在结果中通过以下键找到它 `browser_instructions_error` 或者 `browser_instructions_warnings`。例如，如果你发送了以下浏览指令，而预期的 `xpath` 未在页面上找到，结果将包含一条警告。

`browser_instructions`:

```json
[
    {
        "type": "input", 
        "selector": {
            "type": "xpath",
            "value": "//input[@type='search']"
        },
        "value": "oxylabs"
    }
]
```

结果：

```json
{
  "results": [
    {
      "content": "<!doctype html><html>
        执行指令后的内容      
      </html>",
      "created_at": "2023-10-11 11:35:23",
      "updated_at": "2023-10-11 11:36:08",
      "browser_instructions_warnings": [
        {
          "action_type": "click",
          "msg": "无法在页面上找到值为 `//input[@type=search]` 的 `xpath` 类型选择器。"
        },
      ],
      "page": 1,
      "url": "https://example.com",
      "job_id": "7117835067442906113",
      "status_code": 200
    }
  ]
}

```

| 可能的错误和警告                                                 |
| -------------------------------------------------------- |
| 将浏览指令转换为操作时发生了意外错误。                                      |
| 执行时发生了意外错误 `{action.type}` 浏览指令。                         |
| 操作 `{action.type}` 超时。                                   |
| 无法找到选择器类型 `{selector.type}` 其值为 `{selector.value}` 在页面上。 |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.oxylabs.io/products/cn/web-scraper-api/features/js-rendering-and-browser-control.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
