# Bilibili Videos & Comments Scraper (`devcake/bilibili-scraper`) Actor

Search public Bilibili videos with independent limits per query, or collect comments and replies from video links.

- **URL**: https://apify.com/devcake/bilibili-scraper.md
- **Developed by:** [devcake](https://apify.com/devcake) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 video results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bilibili Videos & Comments Scraper

Search public Bilibili videos or collect comments and replies from video links. The Actor writes structured rows to the Apify dataset and a coverage report to the run's key-value store.

### Get started

Enter **Search words** to find videos, or add **Video links or IDs** to collect comments. Video links take priority when both are filled, including when the search example is still present. Leave video links empty to search. Blank entries are ignored; if both lists are empty, add search words or video links to start. Invalid video targets are reported instead of starting an unintended search. The form has three sections: **Search**, **Filters**, and **Comments**.

| Scenario | Required input | Dataset rows |
| --- | --- | --- |
| Search | `searchQueries` | Videos with ordinary search metadata, optionally comments |
| Comments | `videoUrls` | Comments and optional replies, without standalone video rows |

#### Search and filters

Enter one independent query per line in **Search words**. **Videos per query** defaults to 30 and accepts 30–5000; explicitly lower values are rejected. Three queries at 30 videos each can return up to 90 unique videos. **Filters** contains search order, length, and inclusive publication dates; Comments ignores these filters.

Videos are deduplicated across queries. A later query continues looking for new videos until its own quota or source limit is reached. Chinese brand-name localization is enabled automatically for supported English brands; translated variants share their original query’s quota.

#### Comments

Enter one `BV` ID, older `av` ID, or `https://www.bilibili.com/video/...` link per line. **Comments** collects comments automatically from the first 10 distinct target videos per run by default. Use full Bilibili video URLs or BV/av IDs; shortened `b23.tv` links and creator/profile URLs are not accepted. It may fetch video metadata internally to identify and contextualize comments, but emits no standalone video rows.

**Comments on Search** optionally adds comment rows to videos found by Search. This toggle is not required for Comments. **Include replies**, **Anonymize commenters**, **Comments per video**, and **Comment order** apply whenever comments are collected. Separate reply rows count toward the per-video comment limit. Parent comments may include up to three reply previews even when **Include replies** is off.

**Anonymize commenters** clears structured commenter identity fields and preview identities. It does not redact names or other personal information inside comment text, or remove the source-provided location label.

**Estimate sentiment** adds an estimated score, polarity, and method to collected video and comment rows. It uses SnowNLP for text containing Chinese characters and a small English keyword list otherwise; it is not a general multilingual sentiment model.

### Pricing

You pay **$0.004 per unique video row** ($4 per 1,000) and **$0.0015 per unique comment or separate reply row** ($1.50 per 1,000) saved to the dataset. Search with comments charges for both row types; Comments-only input charges only for comments and replies.

There is no start fee. Empty results, unavailable videos, validation errors, duplicate rows, inline reply previews, the coverage report, and other legacy output row types do not trigger these charges. Rows already delivered remain chargeable even if a later source request fails. Set a maximum charge for the run to control spending; the Actor stops when it cannot deliver the next result type within that limit, preserves saved rows, and records partial coverage in `REPORT`. A small remainder may remain unused.

### Output

The run’s **Output** tab links to both the default dataset and the structured coverage report. The dataset provides Overview, Video fields, Comment fields, and Compatible legacy fields views. Its machine-readable schema documents every supported field and record type for API, MCP, and automation clients. The `REPORT` record contains counts, requests, pagination, warnings, privacy-safe error categories, and coverage limits.

Public scenarios return `video` and/or `comment` rows. Search uses ordinary listing metadata without automatic full-details enrichment. Fields unavailable in the listing remain null or empty; missing metrics are not invented.

Video rows include identity, title, description, URL, author, category, available engagement counts, and collection time. Search rows also have `searchKeyword` and `sourceQuery`. Comment rows include `commentId`, text, author fields, engagement, timestamps, `videoBvid`, video context, and `rootRpid`/`parentRpid` for replies.

Example inputs:

```json
{"searchQueries":["人工智能教程","机器学习","Python教程"],"maxVideosPerQuery":30}
```

```json
{"videoUrls":["BV1xx411c7mD"],"includeReplies":true,"maxComments":20}
```

### Coverage and limits

Bilibili may restrict anonymous searches and comment pages. Unavailable fields remain null or empty. Source restrictions, unexpectedly ended pagination, and failed requested enrichments are recorded in `REPORT` as partial coverage; a configured limit does not guarantee that many accessible records. A partial or failed report marks the Apify run **Failed**, even when its dataset contains usable rows; inspect both the dataset and `REPORT`. Reaching a requested quota alone does not mark a run as failed.

Search uses **Videos per query** independently for each query. Comments processes up to 10 target videos per run by default. **Comments per video** includes replies, and comment rows do not consume video quotas. Pagination also has internal page bounds.

Transient network/5xx failures and rate limits (HTTP429/API−509) have at most two retries by default, with exponential backoff and up to 0.5s jitter. Retry-After is honored when it fits the cumulative 30-second retry-wait budget; otherwise the request stops without retrying early. Verification/authentication failures are not blindly retried. The Actor does not solve CAPTCHA or verification challenges. `REPORT.retries` provides safe endpoint/reason/delay diagnostics.

Search filters affect video discovery only. They do not filter comments by date or length. **Most liked in results** sorts the comments actually collected; it does not guarantee the most-liked comments across the entire video.

### Existing API integrations

The current form selects Search or Comments automatically. An explicit legacy `mode` in API input or saved JSON overrides that selection. Remove `mode` from an old saved input to use the current automatic behavior.

Creator collection, direct video details, subtitles, rankings, and other historical modes are not offered in the form. Their runtime implementations remain for existing integrations, with no guarantee of complete creator listings or access to restricted data. See the [legacy API compatibility reference](https://github.com/Bahm9919/bilibili-scraper/blob/main/docs/LEGACY_API.md) for retained settings and limits.

### Local development

```sh
python -m venv .venv
.venv/bin/pip install -r requirements.txt pytest ruff
.venv/bin/python -m pytest tests -q
.venv/bin/ruff check bilibili_scraper tests
apify validate-schema
```

The Dockerfile uses Python 3.12. `apify push` deploys the code and builds it on Apify. Cloud runs use an Apify China residential proxy by default, or a privately configured `BILIBILI_PROXY_URL` environment variable. Proxy credentials belong in private runtime configuration, never in Actor input or source control. Local command-line runs also accept `--proxy-file /private/path/proxy.txt`; see `python -m bilibili_scraper --help`.

# Actor input Schema

## `searchQueries` (type: `array`):

Enter one video search query per line. Each query has its own limit: 3 queries × 30 = up to 90 unique videos if available. Leave Video links or IDs empty to search.

## `maxVideosPerQuery` (type: `integer`):

Limit videos for each Search words entry independently. Default and minimum: 30. Chinese brand-name variants share the original query limit. Duplicate videos are output once; later queries continue looking for new videos. Each unique video row saved costs $0.004.

## `sortOrder` (type: `string`):

Choose how to order videos found by search. This does not change comment order.

## `durationFilter` (type: `string`):

Filter videos found by search by their duration. This does not filter comments.

## `pubtimeBegin` (type: `string`):

Choose the first publication date for Search videos, inclusive, in Bilibili time (UTC+8).

## `pubtimeEnd` (type: `string`):

Choose the last publication date for Search videos, inclusive, in Bilibili time (UTC+8).

## `videoUrls` (type: `array`):

Enter one BV ID, av ID, or full bilibili.com/video/ URL per line. Collects comments from the first 10 distinct videos per run by default. Short b23.tv and profile links are not accepted. Video links take priority over Search words; leave this list empty to search.

## `includeComments` (type: `boolean`):

Add comments to videos found by Search. Video links collect comments automatically, without requiring this toggle.

## `includeReplies` (type: `boolean`):

Collect available thread replies as separate rows within Comments per video. Parent comments may still contain reply previews when this is off.

## `anonymizeCommenters` (type: `boolean`):

Clear structured commenter identities, profile details, and preview identities. Comment text and source-provided location labels remain unchanged.

## `maxComments` (type: `integer`):

Limit the combined comment and reply rows for each video. Bilibili may expose fewer rows to anonymous requests. Each unique comment or separate reply row saved costs $0.0015; inline reply previews are not charged.

## `sortComments` (type: `string`):

Choose popular or newest comments. Most liked in results sorts only the collected rows, not all comments on the video.

## `sentimentAnalysis` (type: `boolean`):

Add estimated sentiment to collected video and comment text. Uses a Chinese model or a small English keyword list; other languages are not reliably supported.

## Actor input object example

```json
{
  "searchQueries": [
    "人工智能教程"
  ],
  "maxVideosPerQuery": 30,
  "sortOrder": "totalrank",
  "durationFilter": "any",
  "videoUrls": [
    "BV1qZSLBYEpa"
  ],
  "includeComments": false,
  "includeReplies": false,
  "anonymizeCommenters": false,
  "maxComments": 20,
  "sortComments": "hot",
  "sentimentAnalysis": false
}
```

# Actor output Schema

## `results` (type: `string`):

Video, comment, reply, and compatible legacy records saved in the default dataset.

## `coverageReport` (type: `string`):

Structured run status, record counts, limits, warnings, errors, retries, and pagination diagnostics.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "人工智能教程"
    ],
    "maxVideosPerQuery": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("devcake/bilibili-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["人工智能教程"],
    "maxVideosPerQuery": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("devcake/bilibili-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "人工智能教程"
  ],
  "maxVideosPerQuery": 30
}' |
apify call devcake/bilibili-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devcake/bilibili-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/x4anE7GA1nWmmBKx2/builds/4CiNv6GIdLxWuispm/openapi.json
