# Ctrip Hotel Reviews Scraper (`automation-lab/ctrip-hotel-reviews-scraper`) Actor

Export the first public Ctrip hotel review group: original text, ratings, stable IDs, dates, public reviewer names, room/traveler context and media links. Up to 10 reviews per property; no full-history pagination.

- **URL**: https://apify.com/automation-lab/ctrip-hotel-reviews-scraper.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.17 / 1,000 guest reviews

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

## Ctrip Hotel Reviews Scraper

Export **Ctrip hotel reviews** for recurring hotel reputation analysis. Supply Ctrip hotel URLs or IDs and receive the first public guest-review group—up to **10 reviews per property**—with original text, ratings, stable review IDs, resolved hotel IDs, dates, public reviewer display names, room/traveler context and available media URLs.

This is a bounded snapshot tool, **not a full-history scraper**. Repeated snapshots can be compared downstream by `hotelId` and `reviewId`; they do not guarantee that every new review between runs is captured.

### Who is it for?

- Hotel reputation managers checking public feedback across a property portfolio.
- Hospitality analysts assembling small, repeatable original-language samples.
- Research teams joining guest feedback to their existing hotel-ID tables.

### Why use it?

The output preserves source text and source identifiers rather than substituting an AI summary. Requested IDs and resolved master-property IDs are separate, which helps reconcile aliases without counting the same review twice. Collection is deliberately limited to the first public group, making the coverage easy to understand.

No Ctrip account or browser session is supplied by the user. Requests use direct HTTP; there is no automatic residential proxy or browser fallback.

### Getting started

1. Open the Actor input form.
2. Enter a Ctrip hotel ID, for example `346322`, or a supported hotel URL.
3. Set a global maximum review count if needed.
4. Start the Actor and wait for its terminal status.
5. Open the default dataset and export JSON, CSV or Excel using Apify's dataset export tools.

```json
{
  "hotelIds": ["346322"],
  "maxItems": 10
}
```

### Inputs

| Field | Meaning |
| --- | --- |
| `hotelIds` | Positive numeric Ctrip hotel IDs as strings. |
| `startUrls` | Ctrip hotel URLs as URL objects; see the example below. |
| `maxItems` | Global unique-review limit, default 100, minimum 1, maximum 500. |

Supply at least one ID or URL and at most 50 combined entries before deduplication. `hotelIds` are processed first, followed by URLs. Duplicate requested IDs are fetched once. Aliases may resolve to the same master hotel; duplicate resolved hotel/review pairs are not saved twice.

```json
{
  "startUrls": [
    {"url": "https://hotels.ctrip.com/hotels/436181.html"},
    {"url": "https://hotels.ctrip.com/hotels/detail/?hotelId=375966"}
  ],
  "maxItems": 15
}
```

Only HTTPS `hotels.ctrip.com/hotels/ID.html` and `/hotels/detail/?hotelId=ID` URLs are supported. Tracking parameters are discarded. Only each entry's `url` is used; request-list `method`, `payload`, `headers` and `userData` are ignored. Requests always use GET with Actor-managed headers. Trip.com, other Ctrip products, login URLs and arbitrary web pages are rejected. Unsupported input fields are rejected rather than silently ignored.

### Ctrip hotel reviews coverage

For each property the Actor reads only the **first public review group**, capped at 10 rows. Properties with fewer visible reviews yield fewer rows. Increasing `maxItems` lets the run collect from more hotels; it never enables pagination.

Source ordering may be recommended or otherwise selected by Ctrip. It is **not guaranteed newest-first**, random, representative of all guests, or exhaustive. There are no date, rating, language or keyword filters. Original text is not translated.

A source-verified hotel with zero reviews produces no review rows. Blocked responses, unrecognized page structures and missing review state are failures, not successful empty datasets.

### Extracted data

| Field | Description |
| --- | --- |
| `reviewId` | Stable public review ID, useful for downstream joins. |
| `hotelId` | Resolved master hotel ID. |
| `requestedHotelId` | ID supplied by the user, which may be an alias. |
| `hotelName`, `hotelUrl` | Hotel name and resolved Ctrip URL. |
| `text` | Original guest review text. |
| `rating`, `ratingScale` | Overall score and maximum scale of 5. |
| `postedAt`, `stayDate` | Source posting and check-in strings, without inferred timezone. |
| `reviewerName` | Public display name with any source masking retained. |
| `roomName`, `travelerType` | Room and traveler/trip labels, when available. |
| `language` | Source language code, such as `CN`. |
| `imageUrls`, `videoUrls` | Available public media URLs; media is not downloaded. |
| `scrapedAt` | UTC snapshot timestamp. |

Optional source fields can be null; media arrays are empty when absent. Check-in dates may be rounded by Ctrip to a month and must not be interpreted as exact stay dates. No private reviewer account IDs, cookies or profile histories are exported.

### Output example

The shape below follows extracted review rows; personal display text is anonymized for this example.

```json
{
  "reviewId": "2100000001",
  "hotelId": "346322",
  "requestedHotelId": "436181",
  "hotelName": "Example Hotel",
  "hotelUrl": "https://hotels.ctrip.com/hotels/346322.html",
  "text": "房间干净，服务周到。",
  "rating": 4.5,
  "ratingScale": 5,
  "postedAt": "2026-10-01 12:00:00",
  "stayDate": "2026-09-01 00:00:00",
  "reviewerName": "Example****",
  "roomName": "豪华双床房",
  "travelerType": "朋友出游",
  "language": "CN",
  "imageUrls": [],
  "videoUrls": [],
  "scrapedAt": "2026-10-18T20:00:00.000Z"
}
```

The key-value store also contains `SUMMARY`: saved review count, request attempts including redirects, received HTML bytes, and `firstGroupOnly: true`. These counters are provided in run storage.

### How much does it cost to export Ctrip hotel reviews?

Pay per event: one **start** fee per valid run plus one **item** event per unique guest review saved. Empty or rejected review rows have no item event. A valid run checking a hotel with no visible reviews still has its start fee. Invalid input is rejected before charging the start event.

The start fee is **$0.005 per run** on all plans. Per-review prices depend on your Apify Store spend tier (aggregate qualifying monthly Store spend, not a volume discount invented for this Actor):

| Spend tier | Per saved review |
| --- | --- |
| FREE | $0.0022379 |
| BRONZE | $0.001946 |
| SILVER | $0.0015179 |
| GOLD | $0.0011676 |
| PLATINUM | $0.0011676 |
| DIAMOND | $0.0011676 |

Estimated BRONZE totals including one start fee: 1 review **$0.006946**, 10 reviews **$0.02446**, and 100 reviews across at least 10 properties **$0.1996**. These are event-charge estimates, not guaranteed invoices. Always consult active pricing. Apify's maximum total charge setting can stop a run before its requested review count.

### Integrations

- Export a review snapshot to Google Sheets and join it to a property registry by resolved `hotelId`.
- Send JSON to a warehouse through Make, Zapier or an Apify webhook after a successful run.
- Schedule snapshots with Apify schedules and compare `(hotelId, reviewId)` in your own database. Store the first-seen timestamp there; this Actor does not persist a cross-run seen list.
- Feed original text to a separate translation or sentiment pipeline. Such processing is not performed or billed by this Actor.

### API examples

Use your own Apify token as a secret environment variable; do not place credentials in shared URLs or logs.

#### cURL

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~ctrip-hotel-reviews-scraper/run-sync-get-dataset-items' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"hotelIds":["346322"],"maxItems":10}'
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/ctrip-hotel-reviews-scraper').call({
  hotelIds: ['346322'], maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/ctrip-hotel-reviews-scraper').call(
    run_input={'hotelIds': ['346322'], 'maxItems': 10})
print(client.dataset(run['defaultDatasetId']).list_items(limit=10).items)
```

### MCP use

Select the stable Actor-specific endpoint rather than a broad tool category:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/ctrip-hotel-reviews-scraper"
```

Equivalent **Claude Desktop**, **Cursor**, and **VS Code** HTTP-capable client configuration (authentication follows your client's secure Apify setup; use a supported HTTP bridge if your client requires one):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/ctrip-hotel-reviews-scraper"
    }
  }
}
```

Example prompt: “Export the first public Ctrip review group for hotel 346322, at most 10 reviews, then summarize the returned original text.”

Discover the actual tools and argument schemas using scoped `tools/list`. Actor selection also adds run/status/storage helpers; it does not expose exactly one tool. Discovery is read-only, not permission to execute or abort.

For an authorized run, start once and retain the run ID and storage IDs. `call-actor` waits at most 45 seconds (default 30), so a run response is not necessarily the review dataset. For a nonterminal run, query that same run with bounded backoff (2, 4, 8 seconds, capped at 10) and a total deadline such as 120 seconds. Configure the client timeout above the selected server wait plus transport margin. Recover a known run after a client timeout; do not restart merely to poll. If the ID is unknown, report uncertainty. At the deadline, report pending and stop.

After success, read `get-dataset-items` with explicit dataset ID, limit, offset and fields from its discovered schema. A reasonable consumer budget is 20 rows per page, at most 100 rows and 64 KiB of serialized UTF-8 content admitted to model context, whichever comes first. Enforce that byte ceiling host-side, disclose oversized/truncated rows and continue offsets using source pagination. If your client cannot intercept oversized responses, do not promise a hard byte guarantee. Keep full exports in storage outside model context. Terminal failures may leave partial rows; do not describe those as a complete successful result.

### Limits and troubleshooting

- A hotel alias can resolve to another master ID. Use the returned `hotelId` for joins; retain `requestedHotelId` for audit.
- A global limit of 1 may return only one review from the first hotel. Hotels after the limit is reached are not requested.
- Network timeouts and transient 429/5xx responses get at most two attempts; there is no endless retry or CAPTCHA solving.
- A later hotel failure fails the run; previously saved rows are partial and remain in the dataset.
- A page-format or blocking error is reported explicitly. Retry later only when you have a reason to expect the upstream condition changed.
- Media links can expire or be unavailable; this Actor does not archive the assets.

### Legality, responsible use and diagnostics

Collect only public hotel-review data you are entitled to process. Respect source terms, applicable law, privacy and reasonable request volumes. Public availability is not permission to identify reviewers or republish personal information without a lawful basis. This tool is independent and is not endorsed by Ctrip.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL query strings are removed; reports are retained for 30 days.

Apify hosts run datasets, input, summaries and logs under your account's storage and retention settings; delete them in Console or through the storage API when no longer needed. The Actor creates no cross-run review cache, downloads no media and sends no review text to an AI provider. Ctrip receives hotel requests; Apify and our private telemetry service handle the operational data described above. Ask product questions or report reproducible issues through the Actor's Apify Store Issues tab.

### FAQ

**Does it collect all hotel reviews?** No. Only the first public group, up to 10 per property.

**Does scheduling guarantee every new review?** No. The first group can change between runs and is not guaranteed chronological. Compare snapshots as samples, not a complete review stream.

**Can I use Trip.com URLs?** No. This Actor supports the specified Ctrip hotel URL patterns and IDs only.

**Why are there fewer than maxItems?** The source group cap, duplicate aliases, properties with fewer public reviews, or the run charge limit can reduce the count.

**Are reviews translated?** No. Text, traveler labels and language codes remain as supplied by the source.

### Related Actors

For a different booking source, see our [Agoda Hotel Reviews Scraper](https://apify.com/automation-lab/agoda-hotel-reviews-scraper). Source identifiers differ, so reconcile properties before combining the datasets.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/ctrip-hotel-reviews-scraper/changelog.md

# Actor input Schema

## `hotelIds` (type: `array`):

Positive numeric Ctrip hotel IDs as strings. Combined with startUrls in this order; 1–50 entries combined, deduplicated before fetching. IDs may resolve to a different master property; both IDs are returned.

## `startUrls` (type: `array`):

HTTPS hotels.ctrip.com/hotels/ID.html or hotels/detail/?hotelId=ID URLs. Combined after hotelIds; URL tracking/query fields are discarded. Other hosts, login and Trip.com URLs are rejected. Only the url field is used: method, payload, headers and userData are ignored; requests always use GET with Actor-managed headers. The same first-group and global-limit behavior applies to both inputs.

## `maxItems` (type: `integer`):

Global maximum unique reviews saved across all properties, after deduplication by resolved hotel ID and review ID. Default 100; range 1–500; zero/unlimited is not supported. Each hotel supplies only its first public group, capped at 10; this limit never enables pagination. Source order is not guaranteed newest-first.

## Actor input object example

```json
{
  "hotelIds": [
    "346322"
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Unique public review rows saved during this run.

## `summary` (type: `string`):

Collection counters and scope.

# 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 = {
    "hotelIds": [
        "346322"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/ctrip-hotel-reviews-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 = {
    "hotelIds": ["346322"],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/ctrip-hotel-reviews-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 '{
  "hotelIds": [
    "346322"
  ],
  "maxItems": 10
}' |
apify call automation-lab/ctrip-hotel-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/ctrip-hotel-reviews-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/eG1Gf7aGB9vtEKpNx/builds/Hw4J95RjiLEQBnWrH/openapi.json
