rsshub
Version:
Make RSS Great Again!
95 lines (94 loc) • 3.93 kB
JavaScript
import { t as getPlaywrightPage } from "./playwright-CFVkq6JF.mjs";
import { t as getSignedHeaders } from "./sign-JzOJfAzW.mjs";
//#region lib/routes/zhihu/browser.ts
const origin = "https://www.zhihu.com";
const allowedResources = /* @__PURE__ */ new Set([
"document",
"script",
"xhr",
"fetch"
]);
const parseApiResponse = (body) => {
try {
return JSON.parse(body);
} catch {
throw new Error("zhihu: browser API request did not return JSON");
}
};
const createBrowserClient = async (url, configuredCookies, initialApiPath) => {
const { page, context, destroy } = await getPlaywrightPage(url, {
noGoto: true,
closeTimeout: 0
});
let phase = "cookie setup";
try {
const cookies = configuredCookies.split(";").map((pair) => pair.trim()).filter((pair) => pair.indexOf("=") > 0).map((pair) => {
const separator = pair.indexOf("=");
return {
name: pair.slice(0, separator),
value: pair.slice(separator + 1),
domain: ".zhihu.com",
path: "/"
};
});
if (cookies.length) await context.addCookies(cookies);
await page.route("**/*", (route) => allowedResources.has(route.request().resourceType()) ? route.continue() : route.abort());
if (cookies.every((cookie) => cookie.name !== "d_c0" || !cookie.value)) {
phase = "guest cookie initialization";
await page.goto(`${origin}/explore`, { waitUntil: "domcontentloaded" });
if ((await context.cookies(origin)).every((cookie) => cookie.name !== "d_c0" || !cookie.value)) throw new Error("The browser did not receive a guest d_c0 cookie");
}
phase = "page navigation";
const response = await page.goto(initialApiPath ? `${origin}${initialApiPath}` : url, { waitUntil: "domcontentloaded" });
const isJson = response?.headers()["content-type"]?.includes("application/json");
if (!isJson) {
phase = "session cookie wait";
await page.waitForFunction(() => document.cookie.split(";").some((pair) => pair.trimStart().startsWith("__zse_ck=")), void 0, { timeout: 1e4 });
}
phase = "session cookie validation";
const browserCookies = await context.cookies(origin);
if ((isJson ? ["d_c0"] : ["d_c0", "__zse_ck"]).some((name) => browserCookies.every((cookie) => cookie.name !== name || !cookie.value))) throw new Error("The browser did not receive the required cookies");
let initialResponse = initialApiPath && isJson && response && response.status() >= 200 && response.status() < 300 ? {
apiPath: initialApiPath,
body: await response.text()
} : void 0;
return {
get: async (apiPath) => {
if (initialResponse?.apiPath === apiPath) {
const { body } = initialResponse;
initialResponse = void 0;
return parseApiResponse(body);
}
if (new URL(page.url()).origin !== origin) await page.goto(`${origin}/explore`, { waitUntil: "domcontentloaded" });
const dc0 = (await context.cookies(origin)).find((cookie) => cookie.name === "d_c0")?.value;
if (!dc0) throw new Error("zhihu: the browser session lost its d_c0 cookie");
const result = await page.evaluate(async ({ apiUrl, headers }) => {
const response = await fetch(apiUrl, {
headers,
credentials: "include",
signal: window.AbortSignal.timeout(3e4)
});
return {
status: response.status,
body: await response.text()
};
}, {
apiUrl: `${origin}${apiPath}`,
headers: getSignedHeaders(apiPath, dc0)
});
if (result.status < 200 || result.status >= 300) throw new Error(`zhihu: browser API request failed with HTTP ${result.status}`);
return parseApiResponse(result.body);
},
getPage: async () => {
await page.goto(url, { waitUntil: "domcontentloaded" });
return page.content();
},
close: destroy
};
} catch {
await destroy();
throw new Error(`zhihu: browser session failed during ${phase}. Configure ZHIHU_COOKIES with an accessible session containing d_c0 and __zse_ck.`);
}
};
//#endregion
export { createBrowserClient };