<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged web-scraping</title>
<link>https://coderlegion.com/tag/web-scraping</link>
<description>Powered by Question2Answer</description>
<item>
<title>The Waiting Problem Behind Most Flaky Suites</title>
<link>https://coderlegion.com/25774/the-waiting-problem-behind-most-flaky-suites</link>
<description>&lt;p&gt;Most of us have had a test suite that passed locally, failed once in CI, then passed on the rerun until nobody trusted it as evidence of anything. This is about why that happens, and why one framework's default behavior removes most of it.&lt;/p&gt;
&lt;h3 id=&quot;flake-is-usually-not-a-bug&quot;&gt;Flake Is Usually Not A Bug&lt;/h3&gt;
&lt;p&gt;A failing browser test is rarely finding something real. It is acting on a page that has not finished arriving. The markup is there, the element is not interactive yet, and the click lands on nothing.&lt;/p&gt;
&lt;p&gt;The usual patch is a fixed sleep. Long enough to be safe and the suite crawls, short enough to be fast and it is flaky again the first time CI has a slow day. Retries hide the problem rather than fix it, which is worse, because now a real failure looks like noise.&lt;/p&gt;
&lt;h3 id=&quot;auto-waiting-as-a-default&quot;&gt;Auto-Waiting As A Default&lt;/h3&gt;
&lt;p&gt;Playwright makes the wait part of the action instead of a line you remembered to write. Before it clicks, it checks that the element is attached to the DOM, visible, stable and enabled. Suites converted from older tools normally lose their sleep statements outright.&lt;/p&gt;
&lt;p&gt;It is also why the same library gets used well outside testing, for scraping, PDF generation and screenshot pipelines, where the timing problem is identical and nobody writes assertions.&lt;/p&gt;
&lt;h3 id=&quot;no-translator-in-the-middle&quot;&gt;No Translator In The Middle&lt;/h3&gt;
&lt;p&gt;WebDriver based tools put a separate process between your script and the browser. Playwright speaks the browser's own debugging protocol, the Chrome DevTools Protocol on Chromium, with Firefox and WebKit implementations maintained by the same team.&lt;/p&gt;
&lt;p&gt;The commonly quoted two to five times speedup over an equivalent Selenium suite comes mostly out of that removed hop, and it widens as the suite grows. The more interesting consequence is that network interception, request mocking and multi-page control behave like first class features rather than bolt-ons.&lt;/p&gt;
&lt;h3 id=&quot;isolation-removes-the-second-kind-of-flake&quot;&gt;Isolation Removes The Second Kind Of Flake&lt;/h3&gt;
&lt;p&gt;Every test runs in its own browser context, effectively a fresh incognito profile with its own cookies and storage, but cheap enough to create per test. Login state stops leaking between tests and execution order stops mattering, which kills a whole class of failure that has nothing to do with timing.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;Benchmark the thing that actually costs you time. Not how fast a green suite runs, but how many reruns you needed last month before you believed a result. A fuller breakdown, selectors, the Python, Java and C# bindings and the honest comparisons against Selenium and Puppeteer, is in &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.webbrowserbot.com/playwright/&quot;&gt;this Playwright guide&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25774/the-waiting-problem-behind-most-flaky-suites</guid>
<pubDate>Sat, 29 Aug 2026 21:40:45 +0000</pubDate>
</item>
<item>
<title>How Browser Agents Read A Page, And When A Script Is Still Better</title>
<link>https://coderlegion.com/25772/how-browser-agents-read-a-page-and-when-a-script-is-still-better</link>
<description>&lt;p&gt;Most of us here have written a scraper or a browser test suite that worked beautifully until someone shipped a redesign and every selector went stale overnight. This is a look at what actually changes when an agent reads the page instead of replaying a recorded path, and where that tradeoff stops paying for itself.&lt;/p&gt;
&lt;h3 id=&quot;why-recorded-steps-break&quot;&gt;Why Recorded Steps Break&lt;/h3&gt;
&lt;p&gt;Traditional web automation targets elements by a fixed path. A class name, an xpath, a nth-child chain. That works right up until the markup moves, and then the script is not wrong in an obvious way, it is confidently clicking nothing. Anyone who has maintained a suite across a design refresh knows the failure is rarely a crash. It is a green run that tested an empty page.&lt;/p&gt;
&lt;p&gt;An agent driving the same browser reasons about the page as it exists at that moment and finds the element by its purpose rather than its address. The login button is whatever currently behaves like the login button. That is the entire difference, and it is why the same agent can complete a task on a site it has never seen.&lt;/p&gt;
&lt;h3 id=&quot;three-ways-an-agent-perceives-a-page&quot;&gt;Three Ways An Agent Perceives A Page&lt;/h3&gt;
&lt;p&gt;Under the surface there are only a few representations in play, and most tool differences come down to how they mix them.&lt;/p&gt;
&lt;p&gt;The DOM gives precise targeting and full text, and it drowns you in presentational noise while hiding the meaning that only layout carries. The accessibility tree, the semantic view browsers already build for screen readers, strips the clutter and labels each element by role and purpose, which usually makes it the cleanest input a model can get. And screenshots with numbered overlays on the interactive elements let a vision model say element 7 instead of describing a position in prose.&lt;/p&gt;
&lt;p&gt;The setups that hold up in production combine them. Structure for exact targeting, the visual pass for understanding and disambiguation. A deeper breakdown of how agents control and perceive a browser is in this &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.autolearningagents.com/ai-browser-automation/&quot;&gt;guide to AI browser automation&lt;/a&gt;, including where the frameworks and protocols underneath fit.&lt;/p&gt;
&lt;h3 id=&quot;the-part-that-still-bites&quot;&gt;The Part That Still Bites&lt;/h3&gt;
&lt;p&gt;Perception does not save you from timing. Most of the modern web arrives nearly empty and fills itself in after running scripts, so an agent that reads too early acts on a page that does not exist yet. It sees a skeleton, decides confidently, and clicks a placeholder. This is the same wait-for-content problem the testing world has fought for a decade, and adding a language model does not remove it. It just makes the wrong answer more fluent.&lt;/p&gt;
&lt;h3 id=&quot;when-a-plain-script-still-wins&quot;&gt;When A Plain Script Still Wins&lt;/h3&gt;
&lt;p&gt;The honest limit is cost and determinism. An agent run costs model tokens and takes longer, and it can make a different choice on two identical pages. If a clean API exists, call it. If the target is one stable page you control, a plain script is cheaper, faster and easier to debug at 3am.&lt;/p&gt;
&lt;p&gt;Browser agents earn their keep on the messy end: many sites with different layouts, no programmatic interface, and a task description that survives a redesign better than a selector does. That is a real category of work, and it is worth reaching for exactly there rather than everywhere.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25772/how-browser-agents-read-a-page-and-when-a-script-is-still-better</guid>
<pubDate>Sat, 29 Aug 2026 17:44:46 +0000</pubDate>
</item>
<item>
<title>Choosing Between A Social Platform API And Direct Collection</title>
<link>https://coderlegion.com/25157/choosing-between-a-social-platform-api-and-direct-collection</link>
<description>&lt;p&gt;Most of the social data questions on here end up in the same place: someone needs posts or comments from a platform, the official API gives them two thirds of what they need, and they are trying to work out whether filling the gap is worth it. This is the decision framework I use before writing any collection code.&lt;/p&gt;
&lt;h3 id=&quot;what-the-official-api-actually-gives-you&quot;&gt;What The Official API Actually Gives You&lt;/h3&gt;
&lt;p&gt;An API gives you a documented schema, stable field names, and a rate limit you can plan around. That last part matters more than people expect, because a documented limit is a budget you can design a queue against instead of a mystery you discover in production.&lt;/p&gt;
&lt;p&gt;What it does not give you is coverage. Platforms have narrowed their public endpoints repeatedly, and the fields most projects actually want, full comment trees, historical posts past a window, engagement counts on other people's content, are frequently the ones that got removed. So the API is the right default and a poor complete answer.&lt;/p&gt;
&lt;h3 id=&quot;where-direct-collection-fills-the-gap&quot;&gt;Where Direct Collection Fills The Gap&lt;/h3&gt;
&lt;p&gt;Direct collection reads the same public pages a logged out visitor sees. It covers the fields the API dropped, and it costs you a parsing layer that breaks whenever the markup changes.&lt;/p&gt;
&lt;p&gt;The pattern that survives is a hybrid. Use the API wherever the endpoint exists, use direct collection only for the specific fields it cannot serve, and reconcile the two on stable ids and timestamps so you can tell which source produced a given record. If your pipeline cannot answer &quot;where did this row come from&quot;, debugging a mismatch six months later is guesswork.&lt;/p&gt;
&lt;h3 id=&quot;treat-a-block-as-rate-feedback&quot;&gt;Treat A Block As Rate Feedback&lt;/h3&gt;
&lt;p&gt;Every large platform scores request patterns, browser fingerprints and timing. The useful mental model is not that you are up against a puzzle to solve, it is that you are being told your collection rate does not look like a person.&lt;/p&gt;
&lt;p&gt;Real browser sessions, honest pacing, retries with backoff, and a hard stop when the platform signals no. Pipelines built that way keep running for months. The aggressive ones work for a week and then poison the whole project, because once you are blocked you have also lost the public access you started with.&lt;/p&gt;
&lt;h3 id=&quot;decide-these-before-you-write-code&quot;&gt;Decide These Before You Write Code&lt;/h3&gt;
&lt;p&gt;Which specific fields do you need, and does the API already serve them. What is your collection rate, and would a human plausibly produce it. Are you touching anything behind a login, because that is the line where a public data project becomes something else. And what is your retention story, since privacy law applies to personal data even when it was posted publicly.&lt;/p&gt;
&lt;p&gt;Answer those four and the implementation is the easy part. I wrote up the longer version, including the platform by platform data breakdown, the Python libraries, and the legal and ethical considerations, in this &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.webbrowserbot.com/social-media-scraping/&quot;&gt;guide to social media scraping&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The short version: collect public data only, at a rate you would be comfortable explaining, and keep the two sources honest about where each record came from.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25157/choosing-between-a-social-platform-api-and-direct-collection</guid>
<pubDate>Sat, 22 Aug 2026 20:01:07 +0000</pubDate>
</item>
<item>
<title>Building NLP Systems for 96 Low-Resource Languages: A Web Scraping War Story</title>
<link>https://coderlegion.com/22837/building-nlp-systems-for-96-low-resource-languages-a-web-scraping-war-story</link>
<description>&lt;h4 id=&quot;or-why-language-vitality-online-has-nothing-to-do-with-speaker-counts-and-everything-to-do-with-data-engineering&quot;&gt;Or why language vitality online has nothing to do with speaker counts — and everything to do with data engineering&lt;/h4&gt;
&lt;p&gt;Russia speaks dozens of minority languages — Tatar, Bashkir, Udmurt, and even tiny ones like South Yukaghir (fewer than 50 speakers). Some have no Wikipedia, no standard corpus, and almost no digital footprint.&lt;/p&gt;
&lt;p&gt;But they &lt;em&gt;do&lt;/em&gt; exist online — scattered across forums, blogs, and VK communities.&lt;/p&gt;
&lt;p&gt;We wanted to measure their online presence. As NLP engineers, we quickly realized: &lt;strong&gt;no global corpus, no labelled dataset, no pre-trained embeddings, and no simple way to ask a search engine &quot;give me all pages in Chechen&quot;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So we built our own pipeline. Here's what every developer building NLP systems for low-resource languages needs to know.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-core-problem-low-resource-nlp-without-a-dictionary&quot;&gt;The Core Problem: Low-Resource NLP Without a Dictionary&lt;/h3&gt;
&lt;p&gt;In mainstream NLP, you start with a corpus. Wikipedia. Common Crawl. Something.&lt;/p&gt;
&lt;p&gt;For 96 minority languages, &lt;strong&gt;the corpus doesn't exist&lt;/strong&gt;. You have to find the data first — before you can even think about training embeddings, fine-tuning BERT, or building a translation system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is the ultimate cold-start problem.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every stage of a standard NLP pipeline breaks:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;NLP Stage &lt;/th&gt;&lt;th&gt;The Problem&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Data Acquisition&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;No central repository. Data is scattered across VK, forums, blogs.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Language Identification&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;No pre-trained classifier for 90% of these languages.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Tokenization&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;No standard orthography. People type without diacritics.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Embeddings&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;No pre-trained word vectors. You must build from scratch.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Evaluation&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;No gold-standard test sets. You can't measure what you can't label.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-data-acquisition-the-search-api-as-your-crawler&quot;&gt;Step 1: Data Acquisition — The Search API as Your Crawler&lt;/h3&gt;
&lt;p&gt;You can't download the whole internet (unless you're Google). But search engines already crawled it.&lt;/p&gt;
&lt;p&gt;We used &lt;strong&gt;Yandex.XML&lt;/strong&gt; — a limited API with &lt;strong&gt;10,000 requests per day&lt;/strong&gt;. Each request returns one page of results, and each &lt;em&gt;next&lt;/em&gt; page costs another request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For an engineer, this means:&lt;/strong&gt; you have to be &lt;em&gt;extremely&lt;/em&gt; efficient with your queries.&lt;/p&gt;
&lt;h4 id=&quot;the-challenge-what-do-you-search-for-&quot;&gt;The challenge: What do you search for?&lt;/h4&gt;
&lt;p&gt;We needed &lt;strong&gt;word markers&lt;/strong&gt; — words that are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Frequent&lt;/strong&gt; in the target language&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unique&lt;/strong&gt; — not appearing in Russian or other languages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free of special characters&lt;/strong&gt; — because real users don't type them&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That last point is &lt;strong&gt;critical for any NLP system dealing with real-world user-generated content.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most minority languages use Cyrillic extensions: &lt;code&gt;ҙ&lt;/code&gt;, &lt;code&gt;ә&lt;/code&gt;, &lt;code&gt;ö&lt;/code&gt;, &lt;code&gt;ҷ&lt;/code&gt;. There's no standard keyboard for them. People write &lt;code&gt;&quot;менан&quot;&lt;/code&gt; instead of &lt;code&gt;&quot;менән&quot;&lt;/code&gt;. If we query with the &quot;correct&quot; spelling, we lose 90% of real texts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We call this &quot;household spelling&quot;&lt;/strong&gt; — like medieval birch-bark manuscripts, but with modern laziness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For your NLP pipeline:&lt;/strong&gt; If you're building systems for languages without standard keyboards, you need &lt;strong&gt;fuzzy matching&lt;/strong&gt; at the query stage, not just at the evaluation stage. This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Levenshtein-based query expansion&lt;/li&gt;
&lt;li&gt;Phonetic normalization&lt;/li&gt;
&lt;li&gt;Handling diacritic stripping before search&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-noise-filtering-the-precision-problem&quot;&gt;Step 2: Noise Filtering — The Precision Problem&lt;/h3&gt;
&lt;p&gt;Even good markers return junk. We had to filter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wikipedia duplicates&lt;/strong&gt; — we can download those in one click, so exclude them&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linguistic papers&lt;/strong&gt; — Russian text containing one line of Rutul as an example&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Music sites&lt;/strong&gt; — song titles but no lyrics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typos&lt;/strong&gt; — e.g., Russian &lt;code&gt;&quot;пазух&quot;&lt;/code&gt; (bosom) mistyped as &lt;code&gt;&quot;пазох&quot;&lt;/code&gt;, which is a valid Khakas word meaning &quot;again&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-solution-multi-marker-verification&quot;&gt;The solution: Multi-marker verification&lt;/h4&gt;
&lt;p&gt;If one marker appears on a domain but others from the same language don't — it's probably a false positive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For your ML pipeline:&lt;/strong&gt; This is &lt;strong&gt;ensemble validation&lt;/strong&gt; — using multiple weak signals to confirm a strong one. The trade-off is extra API calls, which are expensive at 10k/day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alternative approach:&lt;/strong&gt; Train a binary classifier (Russian vs. everything else) on the fly. We did this later — more on that below.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-domain-classification-building-a-tiered-index&quot;&gt;Step 3: Domain Classification — Building a Tiered Index&lt;/h3&gt;
&lt;p&gt;We asked Yandex to categorize each domain into three tiers:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Tier &lt;/th&gt;&lt;th&gt;Description &lt;/th&gt;&lt;th&gt;Example&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;1. Language-first&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Most pages in the minority language &lt;/td&gt;&lt;td&gt;Small blogs, community sites&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;2. Mixed&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Several pages, but not dominant &lt;/td&gt;&lt;td&gt;Regional news portals&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3. Giant platforms&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Minority content is buried &lt;/td&gt;&lt;td&gt;YouTube, stihi.ru&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Additionally, we specifically queried &lt;strong&gt;VK.com&lt;/strong&gt; communities because post-2012, most standalone language sites died — and everything moved to social networks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For your data engineering pipeline:&lt;/strong&gt; You need a &lt;strong&gt;stratified crawling strategy&lt;/strong&gt;. Don't treat all domains equally. Allocate more budget to Tier 1, and use depth-limited sampling for Tier 3.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-4-language-detection-without-a-training-corpus&quot;&gt;Step 4: Language Detection Without a Training Corpus&lt;/h3&gt;
&lt;p&gt;We downloaded pages and needed to separate Russian from, say, Tuvan.&lt;/p&gt;
&lt;p&gt;Classic n-gram language detection requires a pre-labelled corpus — which we didn't have for most languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt; We &lt;em&gt;did&lt;/em&gt; have Russian — plenty of it. So we built a &lt;strong&gt;binary classifier&lt;/strong&gt;: &lt;em&gt;Russian vs. everything else&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If a paragraph isn't Russian, we assume it's our target language.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For your ML system:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is a &lt;strong&gt;one-class classification&lt;/strong&gt; problem with a twist.&lt;/li&gt;
&lt;li&gt;It's not perfect — but it's good enough to build the &lt;strong&gt;first ever web corpora&lt;/strong&gt; for these languages.&lt;/li&gt;
&lt;li&gt;This is a classic &lt;strong&gt;weak supervision&lt;/strong&gt; approach: use what you have (Russian) to learn what you don't (everything else).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-we-learned-online-vs-offline-zero-correlation&quot;&gt;What We Learned: Online vs. Offline — Zero Correlation&lt;/h3&gt;
&lt;p&gt;We compared online metrics (Wikipedia articles, VK communities, text volume) with offline metrics (speaker count, regional economy).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric Pair &lt;/th&gt;&lt;th&gt;Correlation&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Among online metrics &lt;/td&gt;&lt;td&gt;&lt;strong&gt;&amp;gt;0.7&lt;/strong&gt; (Spearman)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Online vs. offline &lt;/td&gt;&lt;td&gt;&lt;strong&gt;≈ 0&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;This is the key insight for anyone building IT systems for low-resource languages:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;How many people speak a language &lt;strong&gt;does not predict&lt;/strong&gt; how alive it is on the internet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What matters is a &lt;strong&gt;small, active, passionate community&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Bashkir has fewer speakers than Chechen, but far more online communities. Why?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chechnya's internet infrastructure recovered later (1990s-2000s conflicts delayed digital adoption).&lt;/li&gt;
&lt;li&gt;Online activity is a function of &lt;strong&gt;infrastructure&lt;/strong&gt;, not just population.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;For your product strategy:&lt;/strong&gt; If you're building a language model for a low-resource language, prioritize communities, not census data. Find the VK groups, Discord servers, and Telegram channels. That's where the data lives.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-few-geeky-findings&quot;&gt;A Few Geeky Findings&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Finding &lt;/th&gt;&lt;th&gt;Implication&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Post-2012 shift&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Almost all minority language activity moved to VK.com. Standalone forums died.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Typical user&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;19-31 years old, lives in the region's capital (Ufa for Bashkir, Izhevsk for Udmurt).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Typical post length&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;strong&gt;~5 words&lt;/strong&gt; per post in minority language. Most conversation is in Russian; people sprinkle native words as identity markers.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Interconnectivity&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Minority language sites link to each other across language families. A rare case of &quot;peoples united&quot; online.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;For your NLP models:&lt;/strong&gt; If you're building embeddings from this data, expect &lt;strong&gt;sparse, short texts&lt;/strong&gt;. Standard word2vec on 5-word posts won't work well. Consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Character-level embeddings&lt;/strong&gt; (more robust to spelling variation)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-lingual transfer&lt;/strong&gt; (use Russian or English as a bridge)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero-shot approaches&lt;/strong&gt; (label propagation from related languages)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-data-engineering-pipeline-summary&quot;&gt;The Data Engineering Pipeline — Summary&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────┐
│  Yandex.XML API │  ← 10k requests/day budget
│  (search engine)│
└────────┬────────┘
         ▼
┌─────────────────┐
│  Word Markers   │  ← Frequent + Unique + No diacritics
│  (handcrafted)  │  ← &quot;Household spelling&quot; handling
└────────┬────────┘
         ▼
┌─────────────────┐
│  Domain Filter  │  ← Remove Wikipedia, linguistic papers
│  (multi-marker) │  ← Ensemble validation
└────────┬────────┘
         ▼
┌─────────────────┐
│  Tiered Index   │  ← Language-first, Mixed, Giant platforms
│  (stratified)   │  ← VK.com special treatment
└────────┬────────┘
         ▼
┌─────────────────┐
│  Binary Class.  │  ← Russian vs. everything else
│  (weak super.)  │  ← No pre-labelled corpus needed
└────────┬────────┘
         ▼
┌─────────────────┐
│  Web Corpora    │  ← First ever for these languages
│  (downloadable) │  ← Available at web-corpora.net
└─────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-data&quot;&gt;The Data&lt;/h3&gt;
&lt;p&gt;Downloadable corpora and domain lists:&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://web-corpora.net/wsgi3/minorlangs/download&quot;&gt;web-corpora.net/wsgi3/minorlangs/download&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And yes, there's a VK community with cute memes in minority languages — featuring cats.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-bottom-line-for-engineers&quot;&gt;The Bottom Line for Engineers&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;Web scraping isn't just about avoiding &lt;code&gt;robots.txt&lt;/code&gt;. It's about understanding human behaviour — typos, keyboard limitations, social network migration, and why a language with 50 speakers can thrive online if those 50 are determined enough.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaways for building NLP systems for low-resource languages:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with search APIs, not crawlers.&lt;/strong&gt; The internet is already indexed. Use it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handle real-world spelling.&lt;/strong&gt; People don't use diacritics. Your queries shouldn't either.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use weak supervision.&lt;/strong&gt; You don't need a labelled corpus to start — use what you have.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prioritize communities, not speakers.&lt;/strong&gt; Online vitality ≠ offline speaker count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expect short texts.&lt;/strong&gt; ~5-word posts are the norm. Plan your embeddings accordingly.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our downloadable corpora: &lt;a rel=&quot;nofollow&quot; href=&quot;http://web-corpora.net/wsgi3/minorlangs/download&quot;&gt;web-corpora.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yandex.XML API documentation&lt;/li&gt;
&lt;li&gt;VK.com API for social media data extraction&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Liked this? Share with your NLP engineering team. They'll never look at &quot;low-resource&quot; the same way again.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22837/building-nlp-systems-for-96-low-resource-languages-a-web-scraping-war-story</guid>
<pubDate>Thu, 06 Aug 2026 16:21:00 +0000</pubDate>
</item>
</channel>
</rss>