← 피드로
JSON-LD
A JSON object in a element. Separate from the visible markup, so it survives a redesign. The recommended form, and the one with the mechanical caveat in the next section.</td>
</tr>
<tr>
<td>Microdata</td>
<td>itemscope, itemtype and itemprop attributes on the visible elements themselves. Cannot drift from what is displayed, because it is attached to it. Verbose, and painful to keep correct across a component library.</td>
</tr>
<tr>
<td>RDFa</td>
<td>The same idea with a different attribute set, from the wider linked-data world. Fully supported by the major parsers and rarely chosen for new work.</td>
</tr>
</tbody></table>
<p>Use JSON-LD unless you have a specific reason not to. It is generated from the same variables your template already has, which is the only arrangement in which the markup and the page cannot disagree.</p>
<h2>
<a name=”why-jsonld-is-invisible-to-a-text-extractor” href=”#why-jsonld-is-invisible-to-a-text-extractor” class=”anchor”>
</a>
Why JSON-LD is invisible to a text extractor
</h2>
<p>Here is the mechanism nobody writes down, and it is tier-2 observable in one command.</p>
<p>A structured-data parser looks for <code>script</code> elements of type <code>application/ld+json</code> and reads their contents. An HTML-to-text converter — the kind that sits in the fetch stage of a retrieval pipeline, turning a fetched page into something to chunk — does the opposite: it strips <code>script</code> and <code>style</code> elements first, precisely because their contents are not prose and would poison the text with JavaScript. Your JSON-LD is discarded before the text is ever chunked.</p>
<p>Prove it on your own page in one line, with any HTML-to-text tool:<br>
</p>
<div class=”highlight”><pre class=”highlight shell”><code><span class=”c”># Does the structured data survive text extraction?</span>
curl <span class=”nt”>-sS</span> https://example.com/some-page <span class=”o”>></span> page.html
<span class=”c”># What a structured-data parser sees:</span>
<span class=”nb”>grep</span> <span class=”nt”>-c</span> <span class=”s1″>’application/ld+json'</span> page.html
<span class=”c”># What a text extractor sees (python-readability, trafilatura,</span>
<span class=”c”># lynx -dump, or your own stripper) — search the output for a value</span>
<span class=”c”># that appears ONLY in the JSON-LD:</span>
lynx <span class=”nt”>-dump</span> <span class=”nt”>-nolist</span> page.html | <span class=”nb”>grep</span> <span class=”nt”>-c</span> <span class=”s1″>’a-value-only-in-your-jsonld'</span>
</code></pre></div>
<p></p>
<p>If the second count is zero, the conclusion is not that structured data is useless. It is that <strong>structured data reaches parsers, and visible text reaches extractors, and you should not rely on one to carry a fact the other needs.</strong> Every fact that matters must be in the prose. The markup is a second, machine-typed statement of facts the page already makes.</p>
<p>Microdata behaves differently here, because its values are the visible text — an extractor keeps them by definition. That is not a reason to switch, but it is the reason the rule below is not merely an ethical preference.</p>
<h2>
<a name=”the-rule-that-keeps-markup-honest” href=”#the-rule-that-keeps-markup-honest” class=”anchor”>
</a>
The rule that keeps markup honest
</h2>
<p>One rule, and it makes several other decisions for you: <strong>nothing in the structured data may be a fact the page does not also show a human.</strong> Every value passed to the generator should be the same variable the visible markup renders.</p>
<p>What that rules out, concretely:</p>
<ul>
<li> <strong>An <code>aggregateRating</code> with no reviews on the page.</strong> The most abused property in the vocabulary and the one most likely to earn a manual action.</li>
<li> <strong>A <code>priceValidUntil</code> you have not committed to.</strong> If you cannot honour the date, it is not a fact.</li>
<li> <strong>FAQ markup for questions not on the page.</strong> The guidance is explicit that the question and answer text must both be visible.</li>
<li> <strong>An <code>author</code> nobody wrote.</strong> If the page has no byline, the author is the organisation, and saying so is more credible than inventing a person.</li>
<li> <strong>An <code>image</code> that does not illustrate the article.</strong> Pointing a thousand articles at one site-wide mark is a claim that each illustrates itself; omitting the property is more honest and costs nothing.</li>
</ul>
<p>The rule also gives you an implementation test: if your generator takes an argument that is not already on the page, that argument is the bug.</p>
<h2>
<a name=”the-types-worth-the-effort” href=”#the-types-worth-the-effort” class=”anchor”>
</a>
The types worth the effort
</h2>
<p>These types and properties all exist in the vocabulary and are widely consumed. Pick by what your page is, not by what sounds impressive.</p>
<table><thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead><tbody>
<tr>
<td>Article / TechArticle / BlogPosting</td>
<td>For prose. TechArticle is an Article subtype and is the accurate choice for documentation and technical explainers. Carries headline, description, datePublished, dateModified, author, publisher, inLanguage, timeRequired.</td>
</tr>
<tr>
<td>BreadcrumbList</td>
<td>The trail you already render, as ListItem entries with position and item. Cheap, unambiguous, and it tells a parser your site's shape without it inferring one from your navigation.</td>
</tr>
<tr>
<td>Organization</td>
<td>Your identity, on one page, with sameAs pointing at the profiles that are also you. This is the property that ties your identifiers together and it matters for entity resolution — see the page on false claims about your company.</td>
</tr>
<tr>
<td>WebSite</td>
<td>Site-level name and URL, usually with the Article's isPartOf pointing at it.</td>
</tr>
<tr>
<td>CollectionPage + ItemList</td>
<td>For a hub that lists other pages. The ItemList should be the same list in the same order the page renders, which is the point of it.</td>
</tr>
<tr>
<td>Product / Offer</td>
<td>For something purchasable, with price and priceCurrency. Only where the price is on the page and current.</td>
</tr>
<tr>
<td>SoftwareApplication</td>
<td>For a tool or an app: applicationCategory, operatingSystem, and a zero-price Offer where it is genuinely free. Do not describe a calculator as an Article.</td>
</tr>
<tr>
<td>Dataset</td>
<td>For downloadable data, with distribution, license and creator. Under-used, and it is the type most likely to make a data page findable by something that wants data.</td>
</tr>
<tr>
<td>FAQPage / QAPage</td>
<td>Question and Answer entries. Still valid vocabulary and still parsed, but see the section on rich results before assuming it earns a display change.</td>
</tr>
<tr>
<td>citation / isBasedOn / license</td>
<td>Properties rather than types, and the most underused ones here. They state what a page rests on and what may be done with it, in machine-readable form.</td>
</tr>
</tbody></table>
<h2>
<a name=”a-complete-article-block” href=”#a-complete-article-block” class=”anchor”>
</a>
A complete article block
</h2>
<p>Generated from the page’s own fields, with no value invented. The escaping detail in the last line is not optional: a string in your data containing a closing script tag would end the element early, which is the one genuine injection risk in a block like this.<br>
</p>
<div class=”highlight”><pre class=”highlight javascript”><code><span class=”kd”>function</span> <span class=”nf”>articleJsonLd</span><span class=”p”>(</span><span class=”nx”>page</span><span class=”p”>,</span> <span class=”nx”>siteUrl</span><span class=”p”>)</span> <span class=”p”>{</span>
<span class=”kd”>const</span> <span class=”nx”>url</span> <span class=”o”>=</span> <span class=”s2″>`</span><span class=”p”>${</span><span class=”nx”>siteUrl</span><span class=”p”>}</span><span class=”s2″>/learn/</span><span class=”p”>${</span><span class=”nx”>page</span><span class=”p”>.</span><span class=”nx”>slug</span><span class=”p”>}</span><span class=”s2″>`</span><span class=”p”>;</span>
<span class=”k”>return</span> <span class=”p”>{</span>
<span class=”dl”>”</span><span class=”s2″>@context</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>https://schema.org</span><span class=”dl”>”</span><span class=”p”>,</span>
<span class=”dl”>”</span><span class=”s2″>@type</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>TechArticle</span><span class=”dl”>”</span><span class=”p”>,</span>
<span class=”dl”>”</span><span class=”s2″>@id</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”nx”>url</span><span class=”p”>,</span>
<span class=”na”>mainEntityOfPage</span><span class=”p”>:</span> <span class=”p”>{</span> <span class=”dl”>”</span><span class=”s2″>@type</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>WebPage</span><span class=”dl”>”</span><span class=”p”>,</span> <span class=”dl”>”</span><span class=”s2″>@id</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”nx”>url</span> <span class=”p”>},</span>
<span class=”na”>headline</span><span class=”p”>:</span> <span class=”nx”>page</span><span class=”p”>.</span><span class=”nx”>title</span><span class=”p”>,</span> <span class=”c1″>// the visible <h1></span>
<span class=”na”>description</span><span class=”p”>:</span> <span class=”nx”>page</span><span class=”p”>.</span><span class=”nx”>summary</span><span class=”p”>,</span> <span class=”c1″>// the meta description</span>
<span class=”na”>datePublished</span><span class=”p”>:</span> <span class=”nx”>page</span><span class=”p”>.</span><span class=”nx”>published</span><span class=”p”>,</span> <span class=”c1″>// the date the URL began to exist</span>
<span class=”na”>dateModified</span><span class=”p”>:</span> <span class=”nx”>page</span><span class=”p”>.</span><span class=”nx”>updated</span><span class=”p”>,</span> <span class=”c1″>// the visible “updated” line</span>
<span class=”na”>timeRequired</span><span class=”p”>:</span> <span class=”s2″>`PT</span><span class=”p”>${</span><span class=”nx”>page</span><span class=”p”>.</span><span class=”nx”>minutes</span><span class=”p”>}</span><span class=”s2″>M`</span><span class=”p”>,</span> <span class=”c1″>// the visible “N min read”</span>
<span class=”na”>inLanguage</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>en</span><span class=”dl”>”</span><span class=”p”>,</span>
<span class=”na”>author</span><span class=”p”>:</span> <span class=”p”>{</span> <span class=”dl”>”</span><span class=”s2″>@type</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>Organization</span><span class=”dl”>”</span><span class=”p”>,</span> <span class=”na”>name</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>Example</span><span class=”dl”>”</span><span class=”p”>,</span> <span class=”na”>url</span><span class=”p”>:</span> <span class=”nx”>siteUrl</span> <span class=”p”>},</span>
<span class=”na”>publisher</span><span class=”p”>:</span> <span class=”p”>{</span> <span class=”dl”>”</span><span class=”s2″>@type</span><span class=”dl”>”</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>Organization</span><span class=”dl”>”</span><span class=”p”>,</span> <span class=”na”>name</span><span class=”p”>:</span> <span class=”dl”>”</span><span class=”s2″>Example</span><span class=”dl”>”</span><span class=”p”>,</span> <span class=”na”>url</span><span class=”p”>:</span> <span class=”nx”>siteUrl</span> <span class=”p”>},</span>
<span class=”p”>};</span>
<span class=”p”>}</span>
<span class=”c1″>// Rendering it. The escape matters.</span>
<span class=”o”><</span><span class=”nx”>script</span>
<span class=”nx”>type</span><span class=”o”>=</span><span class=”dl”>”</span><span class=”s2″>application/ld+json</span><span class=”dl”>”</span>
<span class=”nx”>dangerouslySetInnerHTML</span><span class=”o”>=</span><span class=”p”>{{</span>
<span class=”na”>__html</span><span class=”p”>:</span> <span class=”nx”>JSON</span><span class=”p”>.</span><span class=”nf”>stringify</span><span class=”p”>(</span><span class=”nx”>data</span><span class=”p”>).</span><span class=”nf”>replace</span><span class=”p”>(</span><span class=”sr”>/</g</span><span class=”p”>,</span> <span class=”dl”>”</span><span class=”se”>\\</span><span class=”s2″>u003c</span><span class=”dl”>”</span><span class=”p”>),</span>
<span class=”p”>}}</span>
<span class=”sr”>/</span><span class=”err”>>
</span></code></pre></div>
<p></p>
<p><code>timeRequired</code> takes an ISO 8601 duration, so “6 min read” is <code>PT6M</code>. <code>datePublished</code> and <code>dateModified</code> take ISO dates or datetimes. Getting the format wrong is the most common validator error and it is silent otherwise.</p>
<h2>
<a name=”what-stopped-producing-rich-results” href=”#what-stopped-producing-rich-results” class=”anchor”>
</a>
What stopped producing rich results
</h2>
<p>Two changes from 2023 are still being sold as tactics and are worth knowing precisely, because they are tier-1 facts you can read in the operator’s own documentation.</p>
<ul>
<li> <strong>FAQ rich results were restricted.</strong> Google announced in August 2023 that the FAQ rich result would generally be shown only for well-known authoritative government and health sites. The markup is still valid and still parsed; the display change is what went away for everyone else.</li>
<li> <strong>HowTo rich results were removed.</strong> Announced in the same change, limited and then dropped as a search feature.</li>
</ul>
<p>The lesson generalises past those two: <strong>a display feature is a product decision and can be withdrawn, while the vocabulary is a standard and does not disappear.</strong> Mark up your page because the markup is a true, structured statement of what the page contains. If you are marking it up for a specific visual treatment, you have bought an asset with an unannounced expiry date.</p>
<h2>
<a name=”validating-it-and-testing-extraction” href=”#validating-it-and-testing-extraction” class=”anchor”>
</a>
Validating it, and testing extraction
</h2>
<ol>
<li> <strong>Validate the vocabulary.</strong> The Schema Markup Validator at <code>validator.schema.org</code> checks your JSON against schema.org itself, with no opinion about any search product. This is the one that tells you whether your types and properties are real.</li>
<li> <strong>Check the search-product view.</strong> Google’s Rich Results Test tells you whether a specific feature is eligible, which is a narrower and more volatile question.</li>
<li> <strong>Validate the rendered page, not the source.</strong> If your markup is injected client-side, paste the URL rather than the source — or better, move the block to the server response, because a non-rendering fetcher will never run the injection at all. See <a href=”https://multigrid.ai/learn/javascript-seo-ai”>JavaScript rendering and what crawlers see</a>.</li>
<li> <strong>Diff the markup against the page.</strong> Take every value in the JSON and confirm the string appears in the visible text. A twenty-line script that does this in CI is the cheapest possible defence against the whole class of dishonest-markup problems.</li>
<li> <strong>Run the extraction test</strong> from the second section, so you know which of your facts survive to the text a chunker sees.</li>
</ol>
<h2>
<a name=”what-structured-data-does-not-do” href=”#what-structured-data-does-not-do” class=”anchor”>
</a>
What structured data does not do
</h2>
<p>It is not a ranking factor in the sense people mean, and no assistant operator documents using it to choose sources. Claims that adding JSON-LD raises your citation rate are tier 3 under the scale in <a href=”https://multigrid.ai/learn/generative-engine-optimization”>what is real and what is sold</a> — unverifiable at your scale and unsupported by any public documentation.</p>
<p>What it does do is cheap, real and independent of that question: it removes ambiguity for parsers that read it, it makes your dates, authorship and identity machine-readable, and it forces you to write down what your page actually claims to be. That is worth an afternoon. It is not worth a retainer.</p>
<h2>
<a name=”related” href=”#related” class=”anchor”>
</a>
Related
</h2>
<ul>
<li> <a href=”https://multigrid.ai/learn/retrieval-friendly-site”>Retrieval-Friendly Site Architecture</a></li>
<li> <a href=”https://multigrid.ai/learn/eeat-technical-content”>E-E-A-T for Technical Content, Concretely</a></li>
</ul>
추출 본문 · 출처: dev.to · https://dev.to/multigrid/structured-data-that-machines-read-366j
답글 남기기
댓글을 달기 위해서는 로그인해야합니다.