Bilibili (哔哩哔哩) Scraper - Videos, Comments & Replies
Pricing
$4.99 / 1,000 results
Bilibili (哔哩哔哩) Scraper - Videos, Comments & Replies
Search Bilibili videos by keyword or by link and collect engagement counts, tags and optionally comments with replies.
Pricing
$4.99 / 1,000 results
Rating
0.0
(0)
Developer
Lantern Lab
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
8 hours ago
Last modified
Categories
Share
Bilibili Video & Comment Scraper
Search Bilibili (哔哩哔哩) videos by keyword, or hand it video links, and get one row per video with the full engagement counts Bilibili shows on the page: views, likes, coins, favourites, shares, danmaku and comment count, plus the description, tags, duration, publish time, cover and author. Turn comments on and it collects the comments and the replies under them as well. No Bilibili login, no cookies, no account of your own. $4.99 per 1,000 results (a video or a comment row), no start fee. See Pricing.
Input:
{"keywords": ["美食"],"maxVideosPerKeyword": 20,"includeComments": false}
Output - one row per video (type: "video"); keyword- and video-summary rows are free. Sample from a real run; channel name partially masked, cover link shortened, tags trimmed:
{"type": "video","bvid": "BV1q6nb6cE4P","aid": "117488947892299","title": "与众09沿海而行,邂逅悠然青岛","description": "追风逐浪,与众09载着海风向前 \n纯电静谧,恰与大海的沉静相融…","url": "https://www.bilibili.com/video/BV1q6nb6cE4P","coverUrl": "http://i1.hdslb.com/bfs/archive/…","durationSec": 60,"publishedAt": "2026-10-23T07:06:35.000Z","author": { "mid": "3546679701670263", "name": "大众***" },"stats": {"views": 22, "likes": 1, "coins": 0, "favorites": 1,"shares": 1, "danmaku": 0, "comments": 0},"tags": ["#大众安徽", "#大众09大众最美智能纯电轿跑", "…"],"keyword": "与众09","collectedAt": "2026-10-23T08:04:48.468Z","partial": false,"partialReason": null,"commentsRequested": false,"commentStatus": "not_requested"}
Limits:
- Public data only. Reads what Bilibili shows a signed-out visitor; the Actor never logs in. No danmaku, no IP region on comments (Bilibili does not return it signed out), no downloads.
- Every keyword gets a summary row saying whether its videos were collected in full, and every video says whether its comments were. A video or keyword that could not be read is marked
failedwith the reason - never "0 results". Bilibili's own search stops at 1,000 videos per keyword. - Your limits stop collection and appear in the row:
maxVideosPerKeyword,maxCommentsPerVideo, themaxUpstreamRequestsrequest ceiling and the maximum charge per run (see Pricing).
What it does
- Searches each keyword and reads the result pages until your limit or the end of what Bilibili returns. Bilibili's own search stops at 1,000 videos per keyword.
- Reads each video's own detail page, so
coinsandsharesare real numbers rather than blanks - the search results alone do not carry them. - Collects comments in the order the video page opens with (hot first), together with the replies under each comment. A comment page already carries up to three replies per comment; the reply endpoint is only called when a comment shows more than came with it.
- Takes video links, short links (
b23.tv), share text copied from the app, BV IDs and av numbers. A line that is none of these stops the run at once - nothing is collected or charged. - Compares what it collected with what Bilibili claims: per keyword (results reported vs. collected) and per video (comment count vs. comments collected).
What it does not do
- No danmaku (the on-screen comments), no channel follower lists, no video or audio downloads, no anything that needs a logged-in account.
- No IP region on comments. Bilibili does not return it on signed-out reads, so
author.ipLocationisnullrather than filled in from elsewhere. - No multi-part handling:
durationSecis the whole video's length, as Bilibili reports it. - No scheduling or monitoring built in; use Apify schedules.
Input
| Field | Default | Meaning |
|---|---|---|
keywords | - | One search term per line. |
videoUrls | - | One video per line: https://www.bilibili.com/video/BV..., https://b23.tv/..., share text from the app, a BV ID such as BV1xx411c7mD, or an av number such as av2. Collected before the keywords. |
maxVideosPerKeyword | 20 | Videos to collect per keyword (1-1000). |
sort | relevance | Search order: relevance, newest, mostPlayed or mostDanmaku. Does not affect videoUrls. |
includeComments | false | Also collect each video's comments and their replies. |
maxCommentsPerVideo | 50 | Comments and replies together per video (1-2000). |
maxUpstreamRequests | derived | Hard ceiling on requests to Bilibili for the whole run. Leave empty and the Actor works one out from the rest of your input. |
At least one of keywords and videoUrls is required. Example:
{"keywords": ["美食", "旅行"],"videoUrls": ["https://www.bilibili.com/video/BV1xx411c7mD"],"maxVideosPerKeyword": 20,"sort": "relevance","includeComments": true,"maxCommentsPerVideo": 50}
The same video entered twice, or found under two keywords, is collected and charged once; the repeat gets a video_summary row with stopReason: "duplicate".
maxUpstreamRequests gives you a known upper bound before the run starts. It counts every request the run sends to Bilibili - search pages, video details, comment pages, reply pages, the signing-key read and every retry. Leave it empty and the Actor derives a ceiling from your keywords, video links, maxVideosPerKeyword and comment settings, so a run is never unbounded. When the ceiling is reached, what was being collected is marked partial with stopReason: "upstream_request_cap", anything after it is reported as not collected, and the run ends normally. This is a limit of yours, not a problem at Bilibili, which may have more.
Output
One dataset; type tells the rows apart. Four table views are predefined: Videos, Comments and replies, Keyword summaries, and Videos not collected. A RUN_SUMMARY record in the key-value store carries the run totals, the status of each keyword, the request counts and why collection stopped.
type: "video"
| Field | Meaning |
|---|---|
bvid, aid | Bilibili's two IDs for the video. |
title, description | As Bilibili shows them. |
url | https://www.bilibili.com/video/<bvid>. |
coverUrl | Cover image. |
durationSec | Length in seconds. |
publishedAt | When it was published on Bilibili (UTC). |
author | { mid, name } - the channel that published it. |
stats | { views, likes, coins, favorites, shares, danmaku, comments }. A number Bilibili did not return is null, never 0. |
tags | The tags on the video page. |
keyword | The search term it was found under; null for a video given by link. |
collectedAt | When we collected the row (UTC). |
commentsRequested | Whether this run asked for comments. |
commentsCollected, repliesCollected | How many were written; null when comments were not requested. |
commentStatus | complete, partial, failed, none, platform_gap or not_requested. |
commentStopReason | Machine-readable reason the comments ended, for example limit_reached, risk_control, end_of_comments. |
partial, partialReason | true with a sentence when this video's collection was cut short. |
type: "comment"
| Field | Meaning |
|---|---|
commentId | Bilibili comment ID (rpid), as a string: these are 64-bit. |
bvid | The video it belongs to. |
level | 1 for a top-level comment, 2 for a reply. |
parentCommentId | The comment this one answers - a top-level comment or another reply. null on a top-level comment. |
rootCommentId | The top-level comment the whole thread hangs off. null on a top-level comment. |
text | The comment text. Emoji appear as Bilibili codes such as [笑哭]. |
likes | Likes at collection time. |
replyCount | Replies Bilibili counts under it. |
publishedAt | When it was posted (UTC). |
author | { mid, name, ipLocation }. Only what the page prints next to the comment; ipLocation is null on signed-out reads. |
collectedAt | When we collected the row (UTC). |
partial, partialReason | true on a comment whose replies were cut short, with the reason. |
type: "keyword_summary" and type: "video_summary"
One keyword_summary per keyword: videosRequested, videosCollected, resultsSeen, duplicatesSkipped, searchPagesRead, resultsReportedByBilibili, collectionStatus (complete, partial, empty or failed), stopReason, statusReason and partial / partialReason.
A video_summary row appears for every video the run was asked for but wrote no video row for: a duplicate (skipped), a video Bilibili no longer serves (not_found), or one the request limit or an error cut off (failed). A keyword that returns nothing is empty with partial: false; a keyword Bilibili refused to serve is failed with partial: true and says so. The two are never mixed up.
Blocking is reported, not hidden
Bilibili sometimes answers a comment request with a three-comment preview and "the list is finished", while its own counter says there are tens of thousands. That is Bilibili withholding the list. This Actor recognises it, retries once from a different egress, and if the list is still withheld marks the video partial with commentStopReason: "risk_control". It never writes a withheld preview out as "this video has 3 comments". RUN_SUMMARY carries riskControlHits, the egress the run ended on and every switch it made.
Pricing
Pay per result: every video row and every comment row is one charged result. Summary rows are free. Comments are the expensive part of a run - one video with 50 comments is 51 results, not 1 - which is why includeComments is off by default.