# Zhihu Columns & Articles — $3/1k (`memo23/zhihu-scraper`) Actor

Zhihu scraper for public columns and articles. Give it a column URL or an article URL and download title, author, headline, upvotes, comments, topics, excerpt and HTML body as JSON or CSV. No login. Search and question threads stay behind Zhihu login and are not collected.

- **URL**: https://apify.com/memo23/zhihu-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 articles

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Zhihu Columns & Articles Scraper

**Paste a Zhihu column or article URL and get the title, author, upvotes, comments, excerpt and HTML body as JSON or CSV.**

Public guest pages only. A column URL walks that column and returns one row per article. An article URL returns that single article. No Zhihu login, no browser.

$3 per 1,000 articles, plus a $0.005 start fee.

***

### Why Use This Scraper?

- **No login.** The actor reads public column and article pages. It does not ask for a cookie and it does not mint one.
- **Column lists carry the longer HTML.** The column endpoint returns the article body we measured at about 3,000 characters. The single-article endpoint for the same id returns a shorter body.
- **One row per article.** Title, author, headline, votes, comments, excerpt, HTML, image and URL. Ready for a sheet or an API pull.
- **You set the cap.** Default is 20 articles. A busy column can hold hundreds, so `maxItems` stops the run where you want it.
- **Pay per article returned.** Empty or skipped URLs are not billed as articles.

***

### Overview

This actor turns a **Zhihu column** or a **Zhihu article** into a flat dataset. The entity is an article (`zhuanlan`), not a question and not a search hit.

A column URL such as `https://zhuanlan.zhihu.com/xuehy` pages through that column's public item list. Each item becomes one row, and the HTML on that row comes from the list payload.

An article URL such as `https://zhuanlan.zhihu.com/p/2084570146228664180` fetches that article on its own. That path also returns topics. The HTML on that path is shorter than the same article's HTML inside a column list.

Search, question pages and answer threads need a logged-in Zhihu session. This actor does not log in, so those URLs are skipped and written to the log.

Zhihu marks guest bodies with `contentTruncated: true` even when the column list already returned a few thousand characters of HTML. Salt-select (盐选) pieces that Zhihu keeps behind a purchase stay on the excerpt.

***

### Supported Inputs

| Input | What you get |
|---|---|
| `https://zhuanlan.zhihu.com/{slug}` | Every article in that column, up to `maxItems` |
| `https://www.zhihu.com/column/{slug}` | Same column list |
| Column slug, e.g. `xuehy` | Same column list |
| `https://zhuanlan.zhihu.com/p/{id}` | One article |
| Numeric article id | One article |

Copy-paste column run:

```json
{
    "startUrls": ["https://zhuanlan.zhihu.com/xuehy"],
    "maxItems": 20
}
```

Not supported, and dropped with a log line:

- `https://www.zhihu.com/question/{id}`
- `https://www.zhihu.com/search?q=...`
- Answer URLs
- People profile URLs as a listing

***

### Use Cases

| Who | What they do with the rows |
|---|---|
| Research | Read a columnist's public essays without clicking through each post |
| Media monitoring | Track a named column for new titles, votes and comment counts |
| Market notes | Pull Chinese-language commentary on a topic a column already covers |
| Archives | Save the public HTML of articles you can already open in a browser |
| Agents | Hand a column URL to an Apify call and read a flat article list |

***

### How It Works

![How the Zhihu scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-zhihu.png)

1. You pass column URLs, article URLs, slugs or numeric ids.
2. Each value is classified. Question and search links are skipped.
3. A column is read from Zhihu's public column-items API, page by page, until `maxItems` or the last page.
4. An article id that did not already come from a column is fetched from the public article API.
5. Each article is pushed as one dataset row. On Apify, that row is the billed result.

Pure HTTP. No browser.

***

### Input Configuration

| Field | Type | Required | Notes |
|---|---|---|---|
| `startUrls` | string\[] | no | Column and article URLs. Prefill: `https://zhuanlan.zhihu.com/xuehy`. If every list is empty, that column is used. |
| `columnIds` | string\[] | no | Slugs such as `xuehy`. |
| `articleIds` | string\[] | no | Numeric ids such as `2084570146228664180`. |
| `maxItems` | integer | no | Default `20`. Minimum `1`. Free plans cap at 100. |
| `maxConcurrency` | integer | no | Default `5`. Range 1–20. Applies to article-detail calls. Column pages stay sequential. |

#### Column, 5 articles

```json
{
    "startUrls": ["https://zhuanlan.zhihu.com/xuehy"],
    "maxItems": 5
}
```

#### One article

```json
{
    "articleIds": ["2084570146228664180"],
    "maxItems": 1
}
```

#### Column plus one extra article

```json
{
    "startUrls": ["https://zhuanlan.zhihu.com/xuehy"],
    "articleIds": ["2082752758818649391"],
    "maxItems": 10
}
```

The column list is collected first. An article id already present in that list is not fetched a second time.

***

### Output Overview

Every row is one article. There is no separate column row and no padding row. Column name is the slug on each article (`columnSlug`).

`source` is `column` when the row came from a column list, and `article` when it came from the article endpoint. Column rows carry the longer HTML. Article rows add `topics` when Zhihu sends them. `contentTruncated` follows Zhihu's own guest flag.

***

### Output Samples

Column list, `https://zhuanlan.zhihu.com/xuehy`, first article. HTML below is cut for the README. The live row's `content` was 3,015 characters.

```json
{
    "entityType": "article",
    "articleId": "2084570146228664180",
    "title": "复利没那么温柔：为什么十年十倍的故事，你复制不了？",
    "excerpt": "“复利”几乎是所有长期投资叙事中最有吸引力的词。…",
    "content": "<p>“复利”几乎是所有长期投资叙事中最有吸引力的词。</p>",
    "contentTruncated": true,
    "voteupCount": 2,
    "commentCount": 1,
    "authorName": "薛洪言",
    "authorHeadline": "星图金融研究院常务副院长、硕士生导师",
    "authorId": "4d4d42d4d525a7f8f2c74925fb5f8db7",
    "authorUrl": "https://www.zhihu.com/people/xuehongyan",
    "columnSlug": "xuehy",
    "imageUrl": "https://picx.zhimg.com/v2-9349de0aae106773e9470667658217a3_720w.jpg",
    "url": "https://zhuanlan.zhihu.com/p/2084570146228664180",
    "createdAt": "2026-09-19T01:18:22.000Z",
    "updatedAt": "2026-09-19T01:18:22.000Z",
    "topics": [],
    "source": "column"
}
```

The same id fetched as an article (not via the column list) returned `content` of 769 characters, `excerpt` of 128 characters, and topics `A股（人民币普通股票）` and `股票交易策略`. Author, votes and comment count matched.

***

### Key Output Fields

**Identity**

- `articleId`, `url`, `entityType` (`article`), `source` (`column` or `article`)
- `columnSlug` when the article belongs to a column

**Text**

- `title`, `excerpt`, `content` (HTML)
- `contentTruncated` — Zhihu's guest flag. A `true` value can still sit on a multi-thousand-character column body.

**Author**

- `authorName`, `authorHeadline`, `authorId`, `authorUrl`

**Engagement**

- `voteupCount`, `commentCount`
- `topics` — filled on article-detail rows when Zhihu sends them. Column-list rows in the sample above had an empty list.

**Media and time**

- `imageUrl`
- `createdAt`, `updatedAt` — ISO timestamps

***

### FAQ

**Does it search Zhihu?**
No. Search, questions and answers ask for a login. Those URLs are skipped.

**What does a column run actually request?**
`GET https://www.zhihu.com/api/v4/columns/{slug}/items?limit=10&offset=0`, then the next offset, until `maxItems` or Zhihu says the list is finished. Ten articles per page. Pages run one after another.

**What does an article run request?**
`GET https://www.zhihu.com/api/v4/articles/{id}` with the public fields `content`, `excerpt`, `voteup_count`, `comment_count`, `author`, `topics` and `column`. The request is signed as a guest. No `d_c0` cookie is sent. Sending that cookie is what flips Zhihu into its click-challenge, so the actor leaves it off.

**Can I mix a column and an article id?**
Yes. The column is walked first. If that article id already appeared in the column, it is not fetched again.

**What if the slug or id is wrong?**
A failed column page or article is logged and counted as a failure. The rest of the run continues. A bad id does not produce a billed row, because a row is only pushed after a title comes back.

**Is there a free-plan cap?**
On Apify, accounts that are not on a paid plan are limited to 100 articles per run. The Store's automated tester is limited to 1.

**How many retries?**
Each URL is tried up to 4 times, with a new proxy address each time. A login wall (`40353`) or an abnormal-request block (`40362`) stops that URL immediately. Those are not transient proxy errors.

**Why is the article HTML shorter than the column HTML?**
They are different Zhihu endpoints. For article `2084570146228664180` the column list returned 3,015 characters and the article endpoint returned 769. Use a column URL when you want the longer public HTML.

**What does `contentTruncated: true` mean?**
Zhihu sets that flag on guest responses. It does not mean the field is empty. Paid 盐选 bodies that Zhihu does not show to guests stay on the excerpt.

**Will a question id work if I paste the number?**
A long numeric id is treated as an article id. Question URLs are rejected before any request.

**How much does a run cost?**
$0.005 when the run starts, then $0.003 for each article row. Five articles cost about $0.02.

***

### Support

- Bugs and field requests: the actor's **Issues** tab at [memo23/zhihu-scraper](https://apify.com/memo23/zhihu-scraper).
- More actors from the same developer: [apify.com/memo23](https://apify.com/memo23).

***

### Additional Services

Need a scheduled column feed, a different field set, or a push into a sheet or warehouse? Custom work is available through the Issues tab.

***

### Explore More Scrapers

Same developer, same pay-per-result model:

- [Quora Scraper](https://apify.com/memo23/quora-scraper) — public Quora questions and answers
- [Douyin Scraper](https://apify.com/memo23/douyin-scraper) — public Douyin posts
- The rest of the catalogue is on the [memo23 Store page](https://apify.com/memo23)

***

### 🤖 For AI Agents & LLM Apps

Compact reference for agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (`memo23/zhihu-scraper`).

**Purpose:** Public Zhihu column articles, or one article by id. No search, no Q\&A, no login.

**Minimal input:**

```json
{ "startUrls": ["https://zhuanlan.zhihu.com/xuehy"], "maxItems": 5 }
```

**Output fields:** entityType, articleId, title, excerpt, content, contentTruncated, voteupCount, commentCount, authorName, authorHeadline, authorId, authorUrl, columnSlug, imageUrl, url, createdAt, updatedAt, topics, source.

**Billing:** $0.005 actor start + $0.003 per dataset row. Skipped question/search URLs are not billed.

**Behavior:** Column rows have the longer HTML and often empty `topics`. Article-detail rows are shorter and may include topics. `contentTruncated` can be true on a full-looking column body. Do not send question URLs.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Zhihu Inc. or any of its subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available Zhihu column and article pages. It does not log in, and it does not collect search results, questions, answers, or content Zhihu only shows to a signed-in member. Users are responsible for ensuring their use complies with Zhihu's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.

***

### SEO Keywords

Zhihu scraper, scrape Zhihu, Apify Zhihu, zhuanlan articles, zhuanlan.zhihu.com scraper, column article export, Chinese essay archive, author monitoring, public HTML export, media monitoring, China commentary data, guest article body, column feed, article JSON, upvote export, research export, column archive, comment count export

# Actor input Schema

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

Zhihu URLs to collect. Column: https://zhuanlan.zhihu.com/xuehy or https://www.zhihu.com/column/xuehy. Article: https://zhuanlan.zhihu.com/p/2084570146228664180. You can mix both in one run. Question and search URLs are ignored. Default if empty: the xuehy column.

## `columnIds` (type: `array`):

Column slugs without the URL, same result as a column URL. Example: xuehy. Letters, numbers, underscore and hyphen only. Leave empty if you already passed URLs in startUrls.

## `articleIds` (type: `array`):

Numeric article ids, the number after /p/ in an article URL. Example: 2084570146228664180. Each id becomes one dataset row from the article endpoint. Leave empty if you already passed article URLs.

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

Stop after this many articles. Example: 20. Default: 20. Minimum: 1. Free plans are capped at 100. A column can hold hundreds of posts, so set this before a large run.

## `maxConcurrency` (type: `integer`):

How many article-detail requests run at once. Example: 5. Default: 5. Minimum: 1. Maximum: 20. Column pages are fetched one after another.

## Actor input object example

```json
{
  "startUrls": [
    "https://zhuanlan.zhihu.com/xuehy"
  ],
  "maxItems": 20,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

No description

# 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 = {
    "startUrls": [
        "https://zhuanlan.zhihu.com/xuehy"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/zhihu-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 = { "startUrls": ["https://zhuanlan.zhihu.com/xuehy"] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/zhihu-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 '{
  "startUrls": [
    "https://zhuanlan.zhihu.com/xuehy"
  ]
}' |
apify call memo23/zhihu-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/zhihu-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/3N6Zpz9h3tV3yGZSD/builds/K3qxk3m4JWR34XLW4/openapi.json
