<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: rhea hollis</title>
    <description>The latest articles on DEV Community by rhea hollis (@rhea_hollis_).</description>
    <link>https://dev.to/rhea_hollis_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4007228%2F8352e523-3fc4-48e7-b2cc-7656b640093b.png</url>
      <title>DEV Community: rhea hollis</title>
      <link>https://dev.to/rhea_hollis_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rhea_hollis_"/>
    <language>en</language>
    <item>
      <title>Localized Video Data Collection: A Practical Pipeline from Search to Structured Records</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:05:37 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/localized-video-data-collection-a-practical-pipeline-from-search-to-structured-records-42ln</link>
      <guid>https://dev.to/rhea_hollis_/localized-video-data-collection-a-practical-pipeline-from-search-to-structured-records-42ln</guid>
      <description>&lt;h1&gt;
  
  
  Localized Video Data Collection: A Practical Pipeline from Search to Structured Records
&lt;/h1&gt;

&lt;p&gt;Collecting video data across regions is not just a download problem. Search coverage, language, location, captions, duplicates, and rights all affect whether the final records are useful. A staged pipeline makes those decisions visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: discover a representative seed set
&lt;/h2&gt;

&lt;p&gt;Start with a narrow set of public search results, channels, or category pages. Save the original query, translated query when used, locale, timestamp, and source URL. This lets you distinguish a discovery gap from a retrieval failure.&lt;/p&gt;

&lt;p&gt;Do not optimize for the largest possible URL list on day one. A smaller sample that covers the languages and content types you care about is more useful for testing the schema and quality checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 2: filter with explicit rules
&lt;/h2&gt;

&lt;p&gt;Turn “relevant video” into rules that can be rerun:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duration range;&lt;/li&gt;
&lt;li&gt;language or language mix;&lt;/li&gt;
&lt;li&gt;publication window;&lt;/li&gt;
&lt;li&gt;creator or channel exclusions;&lt;/li&gt;
&lt;li&gt;duplicate threshold;&lt;/li&gt;
&lt;li&gt;review status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Store the rule version on each batch. If “recent” changes from 90 days to 30 days, you should be able to reproduce both views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 3: retrieve with bounded retries
&lt;/h2&gt;

&lt;p&gt;Network failures are expected. Classify them, retry only transient errors, and keep an idempotent queue keyed by platform and source ID. This prevents a retry from silently creating a second record.&lt;/p&gt;

&lt;p&gt;If the target page is localized, test a small set of required countries or cities separately. Residential routing can help a collector observe public, region-specific pages, but it does not replace permission, rate limiting, or a valid client implementation. Keep proxy credentials in environment variables and outside the dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 4: normalize without losing the evidence
&lt;/h2&gt;

&lt;p&gt;Store raw response metadata beside the normalized record. Convert timestamps to UTC while retaining the original timezone when available. Keep the original title and description before removing markup or normalizing punctuation.&lt;/p&gt;

&lt;p&gt;For multilingual video, preserve the original language label, your detected language, and the confidence or review status as separate fields. This is more useful than pretending every item has one certain language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 5: validate the batch
&lt;/h2&gt;

&lt;p&gt;For every batch, report:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;duplicate and near-duplicate rate;&lt;/li&gt;
&lt;li&gt;missing required fields;&lt;/li&gt;
&lt;li&gt;caption or transcript alignment;&lt;/li&gt;
&lt;li&gt;language distribution;&lt;/li&gt;
&lt;li&gt;download and decode failures;&lt;/li&gt;
&lt;li&gt;items needing manual review.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A dashboard that shows only record count hides the expensive problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a ready-made dataset is sensible
&lt;/h2&gt;

&lt;p&gt;Build your own collector when the source mix or filtering logic is central to the research. Consider a ready-made or custom dataset when the team needs structured metadata, captions, transcripts, engagement fields, and a predictable delivery process more than it needs to own every retrieval step.&lt;/p&gt;

&lt;p&gt;Thordata describes multi-platform video data with source and platform fields, content signals, ready-to-use datasets, custom collection, API access, and JSON/CSV/Parquet delivery. Ask for a representative sample and confirm the fields, media availability, refresh schedule, and rights documentation before integrating it: &lt;a href="https://www.thordata.com/products/multi-platform-video-datasets?op=rhea&amp;amp;from=x" rel="noopener noreferrer"&gt;https://www.thordata.com/products/multi-platform-video-datasets?op=rhea&amp;amp;from=x&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical handoff checklist
&lt;/h2&gt;

&lt;p&gt;Before giving records to a model or analyst, deliver the schema, manifest, checksums, collection window, rule version, exclusion list, and a short quality report. The handoff should make clear what is covered, what is missing, and what must not be inferred.&lt;/p&gt;

</description>
      <category>dataengineering</category>
    </item>
    <item>
      <title>How to Build a Video Dataset for Multimodal AI: Schema and Provenance First</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:05:13 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/how-to-build-a-video-dataset-for-multimodal-ai-schema-and-provenance-first-38mj</link>
      <guid>https://dev.to/rhea_hollis_/how-to-build-a-video-dataset-for-multimodal-ai-schema-and-provenance-first-38mj</guid>
      <description>&lt;h1&gt;
  
  
  How to Build a Video Dataset for Multimodal AI: Schema and Provenance First
&lt;/h1&gt;

&lt;p&gt;Many video dataset projects start with a large download queue. A better starting point is a small schema and a clear answer to one question: &lt;strong&gt;can another person explain where each record came from and what happened to it?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Design the record before collecting files
&lt;/h2&gt;

&lt;p&gt;Separate source facts from derived labels. A practical record can include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"platform_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"creator_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"published_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transcript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rights_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"review_required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collected_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schema_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the raw source fields. If a title is later cleaned, translated, or classified, store the derived value in another field instead of overwriting the original.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provenance is part of model quality
&lt;/h2&gt;

&lt;p&gt;For each item, record the source URL or platform ID, collection time, transformation version, and exclusion reason when an item is removed. This helps a team investigate a surprising training example, refresh a deleted source, or explain why two exports differ.&lt;/p&gt;

&lt;p&gt;A URL is not the same thing as redistribution permission. Rights review, platform terms, creator consent where required, and regional privacy rules belong in the dataset plan. Keep a review state such as &lt;code&gt;pending&lt;/code&gt;, &lt;code&gt;approved&lt;/code&gt;, &lt;code&gt;restricted&lt;/code&gt;, or &lt;code&gt;removed&lt;/code&gt;, with a reason code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five checks to automate early
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Duplicate detection:&lt;/strong&gt; use platform IDs first, then perceptual hashes or short audio fingerprints for near-duplicates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temporal consistency:&lt;/strong&gt; check that duration, caption timestamps, transcript length, and scene boundaries agree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Language quality:&lt;/strong&gt; store detected language separately from an uploader-provided label. Mixed-language content should not be forced into a single class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creator leakage:&lt;/strong&gt; split by creator or channel when the same style or footage could otherwise appear in both training and evaluation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decode and field completeness:&lt;/strong&gt; report missing media, unreadable files, absent captions, and missing metadata as separate failure categories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose delivery around the next user of the data
&lt;/h2&gt;

&lt;p&gt;JSON is convenient for nested records, CSV for flat analysis, and Parquet for columnar processing. Whichever format you use, ship a manifest with checksums, schema version, collection window, exclusions, and known gaps.&lt;/p&gt;

&lt;p&gt;Thordata’s multi-platform video dataset page describes structured metadata, captions, transcripts, engagement signals, ready-to-use datasets, custom collection, API access, and JSON/CSV/Parquet delivery. Those options are useful when a team wants to spend more time on modeling and less time maintaining every collector. Request a sample schema first and compare it with your required fields, languages, refresh cadence, and rights process: &lt;a href="https://www.thordata.com/products/multi-platform-video-datasets?op=rhea&amp;amp;from=x" rel="noopener noreferrer"&gt;https://www.thordata.com/products/multi-platform-video-datasets?op=rhea&amp;amp;from=x&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A small baseline beats a huge guess
&lt;/h2&gt;

&lt;p&gt;Before scaling, train or evaluate a small baseline on a fixed sample. Track required-field coverage, duplicate rate, transcript alignment, language distribution, and manual-review volume. If adding more records does not improve the baseline, improve sampling or labels before buying more storage.&lt;/p&gt;

</description>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Rotating vs Sticky Residential Proxies: How to Choose for Web Scraping</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:04:13 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/rotating-vs-sticky-residential-proxies-how-to-choose-for-web-scraping-2kh1</link>
      <guid>https://dev.to/rhea_hollis_/rotating-vs-sticky-residential-proxies-how-to-choose-for-web-scraping-2kh1</guid>
      <description>&lt;h1&gt;
  
  
  Rotating vs Sticky Residential Proxies: How to Choose for Web Scraping
&lt;/h1&gt;

&lt;p&gt;Choosing a residential proxy is often framed as a question of IP count. In practice, the more useful question is: &lt;strong&gt;does this workflow need a new IP for each request, or continuity across several requests?&lt;/strong&gt; That distinction affects parsing, login-free sessions, retries, and debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  When rotating residential proxies make sense
&lt;/h2&gt;

&lt;p&gt;Rotating sessions are a good fit for independent requests: product pages, public listings, search-result samples, and broad market checks. The collector can move from one URL to the next without depending on a cookie or a previous response.&lt;/p&gt;

&lt;p&gt;Rotation is not a cure for aggressive crawling. Keep concurrency bounded, use a clear user agent, respect site rules, and retry only transient failures. Measure valid records rather than counting HTTP 200 responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a sticky session is the better tool
&lt;/h2&gt;

&lt;p&gt;Some workflows need a little continuity. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;walking through pagination where a short-lived cookie is set;&lt;/li&gt;
&lt;li&gt;checking several pages in one browser-like flow;&lt;/li&gt;
&lt;li&gt;reproducing a localized page while debugging a parser;&lt;/li&gt;
&lt;li&gt;running a short sequence of requests that should share the same network identity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these cases, a sticky session with an explicit time limit is easier to reason about than changing the IP after every request. It should still expire. A session that lasts longer than the job needs can make failures harder to isolate and may waste traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small benchmark you can repeat
&lt;/h2&gt;

&lt;p&gt;Use the same URL list, parser version, and request schedule for both modes. Record:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;required-field completion;&lt;/li&gt;
&lt;li&gt;empty or blocked responses;&lt;/li&gt;
&lt;li&gt;median and p95 latency;&lt;/li&gt;
&lt;li&gt;retry count;&lt;/li&gt;
&lt;li&gt;cost per valid record.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a page is location-sensitive, repeat the test for the exact country or city required by the project. Do not assume that a generic endpoint represents every local result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example request configuration
&lt;/h2&gt;

&lt;p&gt;Keep credentials outside source code and dataset records. A simple Python test can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;proxy_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PROXY_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/public-page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ResearchBot/1.0 (+contact@example.com)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is deliberately small. The important part is the surrounding log: target URL, region, session mode, response status, parser result, and timestamp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Thordata fits
&lt;/h2&gt;

&lt;p&gt;Thordata’s residential-proxy documentation describes country, city, and other location targeting, along with rotating and sticky session options over HTTP/HTTPS. That makes it a candidate for a controlled comparison, not a reason to skip your own benchmark. A practical starting point is the residential proxy page: &lt;a href="https://www.thordata.com/?ls=dde&amp;amp;lk=dde" rel="noopener noreferrer"&gt;https://www.thordata.com/?ls=dde&amp;amp;lk=dde&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Responsible collection
&lt;/h2&gt;

&lt;p&gt;Collect only public data you have a legitimate reason to use. Follow the target site’s terms, applicable rate limits, and copyright requirements. A slower, observable collector is easier to maintain than a fast one that cannot explain where its requests came from.&lt;/p&gt;

</description>
      <category>webscraping</category>
    </item>
    <item>
      <title>From Public Video URLs to a Maintainable Data Pipeline</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Sun, 20 Sep 2026 14:21:00 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/from-public-video-urls-to-a-maintainable-data-pipeline-h92</link>
      <guid>https://dev.to/rhea_hollis_/from-public-video-urls-to-a-maintainable-data-pipeline-h92</guid>
      <description>&lt;p&gt;Collecting video data is not one request followed by one download. It is a sequence of discovery, filtering, retrieval, normalization, quality control, and refresh. Treating it as a pipeline makes failures visible and keeps a useful dataset from becoming a one-off archive.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Discovery: build a narrow seed set
&lt;/h2&gt;

&lt;p&gt;Start with search results, known channels, or public category pages. Save the query, locale, timestamp, and source URL for every seed. Do not begin with maximum volume; begin with a set that represents the task you want to measure.&lt;/p&gt;

&lt;p&gt;For multilingual work, keep the original query and a translated query as separate fields. This makes it possible to tell whether a gap came from search coverage or from later filtering.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Filtering: make policy executable
&lt;/h2&gt;

&lt;p&gt;Turn vague requirements into rules: duration range, language, date window, creator exclusions, duplicate threshold, and review status. Store the rule version on every run. When a stakeholder changes “recent” from 90 days to 30 days, you should be able to reproduce both outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Retrieval: design for partial failure
&lt;/h2&gt;

&lt;p&gt;Network requests fail. A good collector records the failure class, retries only transient errors, and moves on. Keep a queue with an idempotent key such as &lt;code&gt;(platform, platform_id, variant)&lt;/code&gt;. That prevents a retry from creating a second copy.&lt;/p&gt;

&lt;p&gt;When location matters, test a small set of regions separately. A residential proxy can help a collector observe public, localized pages, but it does not replace permission, rate limiting, or a valid client implementation. Keep proxy credentials in environment variables and outside dataset records.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Normalization: preserve the raw evidence
&lt;/h2&gt;

&lt;p&gt;Store the raw response metadata and a normalized record side by side. Normalize timestamps to UTC while retaining the original timezone when it is available. Keep the original title and description before cleaning punctuation or HTML. These small choices make later audits possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Quality control: sample every batch
&lt;/h2&gt;

&lt;p&gt;For each batch, inspect a fixed sample and calculate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate and near-duplicate rates;&lt;/li&gt;
&lt;li&gt;missing required fields;&lt;/li&gt;
&lt;li&gt;caption or transcript alignment;&lt;/li&gt;
&lt;li&gt;language distribution;&lt;/li&gt;
&lt;li&gt;download and decode failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Publish the report with the batch identifier. A dashboard that shows only item count hides the most expensive problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Delivery: match the consumer
&lt;/h2&gt;

&lt;p&gt;JSON is convenient for nested metadata, CSV works well for flat analysis, and Parquet is usually a better fit for columnar processing. Deliver a manifest with checksums, schema version, collection window, exclusions, and known gaps. The manifest is as important as the media files.&lt;/p&gt;

&lt;p&gt;Thordata presents video data from public platforms with structured metadata, captions, transcripts, and engagement signals, and describes delivery through files, APIs, and cloud or integration endpoints. That can be useful when a team wants to spend its time on modeling rather than maintaining every collector. Still, evaluate a sample against your own schema, languages, and rights process.&lt;/p&gt;

&lt;h2&gt;
  
  
  A refresh policy keeps the dataset honest
&lt;/h2&gt;

&lt;p&gt;Define what changes trigger a refresh: new uploads, edited captions, deleted sources, or a changed moderation decision. Keep immutable batch snapshots and a current index. This gives model builders a stable experiment set while allowing the production view to evolve.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>Video Datasets That Survive Contact With a Real Model</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Sun, 20 Sep 2026 11:21:00 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/video-datasets-that-survive-contact-with-a-real-model-395h</link>
      <guid>https://dev.to/rhea_hollis_/video-datasets-that-survive-contact-with-a-real-model-395h</guid>
      <description>&lt;p&gt;A video dataset can look impressive in a spreadsheet and still fail in training. The usual causes are ordinary: duplicate clips, missing timestamps, unclear source history, captions that do not match the audio, and a split that leaks the same creator into both training and evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define the record before collecting media
&lt;/h2&gt;

&lt;p&gt;Start with a schema that separates source facts from derived labels. A useful minimum record might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"platform_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"creator_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"published_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"caption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transcript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rights_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"review_required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collected_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schema_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep &lt;code&gt;collected_at&lt;/code&gt; and &lt;code&gt;schema_version&lt;/code&gt;. They make a refresh auditable and prevent a silent change in meaning when a field is renamed later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five quality checks worth automating
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Identity.&lt;/strong&gt; Deduplicate on stable platform IDs where available, then use perceptual hashes or short audio fingerprints for near-duplicates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time.&lt;/strong&gt; Check that duration, timestamps, captions, and scene boundaries are internally consistent. A transcript that extends beyond the video is a useful failure signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Language.&lt;/strong&gt; Store the detected language and confidence separately from the uploader’s language label. Mixed-language videos should not be forced into one bucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content safety.&lt;/strong&gt; Keep a review status and a reason code. A single yes/no moderation flag is hard to audit and easy to misuse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Splits.&lt;/strong&gt; Split by creator, channel, or source group when the task could otherwise leak style or repeated footage across train and test sets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provenance is a model feature
&lt;/h2&gt;

&lt;p&gt;For every record, preserve where it came from, when it was observed, which transformation produced the caption or transcript, and what was removed. This lets a team investigate a surprising model output without rebuilding the entire collection job.&lt;/p&gt;

&lt;p&gt;It also clarifies what a dataset does &lt;strong&gt;not&lt;/strong&gt; grant. A source URL is not automatically a licence to redistribute a video. Rights review, platform terms, consent requirements, and regional rules belong in the project plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing delivery: files, API, or a custom job
&lt;/h2&gt;

&lt;p&gt;Ready-to-use files are convenient when the schema and coverage already match your task. API access is useful when you need on-demand filtering or regular refreshes. A custom collection is justified when the required fields, languages, or source mix are specific enough that a generic package would create too much cleanup.&lt;/p&gt;

&lt;p&gt;Thordata’s video-data pages describe structured metadata, captions, transcripts, engagement fields, and exports such as JSON, CSV, and Parquet, with ready-to-use, API, and custom collection options. Ask for a sample schema and a small evaluation set first. Check field availability, refresh timing, and rights documentation before committing to a larger delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure usefulness, not only volume
&lt;/h2&gt;

&lt;p&gt;Report the percentage of records that pass every required-field check, the duplicate rate, transcript alignment, language mix, and the number of items needing manual review. Then train a small baseline model. If quality does not improve when the dataset grows, more records are not the answer; better sampling or labels are.&lt;/p&gt;

</description>
      <category>datasets</category>
    </item>
    <item>
      <title>A Practical Residential Proxy Playbook for Reliable Web Data Collection</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Sun, 20 Sep 2026 08:22:21 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/a-practical-residential-proxy-playbook-for-reliable-web-data-collection-4o5l</link>
      <guid>https://dev.to/rhea_hollis_/a-practical-residential-proxy-playbook-for-reliable-web-data-collection-4o5l</guid>
      <description>&lt;p&gt;Residential proxies are useful when a website serves different content by location or treats data-centre traffic differently. They are not a magic bypass, though. A stable collection job still depends on request pacing, clear scope, retries, and good data validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the target, not the proxy
&lt;/h2&gt;

&lt;p&gt;Before choosing a provider, write down four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which public pages are in scope?&lt;/li&gt;
&lt;li&gt;Which countries, cities, or languages matter?&lt;/li&gt;
&lt;li&gt;How often will the same page be requested?&lt;/li&gt;
&lt;li&gt;What should happen when a request is blocked or returns an empty page?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This short brief prevents a common mistake: buying a large pool when the real problem is an overly aggressive crawler or an incorrect locale setting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rotating or sticky sessions?
&lt;/h2&gt;

&lt;p&gt;Use a rotating session when requests are independent, such as collecting product pages across many public URLs. A fresh IP can reduce concentration on one address, but rotation alone will not fix a broken parser or an invalid request pattern.&lt;/p&gt;

&lt;p&gt;Use a sticky session when a workflow needs continuity. Login-free flows such as pagination, a multi-step form, or a page that sets a short-lived cookie are easier to debug when the same session is kept for a limited period. Set a clear expiry instead of keeping a session forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small, observable request loop
&lt;/h2&gt;

&lt;p&gt;Keep the first test small. Log the URL, timestamp, chosen region, status code, response time, and parser result. Never log proxy passwords or full authorization URLs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PROXY_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seed_urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ResearchBot/1.0 (+contact@example.com)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;record_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not the code itself. It is the feedback loop: measure success by the fields you actually need, not only by HTTP 200 responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check in a provider
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Geo controls:&lt;/strong&gt; Can you target the country or city required by the project?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session controls:&lt;/strong&gt; Are rotation and sticky behaviour explicit and testable?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol and tooling:&lt;/strong&gt; Does the service document HTTP/HTTPS integration for your client?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage visibility:&lt;/strong&gt; Can you see traffic, errors, and spend while testing?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceptable-use and sourcing:&lt;/strong&gt; Is there a clear policy for lawful public-data collection?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thordata’s residential-proxy documentation describes HTTP/HTTPS access, rotating and sticky sessions, and location targeting. Its product pages also present residential IP coverage across many regions. Treat those as capabilities to verify in your own trial, not as a substitute for a benchmark on your target pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple benchmark that travels well
&lt;/h2&gt;

&lt;p&gt;Run the same 50–100 public URLs through your existing route and a candidate proxy route. Compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;required-field completion;&lt;/li&gt;
&lt;li&gt;median and p95 response time;&lt;/li&gt;
&lt;li&gt;block, timeout, and empty-page rates;&lt;/li&gt;
&lt;li&gt;location accuracy;&lt;/li&gt;
&lt;li&gt;cost per valid record.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep the URL list and parser version fixed. If the result is inconclusive, change one variable at a time: region, session mode, concurrency, or retry policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Responsible collection is part of reliability
&lt;/h2&gt;

&lt;p&gt;Respect the site’s terms, robots guidance where applicable, rate limits, and copyright rules. Collect only public data that you have a legitimate reason to use, and provide a contact route in your user agent when appropriate. A slower, explainable pipeline is usually easier to keep running than a fast one that creates complaints.&lt;/p&gt;

</description>
      <category>webscrapingproxy</category>
    </item>
    <item>
      <title>Building Reliable Web Scraping Pipelines in 2026: Why Proxy Infrastructure Matters</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Fri, 11 Sep 2026 09:13:33 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/building-reliable-web-scraping-pipelines-in-2026-why-proxy-infrastructure-matters-33fo</link>
      <guid>https://dev.to/rhea_hollis_/building-reliable-web-scraping-pipelines-in-2026-why-proxy-infrastructure-matters-33fo</guid>
      <description>&lt;p&gt;Modern web scraping is no longer just about sending HTTP requests and parsing HTML.&lt;/p&gt;

&lt;p&gt;As websites become more dynamic and anti-bot systems become more advanced, developers need to think beyond code. Browser fingerprints, IP reputation, request patterns, and infrastructure quality all affect whether a scraping workflow succeeds.&lt;/p&gt;

&lt;p&gt;A crawler that works perfectly in development may fail completely when running at scale.&lt;/p&gt;

&lt;p&gt;The difference is often not the scraper itself — it is the infrastructure behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Challenges Behind Large-Scale Web Scraping
&lt;/h2&gt;

&lt;p&gt;Many scraping failures come from three common problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Unstable IP Sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using shared or low-quality IPs often leads to:&lt;/p&gt;

&lt;p&gt;High block rates&lt;br&gt;
Frequent CAPTCHA challenges&lt;br&gt;
Unstable sessions&lt;br&gt;
Poor data collection efficiency&lt;/p&gt;

&lt;p&gt;For production scraping, IP quality directly impacts success rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Scaling Requests Without Losing Stability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Small scripts can work with a single connection.&lt;/p&gt;

&lt;p&gt;However, large-scale projects require:&lt;/p&gt;

&lt;p&gt;Multiple locations&lt;br&gt;
Different browsing patterns&lt;br&gt;
Session management&lt;br&gt;
Reliable connection performance&lt;/p&gt;

&lt;p&gt;A good proxy system should support different workloads instead of forcing every project into the same setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Collecting Data From Different Regions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many real-world projects require location-specific data:&lt;/p&gt;

&lt;p&gt;E-commerce price monitoring&lt;br&gt;
Search engine research&lt;br&gt;
Ad verification&lt;br&gt;
Market intelligence&lt;/p&gt;

&lt;p&gt;A crawler needs access from different countries, cities, or networks to collect accurate regional data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Proxy Infrastructure
&lt;/h2&gt;

&lt;p&gt;For serious scraping projects, &lt;a href="https://www.thordata.com/?ls=dde&amp;amp;lk=dde" rel="noopener noreferrer"&gt;residential proxies&lt;/a&gt; are commonly used because they represent real consumer network connections.&lt;/p&gt;

&lt;p&gt;A reliable residential proxy solution should provide:&lt;/p&gt;

&lt;p&gt;Large IP availability&lt;br&gt;
Geographic targeting&lt;br&gt;
Rotating sessions&lt;br&gt;
Stable long-term connections&lt;/p&gt;

&lt;p&gt;This allows developers to build more resilient data pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Thordata Fits
&lt;/h2&gt;

&lt;p&gt;Thordata provides residential and mobile proxy infrastructure designed for:&lt;/p&gt;

&lt;p&gt;Web scraping&lt;br&gt;
AI data collection&lt;br&gt;
Browser automation&lt;br&gt;
Market research&lt;br&gt;
Business intelligence&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;p&gt;100M+ real residential IPs&lt;br&gt;
Coverage across 195+ countries&lt;br&gt;
Rotating and sticky sessions&lt;br&gt;
Flexible geo-targeting&lt;/p&gt;

&lt;p&gt;Developers can test workflows before scaling with a 3-day free trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Successful scraping is not only about writing better crawlers.&lt;/p&gt;

&lt;p&gt;The modern data stack requires:&lt;/p&gt;

&lt;p&gt;Better extraction logic&lt;br&gt;
+&lt;br&gt;
Reliable browser automation&lt;br&gt;
+&lt;br&gt;
High-quality proxy infrastructure&lt;/p&gt;

&lt;p&gt;Building the right foundation early saves significant maintenance time later.&lt;/p&gt;

&lt;p&gt;Try Thordata free for 3 days and evaluate your workflow.&lt;/p&gt;

&lt;p&gt;Successful scraping is not only about writing better crawlers.&lt;/p&gt;

&lt;p&gt;The modern data stack requires:&lt;/p&gt;

&lt;p&gt;Better extraction logic&lt;br&gt;
+&lt;br&gt;
Reliable browser automation&lt;br&gt;
+&lt;br&gt;
High-quality proxy infrastructure&lt;/p&gt;

&lt;p&gt;Building the right foundation early saves significant maintenance time later.&lt;/p&gt;

&lt;p&gt;Try Thordata free for 3 days and evaluate your workflow.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Action Recognition Field Notes: Boundary Relabeling, Near-Miss Pairs, and Splits That Don't Lie</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Fri, 04 Sep 2026 09:13:46 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/action-recognition-field-notes-boundary-relabeling-near-miss-pairs-and-splits-that-dont-lie-h70</link>
      <guid>https://dev.to/rhea_hollis_/action-recognition-field-notes-boundary-relabeling-near-miss-pairs-and-splits-that-dont-lie-h70</guid>
      <description>&lt;p&gt;An action recognition run that stalls at "decent" usually isn't starved of architecture. It's starved of honest labels. This post is the code for the three fixes that moved accuracy more than any tuning: boundary relabeling, near-miss sampling, and source-aware splits.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Relabel to temporal boundaries
&lt;/h2&gt;

&lt;p&gt;Fixed 16-frame windows make "opening a door" and "closing a door" nearly the same clip. Trim to the detected action, keep context padding on both sides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trim_to_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# boundaries carry the signal, not the window
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relabel of ~200 near-miss clips outperformed a week of architecture experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Oversample the near-miss pairs
&lt;/h2&gt;

&lt;p&gt;Aggregate class balance hides the pairs that matter (sit down vs. bend-to-pick-up). Mine the confusion matrix and oversample against nearest neighbors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pair_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;topk_offdiagonal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;   &lt;span class="c1"&gt;# top confusable pairs
&lt;/span&gt;        &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boost&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt;   &lt;span class="c1"&gt;# feed a WeightedRandomSampler, not a blanket class weight
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blanket class weights lift everything evenly; pair boosts lift exactly what's broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Split by scene and source, never randomly
&lt;/h2&gt;

&lt;p&gt;Random splits leak the same actor and room into train and test. The split needs provenance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;honest_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scene_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ratios&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# provenance fields drive the split
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;random_group_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ratios&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This only works when every row knows where it came from — which is why lineage labels belong in the schema. Collections that ship source metadata per clip (Thordata's video datasets, for one — free trial here: &lt;a href="https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1" rel="noopener noreferrer"&gt;https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1&lt;/a&gt;) make the honest split a one-liner instead of a reconstruction project.&lt;/p&gt;

&lt;p&gt;The pattern across all three fixes: the data work is the model work. Boundaries in the labels, pairs in the sampler, provenance in the split.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>computervision</category>
      <category>deeplearning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>An Honest Price Monitor: Session Config, ASN Pinning, and Decoy Detection</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Thu, 03 Sep 2026 07:59:05 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/an-honest-price-monitor-session-config-asn-pinning-and-decoy-detection-50f7</link>
      <guid>https://dev.to/rhea_hollis_/an-honest-price-monitor-session-config-asn-pinning-and-decoy-detection-50f7</guid>
      <description>&lt;p&gt;A price monitor that fails loudly is fine — you fix it. The dangerous one returns 200s and plausible numbers while the dataset rots. This post is the code for the three fixes that matter: session config, ASN pinning, and decoy detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Size the session to the crawl
&lt;/h2&gt;

&lt;p&gt;The classic mistake is a default timeout that expires mid-catalog. The exit rotates, half the store re-downloads, and the two halves of the catalog are seen from different locations — the prices stop being comparable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SESSION_MINUTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_page_count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;avg_seconds_per_page&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sticky_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SESSION_MINUTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# &amp;gt; longest category, plus margin
&lt;/span&gt;    &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;asn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_asn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;# pin the exit to one network
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sticky sessions of 30–90 minutes with country and ASN targeting are standard on residential providers — Thordata exposes all three at session creation (free trial here: &lt;a href="https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1" rel="noopener noreferrer"&gt;https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1&lt;/a&gt;) — so this is configuration, not engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Log the exit country as a first-class column
&lt;/h2&gt;

&lt;p&gt;Mixed-location data reads as spread but is mostly geo-pricing noise. The fix costs one column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PriceRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;exit_country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;   &lt;span class="c1"&gt;# from the session, at capture time
&lt;/span&gt;    &lt;span class="n"&gt;exit_asn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;captured_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comparisons then happen &lt;code&gt;GROUP BY exit_country&lt;/code&gt; — anything else compares apples to a different country's apples.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Variance-check before storage
&lt;/h2&gt;

&lt;p&gt;Decoy pages arrive with a 200 status and plausible digits. Structural parsing won't catch them; statistics will:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_plausible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PriceRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;median&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# 7-day rolling median
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;  &lt;span class="c1"&gt;# not enough history yet
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;

&lt;span class="c1"&gt;# plausible -&amp;gt; store; implausible -&amp;gt; flag for review, never store silently
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 30% threshold against a 7-day median catches currency swaps and decoy prices while letting genuine flash sales through to review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full loop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_asn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;sticky_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SESSION_MINUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;catalog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_plausible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                &lt;span class="nf"&gt;store_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;flag_for_review&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Segment the schedule too: hourly snapshots for flash-deal SKUs, daily for the stable catalog — hourly-everything burns ~20x the bandwidth for marginal signal.&lt;/p&gt;

&lt;p&gt;The monitor that assumes its data is lying until proven otherwise is the one that survives.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>tutorial</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Provenance Labels for Video Datasets: Write Them at Collection Time</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Wed, 02 Sep 2026 02:19:18 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/provenance-labels-for-video-datasets-write-them-at-collection-time-n70</link>
      <guid>https://dev.to/rhea_hollis_/provenance-labels-for-video-datasets-write-them-at-collection-time-n70</guid>
      <description>&lt;p&gt;Every video dataset I've inherited has the same hole: the clips are fine, the metadata about where they came from doesn't exist. This post is the schema and the write path I now put in on day one, so the question "can we still use this clip?" has an answer two years later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four fields that matter
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Provenance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;      &lt;span class="c1"&gt;# exact page, not the domain
&lt;/span&gt;    &lt;span class="n"&gt;license_at_capture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;captured_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;  &lt;span class="c1"&gt;# UTC, always
&lt;/span&gt;    &lt;span class="n"&gt;session_geo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;      &lt;span class="c1"&gt;# {"country": ..., "city": ..., "asn": ...}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything beyond these four is optional. Anything missing from these four makes the row unanswerable later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write it in the same transaction as the clip
&lt;/h2&gt;

&lt;p&gt;The mistake is a two-phase design: download first, enrich later. Enrichment never happens. The provenance block has to be written in the same code path that saves the clip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_clip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prov&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Provenance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;license_at_capture&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;license_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;captured_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;session_geo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# from the proxy session itself
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;provenance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prov&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;session.geo&lt;/code&gt; is the interesting part. If you collect through a residential proxy network, the session's exit country, city, and ASN are known at request time — providers like Thordata expose session targeting directly, so the geo column needs no separate IP-lookup step (their free trial is here if you want to try the pattern: &lt;a href="https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1" rel="noopener noreferrer"&gt;https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1&lt;/a&gt;). Logging it costs one dictionary write and makes the row auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reject rows without provenance
&lt;/h2&gt;

&lt;p&gt;Schema enforcement is what separates a label from a hope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_constraint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provenance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_missing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A rejected write at collection time costs one clip. A missing field discovered at audit time costs the re-collection of the entire dataset, because backfilling a source URL you never saved means re-downloading the clip and re-checking the license — you pay for the same row twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dedup dividend
&lt;/h2&gt;

&lt;p&gt;One side effect: &lt;code&gt;source_url&lt;/code&gt; plus a content hash gives you cheap dedup across collection runs. Most "duplicate" rows I've cleaned up were the same clip collected twice through different exits — the label catches it before you store the second copy.&lt;/p&gt;

&lt;p&gt;Provenance is the cheapest field you'll ever add and the most expensive one to backfill. Write it at collection time.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Video Scraping Is a Bandwidth Problem: Four Rules for Proxying Large-File Collection</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Tue, 01 Sep 2026 08:38:24 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/video-scraping-is-a-bandwidth-problem-four-rules-for-proxying-large-file-collection-1ele</link>
      <guid>https://dev.to/rhea_hollis_/video-scraping-is-a-bandwidth-problem-four-rules-for-proxying-large-file-collection-1ele</guid>
      <description>&lt;p&gt;Most proxy advice is written for text: rotate per request, retry on failure, move on. The first time you point that stack at video, it falls apart in new and expensive ways. Four rules that fixed it for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 1: One sticky session per file
&lt;/h2&gt;

&lt;p&gt;An IP switch mid-download means a restart or a corrupted file — you pay for the same bytes twice. Bind the session ID to the file, not the job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pseudo-Python
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;video&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_session_id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;          &lt;span class="c1"&gt;# sticky, up to 90 min
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;                    &lt;span class="c1"&gt;# completed on ONE ip
&lt;/span&gt;    &lt;span class="nf"&gt;release_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;# fresh ip for the NEXT file
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ninety minutes of stickiness covers most clips end-to-end; anything longer belongs in a resumable-download design, not a longer session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 2: Rotate between files, never during one
&lt;/h2&gt;

&lt;p&gt;Address diversity still matters — you're just moving the rotation boundary. Fresh IP per file gives you the same spread as per-request rotation would, without ever breaking a transfer in progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 3: Parallelize by file, and check your concurrency cap
&lt;/h2&gt;

&lt;p&gt;Video throughput comes from per-file parallelism: one lane per file, each on its own sticky session. The bottleneck is almost never your code — it's providers that cap concurrent sessions. Pools without such caps exist — Thordata's residential network, for one (unlimited concurrency, 100M+ IPs; trial traffic here: &lt;a href="https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1" rel="noopener noreferrer"&gt;https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1&lt;/a&gt;) — but the pattern is provider-agnostic: verify the cap, then size your worker pool to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 4: Budget in cost-per-file, not cost-per-month
&lt;/h2&gt;

&lt;p&gt;At $0.65/GB, a 50MB clip costs ~3.3 cents and a 20,000-clip run costs ~$650 in bandwidth. Multiply average file size by count by price before every run — and remember that every restarted file is bandwidth you pay for twice. Cheap-but-flaky pools are the expensive ones.&lt;/p&gt;




&lt;p&gt;The mental shift that ties it together: with text, the proxy is a per-request anonymizer. With video, it's a transfer layer — and transfer layers are engineered around sessions, lanes, and bandwidth, not around hiding.&lt;/p&gt;

&lt;p&gt;If you're evaluating providers for large-file collection, these four rules are the checklist — and a trial with a session-control provider like Thordata (trial traffic: &lt;a href="https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1" rel="noopener noreferrer"&gt;https://www.thordata.com/?ls=dev&amp;amp;lk=dev-1&lt;/a&gt;) is a cheap way to validate them before you commit bandwidth.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>scraping</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From Video URLs to Training Records: How a Model-Ready Video Dataset Is Structured</title>
      <dc:creator>rhea hollis</dc:creator>
      <pubDate>Mon, 31 Aug 2026 07:21:46 +0000</pubDate>
      <link>https://dev.to/rhea_hollis_/from-video-urls-to-training-records-how-a-model-ready-video-dataset-is-structured-1gcd</link>
      <guid>https://dev.to/rhea_hollis_/from-video-urls-to-training-records-how-a-model-ready-video-dataset-is-structured-1gcd</guid>
      <description>&lt;p&gt;Most “video dataset” posts show you a folder of .mp4 files. That's the wrong abstraction. If you're training a VLM or building a retrieval system, the video file is one field in a record — and the quality of everything around it decides whether your pipeline works. Here's how we structure records in the video datasets we just launched at Thordata, and the engineering choices behind it.&lt;/p&gt;

&lt;p&gt;The record, not the file. Each video maps to a structured entry with four layers:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "video": {&lt;br&gt;
    "title": "...",&lt;br&gt;
    "description": "...",&lt;br&gt;
    "publish_time": "...",&lt;br&gt;
    "category_labels": ["..."],&lt;br&gt;
    "engagement": { "views": 0, "likes": 0 }&lt;br&gt;
  },&lt;br&gt;
  "creator": {&lt;br&gt;
    "channel_id": "UC...",&lt;br&gt;
    "handle": "@...",&lt;br&gt;
    "channel_url": "..."&lt;br&gt;
  },&lt;br&gt;
  "multimodal": {&lt;br&gt;
    "captions": "...",&lt;br&gt;
    "transcript": "...",&lt;br&gt;
    "audio_scene_info": "..."&lt;br&gt;
  },&lt;br&gt;
  "provenance": {&lt;br&gt;
    "platform": "youtube",&lt;br&gt;
    "video_url": "https://...",&lt;br&gt;
    "platform_video_id": "..."&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Provenance is a first-class layer, not an afterthought — retroactive provenance means re-collecting the dataset. Field availability varies by platform and dataset scope, so treat the schema as a contract you inspect per dataset, not a universal constant.&lt;/p&gt;

&lt;p&gt;Formats chosen per data type. Metadata and transcripts ship as JSON; tabular analysis works better in CSV or Parquet, and Parquet in particular pays off once you're filtering millions of rows by category or language. Video files deliver as .mkv or .mp4, audio as .m4a or .mp3. Don't force one format onto everything — pick per data type, and export all three metadata formats from the same source of truth.&lt;/p&gt;

&lt;p&gt;Delivery is engineering, not logistics. Datasets land where your stack already reads from: Amazon S3, Azure Blob, GCS, SFTP, or Webhook, on-demand or on a custom schedule. On our side that means 8–10 PB of daily delivery capacity; on your side it means zero new ingestion code. If your workflow is API-shaped instead of batch-shaped, on-demand retrieval with field-level filtering hits the same records.&lt;/p&gt;

&lt;p&gt;Sizing the corpus. 17B+ indexed video URLs across 700M+ channels in 100+ languages is the ceiling, not the unit of purchase. The practical move: pull a sample, inspect the schema and field coverage for your platforms, define filters (content type, upload date, view metrics, quality), validate with a test batch, then scale to full delivery. Custom collection covers the gaps the predefined datasets don't.&lt;/p&gt;

&lt;p&gt;Quality notes worth knowing. Videos deliver up to 2K resolution, audio at the best available source quality, and every dataset we ship is consent-approved content cleared for AI training — check that box before you check any other.&lt;/p&gt;

&lt;p&gt;The mental model that ties it together: treat a video dataset like a database with media attachments, not a media folder with a spreadsheet. Your training loop, your dedup jobs, and your future self will all benefit.&lt;/p&gt;

&lt;p&gt;We just launched these datasets at Thordata — sample access and docs:&lt;a href="https://www.thordata.com/products/multi-platform-video-datasets?op=rhea&amp;amp;from=x" rel="noopener noreferrer"&gt;https://www.thordata.com/products/multi-platform-video-datasets?op=rhea&amp;amp;from=x&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
