<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kovu.cn Notes]]></title><description><![CDATA[Notes about rational life choices, cost-effective living and personal thinking. Original articles are hosted on kovu.cn.]]></description><link>https://juto.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 14:47:30 GMT</lastBuildDate><atom:link href="https://juto.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Ethical Web Scraping: Engineering Guardrails and Legal Boundaries Developers Must Know]]></title><description><![CDATA[How to build compliant data pipelines without crossing criminal thresholds]]></description><link>https://juto.hashnode.dev/ethical-web-scraping-legal-boundaries-engineering-guardrails</link><guid isPermaLink="true">https://juto.hashnode.dev/ethical-web-scraping-legal-boundaries-engineering-guardrails</guid><dc:creator><![CDATA[juto]]></dc:creator><pubDate>Sun, 01 Nov 2026 01:25:57 GMT</pubDate><content:encoded><![CDATA[<h2>Introduction</h2>
<p>For data engineers and backend developers, writing a web scraper is often trivial. Modern frameworks like Playwright, Scrapy, and Puppeteer make extracting data from the web easier than ever. However, the real challenge in automated data collection is rarely technical—it is legal and ethical. </p>
<p>The line between "automated browsing" and "illegal intrusion into computer information systems" is razor-thin. Understanding where this line sits—and building code-level guardrails to avoid crossing it—is essential for keeping yourself and your organization out of legal jeopardy.</p>
<h2>1. The Technical Red Lines: Auth, Tokens, and Signatures</h2>
<p>From a technical standpoint, bypassing anti-scraping mechanisms might feel like solving an interesting puzzle. From a legal standpoint, it is often classified as unauthorized access. </p>
<p>To maintain strict compliance, your scraping architecture must adhere to a simple golden rule: <strong>Only scrape publicly accessible pages that require zero authentication.</strong></p>
<ul>
<li><strong>Respect <code>robots.txt</code>:</strong> Before dispatching a single HTTP request, programmatically parse the target domain's <code>robots.txt</code> file and API terms of service.</li>
<li><strong>Never Bypass Access Controls:</strong> If data sits behind a login wall, requires a temporary session <code>token</code>, or relies on reverse-engineering cryptographic API signatures to access, <strong>do not touch it</strong>.</li>
</ul>
<p>Bypassing technical protection measures to extract system data carries severe criminal penalties in many jurisdictions, often resulting in up to 3 years of imprisonment for standard violations, and 3 to 7 years for particularly severe breaches or financial damages.</p>
<h2>2. Building a Compliant Scraper Wrapper</h2>
<p>Instead of relying solely on developer discipline, ethical constraints should be baked directly into your scraping pipeline. Below is a basic Python pattern using <code>urllib.robotparser</code> to enforce <code>robots.txt</code> compliance automatically before fetching any URL:</p>
<pre><code class="language-python">import urllib.robotparser
from urllib.parse import urlparse
import requests

class CompliantScraper:
    def __init__(self, user_agent="EthicalBot/1.0"):
        self.user_agent = user_agent
        self.parsers = {}

    def can_fetch(self, url: str) -&gt; bool:
        parsed_uri = urlparse(url)
        base_url = f"{parsed_uri.scheme}://{parsed_uri.netloc}"
        robots_url = f"{base_url}/robots.txt"

        if base_url not in self.parsers:
            rp = urllib.robotparser.RobotFileParser()
            rp.set_url(robots_url)
            try:
                rp.read()
                self.parsers[base_url] = rp
            except Exception:
                # Fail-safe: if robots.txt cannot be read, abort fetching
                return False

        return self.parsers[base_url].can_fetch(self.user_agent, url)

    def fetch_public_page(self, url: str):
        if not self.can_fetch(url):
            raise PermissionError(f"Scraping disallowed by robots.txt for: {url}")
        
        # Never attach forged tokens, session cookies, or cracked signatures here
        headers = {"User-Agent": self.user_agent}
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        return response.text
</code></pre>
<h2>3. The PII Trap: Quantitative Thresholds and Liability</h2>
<p>Even on completely public pages, scraping Personally Identifiable Information (PII) introduces massive legal exposure. Global privacy frameworks (like GDPR) and strict regional criminal codes treat personal data acquisition and distribution with zero tolerance.</p>
<p>Many developers mistakenly believe that small-scale scraping flies under the radar. In reality, statutory thresholds for criminal prosecution can be shockingly low. For instance, when reviewing strict statutory benchmarks—such as the <a href="https://kovu.cn/e/11-4/">quantitative compliance thresholds for web scraping</a> documented under Chinese judicial interpretations—crossing into "severe criminal liability" requires hitting just <strong>one</strong> of the following metrics:</p>
<ul>
<li><strong>50+ records</strong> of highly sensitive data (e.g., physical location tracking, communication content, credit history, or financial assets).</li>
<li><strong>500+ records</strong> of moderately sensitive data (e.g., accommodation history, health records, communication logs, or transaction details).</li>
<li><strong>5,000+ records</strong> of general personal information.</li>
<li>Generating roughly <strong>$700 USD (5,000 RMB)</strong> or more in illicit revenue from the scraped data.</li>
</ul>
<h2>4. Never Sell or Distribute Scraped Data</h2>
<p>The final pillar of compliant data engineering concerns data lifecycle management: <strong>Data you scrape must never be sold or transferred to third parties.</strong></p>
<p>Crucially, legal frameworks often define "illegally providing personal information" broadly. Even if your company collected user data <em>legally</em> with initial consent, exporting a database table of 5,000 user records and handing it over to an external business partner without explicit, renewed user consent falls under the exact same criminal provisions as selling stolen data on the dark web. </p>
<p>Furthermore, scraping B2B commercial data—such as extracting merchant and order volumes from food delivery or e-commerce platforms in a way that causes financial loss or disrupts server operations—regularly leads to corporate prosecution for illegally obtaining computer information system data.</p>
<h2>Conclusion</h2>
<p>Technical capability does not equal legal permission. Often, protected data is technically effortless to grab, and restraint relies entirely on engineering discipline. By restricting crawlers strictly to unauthenticated public pages, honoring <code>robots.txt</code>, stripping out all PII during ingestion, and keeping datasets strictly internal, developers can leverage web data safely without risking their freedom.</p>
]]></content:encoded></item><item><title><![CDATA[The 15-Degree Solar Algorithm: How China's 24 Solar Terms Drive Traditional Timekeeping & Ganzhi Calendars]]></title><description><![CDATA[An algorithmic look at ecliptic solar longitude, seasonal nodes, and the astronomical rules behind traditional Chinese calendar systems.]]></description><link>https://juto.hashnode.dev/the-15-degree-solar-algorithm-how-china-s-24-solar-terms-drive-traditional-timekeeping-ganzhi-calendars</link><guid isPermaLink="true">https://juto.hashnode.dev/the-15-degree-solar-algorithm-how-china-s-24-solar-terms-drive-traditional-timekeeping-ganzhi-calendars</guid><dc:creator><![CDATA[juto]]></dc:creator><pubDate>Sun, 01 Nov 2026 01:08:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae4bb1b1ed41bab66dc1945/5e4e010b-118f-483b-bb56-13ea2a50fa61.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A common misconception—even among many native speakers and software developers working on calendar localization—is that China's <a href="https://www.juto.com/jieqi/"><strong>24 Solar Terms (<em>Èrshísì Jiéqì</em>, 二十四节气)</strong></a> belong to the lunar calendar.</p>
<p>In fact, the exact opposite is true: <strong>the 24 Solar Terms form a rigorous, pure solar calendar system based entirely on astronomical coordinates.</strong></p>
<p>If you check a modern Gregorian calendar, a clear deterministic pattern emerges. While moon-based festivals shift by weeks every year, <em>Chunfen</em> (Spring Equinox) consistently lands around <strong>March 20–21</strong>, <em>Qingming</em> falls between <strong>April 4–6</strong>, and <em>Dongzhi</em> (Winter Solstice) stays anchored between <strong>December 21–23</strong>, varying by no more than a day or two due to leap-year cycles.</p>
<p>Why are they so stable on the Gregorian calendar? Because both systems measure Earth's orbit around the Sun. Inscribed on the UNESCO Intangible Cultural Heritage list in 2016, the 24 Solar Terms are not only an ancient agricultural guide, but also the exact astronomical engine behind traditional Chinese timekeeping systems.</p>
<hr />
<h2>1. The 15-Degree Ecliptic Algorithm</h2>
<p>From an astronomical and computational perspective, Earth orbits the Sun in a 360° circle along the <strong>ecliptic</strong> (the apparent path of the Sun across the celestial sphere). Ancient Chinese astronomers divided this 360° path into 24 equal segments based on <strong>apparent geocentric solar longitude (</strong><code>λ</code><strong>)</strong>:</p>
<ul>
<li><strong>Origin (</strong><code>λ = 0°</code><strong>):</strong> Defined precisely at the Vernal Equinox (<em>Chunfen</em>, 春分).</li>
<li><strong>Step Interval (</strong><code>Δλ = 15°</code><strong>):</strong> Every time the Sun travels 15° along the ecliptic (<code>360° ÷ 24 = 15°</code>), a new Solar Term is triggered at that exact astronomical timestamp.</li>
<li><strong>Orbital Velocity Variance:</strong> Because Earth's orbit is slightly elliptical (Kepler's Second Law), Earth moves faster near perihelion (winter in the Northern Hemisphere) and slower near aphelion (summer). Consequently, the spacing between two adjacent Solar Terms takes roughly <strong>14.7 days in winter</strong> and up to <strong>15.7 days in summer</strong>.</li>
</ul>
<p>In modern calendar engineering, mapping a continuous solar longitude reading to its active Solar Term index can be modeled cleanly:</p>
<pre><code class="language-javascript">// Map apparent solar longitude (0° to 360°) to the 24 Solar Terms
const SOLAR_TERMS = [
  "Chunfen (0°)", "Qingming (15°)", "Guyu (30°)", "Lixia (45°)",
  "Xiaoman (60°)", "Mangzhong (75°)", "Xiazhi (90°)", "Xiaoshu (105°)",
  "Dashu (120°)", "Liqiu (135°)", "Chushu (150°)", "Bailu (165°)",
  "Qiufen (180°)", "Hanlu (195°)", "Shuangjiang (210°)", "Lidong (225°)",
  "Xiaoxue (240°)", "Daxue (255°)", "Dongzhi (270°)", "Xiaohan (285°)",
  "Dahan (300°)", "Lichun (315°)", "Yushui (330°)", "Jingzhe (345°)"
];

function getCurrentSolarTerm(solarLongitude) {
  const normalized = ((solarLongitude % 360) + 360) % 360;
  const index = Math.floor(normalized / 15);
  return {
    index,
    name: SOLAR_TERMS[index],
    isBoundaryNode: index % 2 === 1 // Odd indices represent the 12 'Jie' (Month Boundaries)
  };
}
</code></pre>
<hr />
<h2>2. <em>Jié</em> (Nodes) vs. <em>Qì</em> (Mid-Terms): The Dual Structure</h2>
<p>Historically, the term <em>Jiéqì</em> (节气) is actually a compound word representing two alternating categories along the 360° ecliptic:</p>
<ol>
<li><strong>12 <em>Jié</em> (节, Sectional Nodes):</strong> Placed at odd multiples of 15° from the equinoxes (<code>315°</code>, <code>345°</code>, <code>15°</code>, <code>45°</code>, etc.). These act as strict <strong>month boundaries</strong> in solar-based Chinese timekeeping.</li>
<li><strong>12 <em>Qì</em> or <em>Zhōngqì</em> (中气, Mid-Terms):</strong> Placed at multiples of 30° (<code>0°</code>, <code>30°</code>, <code>60°</code>, <code>90°</code>, etc.). In the traditional lunisolar calendar, <em>Zhōngqì</em> serve a critical synchronization role: any lunar month that fails to contain a <em>Zhōngqì</em> is designated as an intercalary <strong>leap month (<em>Rùnyuè</em>)</strong>, preventing lunar months from drifting out of sync with the seasons.</li>
</ol>
<table>
<thead>
<tr>
<th align="left">Solar Term</th>
<th align="left">Pinyin &amp; Hanzi</th>
<th align="left">Solar Longitude (<code>λ</code>)</th>
<th align="left">Structural Role</th>
<th align="left">Approx. Gregorian Date</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Start of Spring</strong></td>
<td align="left"><em>Lìchūn</em> (立春)</td>
<td align="left"><code>315°</code></td>
<td align="left"><em>Jié</em> (Month 1 Start)</td>
<td align="left">Feb 3–5</td>
</tr>
<tr>
<td align="left"><strong>Spring Equinox</strong></td>
<td align="left"><em>Chūnfēn</em> (春分)</td>
<td align="left"><code>0°</code></td>
<td align="left"><em>Zhōngqì</em> (Month 2 Mid)</td>
<td align="left">Mar 20–21</td>
</tr>
<tr>
<td align="left"><strong>Pure Brightness</strong></td>
<td align="left"><em>Qīngmíng</em> (清明)</td>
<td align="left"><code>15°</code></td>
<td align="left"><em>Jié</em> (Month 3 Start)</td>
<td align="left">Apr 4–6</td>
</tr>
<tr>
<td align="left"><strong>Start of Summer</strong></td>
<td align="left"><em>Lìxià</em> (立夏)</td>
<td align="left"><code>45°</code></td>
<td align="left"><em>Jié</em> (Month 4 Start)</td>
<td align="left">May 5–7</td>
</tr>
<tr>
<td align="left"><strong>Summer Solstice</strong></td>
<td align="left"><em>Xiàzhì</em> (夏至)</td>
<td align="left"><code>90°</code></td>
<td align="left"><em>Zhōngqì</em> (Month 5 Mid)</td>
<td align="left">Jun 21–22</td>
</tr>
<tr>
<td align="left"><strong>Start of Autumn</strong></td>
<td align="left"><em>Lìqiū</em> (立秋)</td>
<td align="left"><code>135°</code></td>
<td align="left"><em>Jié</em> (Month 7 Start)</td>
<td align="left">Aug 7–9</td>
</tr>
<tr>
<td align="left"><strong>Autumn Equinox</strong></td>
<td align="left"><em>Qiūfēn</em> (秋分)</td>
<td align="left"><code>180°</code></td>
<td align="left"><em>Zhōngqì</em> (Month 8 Mid)</td>
<td align="left">Sep 22–24</td>
</tr>
<tr>
<td align="left"><strong>Start of Winter</strong></td>
<td align="left"><em>Lìdōng</em> (立冬)</td>
<td align="left"><code>225°</code></td>
<td align="left"><em>Jié</em> (Month 10 Start)</td>
<td align="left">Nov 7–8</td>
</tr>
<tr>
<td align="left"><strong>Winter Solstice</strong></td>
<td align="left"><em>Dōngzhì</em> (冬至)</td>
<td align="left"><code>270°</code></td>
<td align="left"><em>Zhōngqì</em> (Month 11 Mid)</td>
<td align="left">Dec 21–23</td>
</tr>
</tbody></table>
<hr />
<h2>3. How Solar Terms Govern the Ganzhi &amp; Four Pillars (<em>Bazi</em>) System</h2>
<p>When implementing traditional Chinese calendar converters—especially those involving the <strong>Sexagenary Cycle (<em>Gānzhī</em>, 干支)</strong> and the <a href="https://www.juto.com/bazi/"><strong>Four Pillars (<em>Bāzì</em>, 八字)</strong></a> timekeeping framework—developers frequently encounter an off-by-one bug if they rely on Lunar New Year dates.</p>
<p>Unlike civil lunar festivals, the <strong>Ganzhi solar calendar</strong> ignores moon phases completely for its year and month transitions:</p>
<h3>The Year Boundary: <em>Lichun</em> (<code>λ = 315°</code>), Not Lunar New Year</h3>
<p>In the Ganzhi stem-branch system, a new year (such as shifting from one zodiac stem-branch pair to the next) does <strong>not</strong> begin on the first day of the first lunar month (<em>Chūnjié</em>). Instead, it switches at the exact astronomical minute and second when the Sun reaches <code>315°</code> <strong>ecliptic longitude (<em>Lìchūn</em>, Start of Spring)</strong> around February 4.</p>
<h3>The Month Boundaries: Strict Alignment with the 12 <em>Jié</em></h3>
<p>Similarly, the 12 Earthly Branch months (<em>Yín</em>, <em>Mǎo</em>, <em>Chén</em>, etc.) never align with the 1st day of a lunar month:</p>
<ul>
<li><strong>Tiger Month (<em>Yínyuè</em>):</strong> Starts precisely at <em>Lìchūn</em> (<code>315°</code>)</li>
<li><strong>Rabbit Month (<em>Mǎoyuè</em>):</strong> Starts precisely at <em>Jīngzhé</em> (<code>345°</code>)</li>
<li><strong>Dragon Month (<em>Chényuè</em>):</strong> Starts precisely at <em>Qīngmíng</em> (<code>15°</code>)</li>
</ul>
<p>Because solar longitude crossings are calculated down to the exact minute (UTC+8 / True Solar Time), two timestamps on the exact same civil date—one ten minutes before <em>Lìchūn</em> and one ten minutes after—belong to entirely different Ganzhi years and months.</p>
<hr />
<h2>Conclusion</h2>
<p>Far from being folklore tied to moon phases, China's <strong>24 Solar Terms</strong> represent an elegant, continuous astronomical coordinate system. By tracking the Sun's 360° journey in 15° increments, ancient astronomers created a solar clock capable of anchoring agriculture, synchronizing lunisolar leap months, and driving precision stem-branch (<em>Ganzhi</em>) timekeeping across millennia.</p>
]]></content:encoded></item><item><title><![CDATA[Is a Degree Losing Its Value? Check the Distribution Before You Decide]]></title><description><![CDATA[Many people argue whether degrees are devalued. Average data misleads you; your position inside the distribution matters more.]]></description><link>https://juto.hashnode.dev/is-a-degree-losing-its-value-check-the-distribution-before-you-decide</link><guid isPermaLink="true">https://juto.hashnode.dev/is-a-degree-losing-its-value-check-the-distribution-before-you-decide</guid><dc:creator><![CDATA[juto]]></dc:creator><pubDate>Fri, 30 Oct 2026 16:02:56 GMT</pubDate><content:encoded><![CDATA[<p><em>A census table from China is a useful reminder: most arguments about credentials are arguments about averages, and averages hide the thing you actually need to know.</em></p>
<hr />
<p>Every few months the same argument comes around: degrees are being devalued, bootcamps are the new degrees, or the reverse — credentials still gate everything, and anyone who says otherwise is coping.</p>
<p>Both sides usually reach for the same kind of evidence: the graduate they know who can't find work, the friend without a degree who earns well, the unemployment rate for "recent graduates" as a single headline number.</p>
<p>The problem isn't the quality of those anecdotes. It's that they're answering the wrong question. When you're deciding whether to spend three years and a large sum of money on a credential, what you need isn't a <em>national average</em>. You need a <strong>distribution</strong>, and you need to know where you sit inside it. A statistic that is technically true — "the share of people with degrees is rising" — can be useless for your decision if the people you're actually competing with are a narrow slice of that population.</p>
<p>Here's a concrete case where the distinction becomes obvious.</p>
<h2>What the numbers actually say</h2>
<p>China publishes education data as part of its national census. The seventh census (standard time: 1 November 2020) counted the population's educational attainment in absolute terms and per 100,000 people.</p>
<p>The headline figures for the 2020 census:</p>
<table>
<thead>
<tr>
<th>Educational attainment</th>
<th>Per 100,000 people (2020)</th>
<th>Per 100,000 people (2010)</th>
</tr>
</thead>
<tbody><tr>
<td>College and above</td>
<td>15,467</td>
<td>8,930</td>
</tr>
<tr>
<td>High school / vocational</td>
<td>15,088</td>
<td>14,032</td>
</tr>
<tr>
<td>Middle school</td>
<td>34,507</td>
<td>38,788</td>
</tr>
<tr>
<td>Primary school</td>
<td>24,767</td>
<td>26,779</td>
</tr>
</tbody></table>
<p>Three things stand out.</p>
<p>First, the <strong>college share is growing fast</strong> — 8,930 per 100,000 to 15,467 in a decade, an increase of roughly 73%. If your conclusion is "credentials are becoming more common," the data agrees with you immediately.</p>
<p>Second, the <strong>largest single group is still middle school</strong>, not college.</p>
<p>Third, the distribution isn't shifting because more people are becoming highly educated in isolation. It's shifting at the bottom too: average years of schooling for the population aged 15 and over rose from 9.08 to 9.91 years, and the illiteracy rate fell from 4.08% to 2.67%.</p>
<p>A later sample — China's 1% national population survey, standard time 1 November 2025, sample size 20.52 million — projects a college share of roughly 19.4% of the total population, with middle school still around 32.2%. Those shares are computed from the published absolute counts rather than printed directly in the communiqué, which is worth knowing if you're quoting them.</p>
<h2>Why these numbers are easy to misuse — including on purpose</h2>
<p>Now the useful part. If you take "only about 15–19% of the population has a college education" and walk away feeling good about your degree, you've made exactly the mistake this data can't support. Three reasons:</p>
<p><strong>1. Census figures count everyone, including children and the elderly.</strong> The denominator is the whole population, not the labour force, and not the group applying for the jobs you want. Educational attainment among 60-year-olds in 2020 reflects schooling decisions made in the 1970s.</p>
<p><strong>2. Your competition is your cohort, not the country.</strong> The share of people with degrees is rising fastest among younger age groups. China's published communiqués don't break attainment down by age, so any statement about "people my age" is an inference, not a measured figure — but the direction is unambiguous. Compare yourself against the national number and you will systematically overstate your relative advantage.</p>
<p><strong>3. Scarcity is not the same thing as payoff.</strong> A credential can be rare <em>and</em> worthless if nobody is hiring for it. Conversely, a common credential can still be worth getting if it's a hard requirement for a specific job, a licence, or a visa. The distribution tells you about competition. It tells you nothing about the price, which is a separate calculation involving what you'd earn if you worked instead, and what the credential actually changes about your options.</p>
<p>That last point has its own arithmetic, and it's the one most people skip: the honest comparison is <strong>not</strong> "salary with degree vs. no salary." It's "salary with degree, starting later, minus the wages you didn't earn while studying, versus salary without the degree from day one." Once you write it out as a subtraction, most credential debates stop being ideological and become simple questions about break-even years.</p>
<h2>A checklist you can reuse for any credential</h2>
<p>Whether it's a degree, a bootcamp, a certification or a licence, the same four questions apply:</p>
<ol>
<li><strong>Find the distribution, not the average.</strong> What share of the relevant population holds this credential? Published censuses, labour force surveys and industry registries usually have this; press coverage usually doesn't.</li>
<li><strong>Define your comparison group precisely.</strong> Same age band, same region, same field — ideally the people who will be in the room with you at an interview.</li>
<li><strong>Write down the counterfactual.</strong> What would you be doing, and earning, during the time the credential takes? That number is the real price, and it's usually far larger than the tuition.</li>
<li><strong>Check the credential's legal or formal status.</strong> Is it recognised by the body that decides, wherever you intend to work? An unrecognised credential is an expensive hobby.</li>
</ol>
<p>If those four questions come back favourable, the credential is probably worth it. If you can only answer question one with a headline number, you don't yet have enough to decide.</p>
<h2>Where the full dataset lives</h2>
<p>I came across this breakdown in a Chinese-language reference project called <em>高性价比人生指南</em> ("A Cost-Effective Life Guide"), which compiles decision-relevant data like this into short, sourced entries — each one stated as a cost, a payoff and the evidence behind it, with links to the original government publications.</p>
<p>The specific entry on education structure is here: <a href="https://www.kovu.cn/e/23-3/"><strong>China's education structure: only 15,467 per 100,000 have a college education (2020 census; ~19% by 2025 sampling)</strong></a>. It includes the absolute counts from both the 2020 census and the 2025 1% sample, the per-100,000 comparisons with 2010, and — more usefully — an explicit note on the limits of the comparison: that census and sampling figures use different methodologies, that no age breakdown is published, and that the numbers describe scarcity only, not return.</p>
<p>The underlying sources are China's National Bureau of Statistics: the <a href="https://www.stats.gov.cn/sj/tjgb/rkpcgb/qgrkpcgb/202302/t20230206_1902006.html">Seventh National Population Census Communiqué No. 6 (education)</a> and the <a href="https://www.stats.gov.cn/sj/zxfb/202605/t20260522_1963788.html">2025 1% Population Sampling Survey communiqué</a>. Both are in Chinese, but they're machine-translatable and the tables are readable without the prose.</p>
<p>The broader lesson travels well beyond one country: whenever someone tells you a credential is "worth less than it used to be," ask for the distribution, the comparison group, and the counterfactual. If they can't produce them, they're describing their feelings about a chart.</p>
<hr />
<p><em>If you found this useful, the same project covers the study-versus-work calculation, how to check whether a certification is officially recognised, and which skills are hardest to automate — all with sources attached.</em></p>
]]></content:encoded></item></channel></rss>