DEV Community

carl kevin
carl kevin

Posted on Fully Autonomous

What a Four-Column Social Comment CSV Can—and Cannot—Tell You

Disclosure: This tutorial and its code were prepared with AI assistance by the CommentTok editorial team, which publishes the exporter discussed below. The sample data is fictional, and the code was tested against the included fixture.

A CSV with author, handle, comment, and likes is enough to inspect a captured discussion and identify questions to review. It is not enough to establish complete coverage, recover reply relationships, or count distinct comments across repeated captures.

That distinction belongs in the code, not just a footnote in a report. This tutorial builds a small Python validator that produces a JSON summary, flags possible repeated records, and deliberately leaves coverage as unknown.

What you'll build: A local, read-only CSV analysis script using Python's standard library. It preserves the input, identifies structural problems, and reports repeated handle/text combinations without deleting them.

Start with the schema's actual meaning

This example follows a real browser exporter whose four columns are fixed. It writes UTF-8 with a byte-order mark, surrounds each value with double quotes, and doubles embedded quote characters. The tutorial data below is entirely fictional: anonymous example labels, invented comments, and invented like counts. It contains no captured user data.

Field Useful interpretation Unsupported conclusion
author Display name returned with the record A stable identity
handle Handle string returned with the record A verified unique person
comment Text available in this capture A unique comment identifier
likes Like count reported for this row A measure of all audience opinion

There is no comment ID, parent ID, posting timestamp, source URL, or capture timestamp in this file. A value visible in a product interface does not necessarily exist in its export schema.

The underlying capture can request replies, but the flat file cannot show which parent a reply belongs to. A successful download also says nothing about how much of the original discussion was accessible. Acquisition limits and schema limits are separate problems; fixing one does not fix the other.

Create a deliberately awkward fixture

Save this as example-comments.csv. The line break in the final comment is intentional. Do not flatten it.

author,handle,comment,likes
"Example A","example_a","Does this come in blue, too?","4"
"Example B","example_b","How do I clean it?","2"
"Example A","example_a","Does this come in blue, too?","5"
"Example C","example_c","The label says ""hand wash"".","1"
"Example D","example_d","First line
second line","0"
Enter fullscreen mode Exit fullscreen mode

These five records exercise three parsing cases: a comma inside text, a quoted phrase, and a multiline field. There is also a repeated handle/text pair with different likes.

Do not parse this with line.split(','). The comma in the first comment is part of the comment. Counting physical lines also gives the wrong number of records because the last record spans two lines.

Python's csv documentation recommends opening CSV files with newline=''. DictReader maps fields to header names and leaves values as strings by default. We will convert only the numeric field we intentionally validate.

Validate first, then summarize

Save the following as analyze_comment_csv.py. It uses no external packages and makes no network requests. A Python 3 installation and a terminal are sufficient.

The script reads the file into memory because this is a small-file tutorial. Its SHA-256 fingerprint covers the exact bytes being parsed. A fingerprint identifies a particular byte sequence; it does not prove who collected the file or whether the content is complete.

"""Validate a small four-column CSV and print a bounded JSON summary.

Usage: python3 analyze_comment_csv.py comments.csv
No network calls, third-party packages, or changes to the input file.
"""
import csv
import hashlib
import io
import json
import re
import sys
from collections import Counter
from pathlib import Path

FIELDS = ["author", "handle", "comment", "likes"]


def analyze(path):
    raw = Path(path).read_bytes()
    stream = io.StringIO(raw.decode("utf-8-sig"), newline="")
    reader = csv.DictReader(stream, strict=True)
    if reader.fieldnames != FIELDS:
        raise ValueError("Expected exactly: author,handle,comment,likes")

    rows = []
    for record_number, row in enumerate(reader, start=1):
        if None in row or any(value is None for value in row.values()):
            raise ValueError(f"Record {record_number}: wrong number of fields")
        if not re.fullmatch(r"[0-9]+", row["likes"]):
            raise ValueError(f"Record {record_number}: likes must be a nonnegative integer")
        rows.append(row)

    candidates = Counter((row["handle"], row["comment"]) for row in rows)
    return {
        "sha256": hashlib.sha256(raw).hexdigest(),
        "rows": len(rows),
        "distinct_nonempty_handle_strings": len({r["handle"] for r in rows if r["handle"]}),
        "blank_handle_rows": sum(not r["handle"] for r in rows),
        "blank_comment_rows": sum(not r["comment"].strip() for r in rows),
        "likes_sum_across_rows": sum(int(r["likes"]) for r in rows),
        "repeated_handle_text_groups": sum(n > 1 for n in candidates.values()),
        "extra_rows_in_repeated_handle_text_groups": sum(n - 1 for n in candidates.values()),
        "deduplication_performed": False,
        "coverage": "unknown",
    }


if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python3 analyze_comment_csv.py comments.csv")
    try:
        result = analyze(sys.argv[1])
    except (OSError, UnicodeError, csv.Error, ValueError) as exc:
        raise SystemExit(f"Cannot analyze CSV: {exc}")
    print(json.dumps(result, indent=2))
Enter fullscreen mode Exit fullscreen mode

Three choices are worth keeping when adapting this:

  1. Require the expected header. An unexpected delimiter, renamed field, extra column, or missing file header should not silently produce a convincing summary.
  2. Validate numeric strings. An empty or malformed like count triggers an error instead of becoming a guessed zero. This tutorial's contract accepts nonnegative integer strings only.
  3. Count candidates without removing them. Matching a handle and comment text is a review signal. It is not proof of duplicate identity.

The utf-8-sig decoder accepts ordinary UTF-8 and skips an initial UTF-8 byte-order mark when present. That matters when matching the first header exactly. See Python's encoding documentation.

Run it and interpret the output

python3 analyze_comment_csv.py example-comments.csv
Enter fullscreen mode Exit fullscreen mode

Besides the file fingerprint, the output should contain:

{
  "rows": 5,
  "distinct_nonempty_handle_strings": 4,
  "blank_handle_rows": 0,
  "blank_comment_rows": 0,
  "likes_sum_across_rows": 12,
  "repeated_handle_text_groups": 1,
  "extra_rows_in_repeated_handle_text_groups": 1,
  "deduplication_performed": false,
  "coverage": "unknown"
}
Enter fullscreen mode Exit fullscreen mode

Those values describe only the fictional fixture. The script intentionally says “handle strings” rather than “people” and “likes sum across rows” rather than “total engagement.” If the same comment appears twice, adding its reported counts can double-count activity. Conversely, two identical texts from the same handle could be separate postings.

Consider the first and third rows. There are at least two plausible explanations: one comment was captured twice as its like count changed, or the same account posted the same question twice. Nothing in these four columns resolves that ambiguity. Keeping whichever row has more likes would impose an assumption, not discover the truth.

Even exact matches across all four columns do not solve it. Separate comments can share all four values. The correct action depends on your analysis goal and additional evidence, which is why this script never drops rows automatically.

Add provenance before combining files

Keep the original export unchanged. For each capture, maintain a small companion record with its source video URL, collection time including time zone, file fingerprint, and any status message or limitation noticed during collection.

If you create a combined working table, carry that capture metadata onto each row. Use a local key such as (file_fingerprint, record_number) to find the row again in your saved material. This locates a record in a file; it is not a platform comment ID.

Do not fill a missing collection time with today's time and label it “captured at.” Record it as unknown, or distinguish the time you received the file from the time the original capture occurred. Neither value substitutes for the comment's posting time.

For larger inputs, you could stream records and hash the input while reading. That is a separate engineering improvement; it does not remove the need to define provenance and identity.

Add themes through an explicit review process

For a small set, a human review column can be more useful than immediately adding an automatic sentiment model. Define labels against a concrete question, keep the original text, and include an “unclear” label.

In the fictional fixture, possible themes include “available colours,” “care instructions,” and “needs context.” Decide whether multiple labels are allowed before counting them. If one row can have two labels, the sum of theme counts may exceed the number of rows; that is acceptable when reported clearly.

Avoid a rule that labels every comment containing a question mark as a buying objection. A question can ask for instructions, make a joke, or refer to an absent parent comment. Missing context remains missing even when a classifier produces a confident answer.

Report the unit you actually reviewed: for example, “five rows in this fixture.” Do not silently convert rows into distinct comments, handles into people, or the captured set into the entire audience.

Keep text as data throughout the workflow

The Python script parses strings and prints only aggregate JSON; it never evaluates comment text. This does not certify the original CSV as safe for every downstream application.

If you open unfamiliar comments in a spreadsheet, use an import path that preserves the text fields as literal text instead of allowing formula interpretation. CSV quotation protects field boundaries. It is not the same operation as preventing spreadsheet formulas. This exporter does not claim to sanitize formula-like values.

Before trusting a new file, check that malformed numeric values fail, multiline comments remain single records, and candidate repeats remain in the original. The useful output is a summary whose boundaries are visible enough for the next person to assess.


Written by the CommentTok editorial team. Product affiliation: the team publishes the exporter discussed here. This article and code were prepared with AI assistance. All sample rows and sample metrics are fictional; the code was run against the included fixture. This is a standalone technical tutorial.

Top comments (0)