> 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 渲染与浏览器控制

## 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="/files/d16ea115e5d720d5ea91a4b631dd6b658ac5b5b5" 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://www.example.com/product-page/123",
    "render": "html",
    "browser_instructions": [
        {
            "type": "fetch_resource",
            "filter": "/graphql/product-info/123"
        }
    ]
}
```

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

```json
{
  "results": [
    {
      "content": "{'product_id': 123, 'description': '', 'price': 123}",
      "created_at": "2023-10-11 11:35:23",
      "updated_at": "2023-10-11 11:36:08",
      "page": 1,
      "url": "https://example.com/v1/graphql/product-info/123/",
      "job_id": "7117835067442906114",
      "status_code": 200
    }
  ]
}
```

## 支持的浏览器指令列表 <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"]`
* **说明：** 浏览器指令类型。
* **必填**: `是`

#### `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>

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

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

**示例**:

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

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

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

**示例：**

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

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

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

**示例：**

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

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

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

**示例**:

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

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

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

**示例**:

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

#### `wait_for_element` <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)。有关指令验证的状态码已有文档说明 [**这里**](/products/cn/web-scraper-api/features/js-rendering-and-browser-control.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.
