Skip to content

Parsers

Clean-room HTML -> M1 model parsers for vgmdb pages.

NotAProductPageError

Bases: ParseError

Raised when the HTML lacks the essential anchors of a product page.

Source code in src/vgmdb_client/parsers/errors.py
36
37
38
39
40
class NotAProductPageError(ParseError):
    """Raised when the HTML lacks the essential anchors of a product page."""

    def __init__(self) -> None:
        super().__init__("Not a vgmdb product page (missing product id or name).")

NotAnArtistPageError

Bases: ParseError

Raised when the HTML lacks the essential anchors of an artist page.

Source code in src/vgmdb_client/parsers/errors.py
29
30
31
32
33
class NotAnArtistPageError(ParseError):
    """Raised when the HTML lacks the essential anchors of an artist page."""

    def __init__(self) -> None:
        super().__init__("Not a vgmdb artist page (missing artist id or name).")

NotAnEventPageError

Bases: ParseError

Raised when the HTML lacks the essential anchors of an event page.

Source code in src/vgmdb_client/parsers/errors.py
50
51
52
53
54
class NotAnEventPageError(ParseError):
    """Raised when the HTML lacks the essential anchors of an event page."""

    def __init__(self) -> None:
        super().__init__("Not a vgmdb event page (missing event id or name).")

NotAnOrganizationPageError

Bases: ParseError

Raised when the HTML lacks the essential anchors of an organization page.

Source code in src/vgmdb_client/parsers/errors.py
43
44
45
46
47
class NotAnOrganizationPageError(ParseError):
    """Raised when the HTML lacks the essential anchors of an organization page."""

    def __init__(self) -> None:
        super().__init__("Not a vgmdb organization page (missing organization id or name).")

ParseError

Bases: VgmdbClientError

Raised when HTML is not a recognizable album/search page.

Source code in src/vgmdb_client/parsers/errors.py
 8
 9
10
11
12
class ParseError(VgmdbClientError):
    """Raised when HTML is not a recognizable album/search page."""

    def __init__(self, message: str = "Could not parse the page.") -> None:
        super().__init__(message)

parse_album(html)

Parse a captured vgmdb album page into an :class:Album (clean-room selectors).

Source code in src/vgmdb_client/parsers/album.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def parse_album(html: str) -> Album:
    """Parse a captured vgmdb album page into an :class:`Album` (clean-room selectors)."""
    tree = _dom.parse_tree(html)

    canonical = tree.xpath('//link[@rel="canonical"]/@href')
    match = _ALBUM_ID.search(canonical[0]) if canonical else None
    if match is None:
        raise NotAnAlbumPageError()
    album_id = int(match.group(1))

    titles = _dom.localized_text(tree.xpath('//h1/span[@class="albumtitle"]'))
    if not titles.all:
        raise NotAnAlbumPageError()

    info = _info_fields(tree)
    cover_full = _cover_url(tree, "medium-media.vgm.io", album_id)
    cover_small = _cover_url(tree, "thumb-media.vgm.io", album_id)

    return Album(
        id=album_id,
        link=canonical[0],
        titles=titles,
        catalog=info.get("Catalog Number"),
        release_date=_dom.partial_date(info.get("Release Date href")) or _dom.partial_date(info.get("Release Date")),
        classification=info.get("Classification"),
        cover_small=cover_small,
        cover_full=cover_full,
        discs=_discs(tree),
        credits=_credits(tree),
        notes=_notes(tree),
        release_event=_release_event(tree),
    )

parse_artist(html)

Parse a captured vgmdb artist page into an :class:Artist (clean-room selectors).

Source code in src/vgmdb_client/parsers/artist.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def parse_artist(html: str) -> Artist:
    """Parse a captured vgmdb artist page into an :class:`Artist` (clean-room selectors)."""
    tree = _dom.parse_tree(html)

    canonical = tree.xpath('//link[@rel="canonical"]/@href')
    match = _ARTIST_ID.search(canonical[0]) if canonical else None
    if match is None:
        raise NotAnArtistPageError()
    artist_id = int(match.group(1))

    names = _name(tree)
    if not names.all:
        raise NotAnArtistPageError()

    fields = _info_fields(tree)
    return Artist(
        id=artist_id,
        link=canonical[0],
        names=names,
        aliases=_aliases(fields.get("Aliases")),
        type=None,
        birthdate=_dom.partial_date(_field_text(fields.get("Birthdate"), "Birthdate")),
        notes=None,
        members=_refs(fields.get("Members")),
        units=_refs(fields.get("Units")),
    )

parse_event(html)

Parse a captured vgmdb event page into an :class:Event (clean-room selectors).

Source code in src/vgmdb_client/parsers/event.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def parse_event(html: str) -> Event:
    """Parse a captured vgmdb event page into an :class:`Event` (clean-room selectors)."""
    tree = _dom.parse_tree(html)

    canonical = tree.xpath('//link[@rel="canonical"]/@href')
    match = _EVENT_ID.search(canonical[0]) if canonical else None
    if match is None:
        raise NotAnEventPageError()

    # No <h1> on event pages; the event's own name is the leading group of albumtitle spans
    # (the page also lists related-album titles in the same class).
    names = _dom.localized_text(_leading_group(tree.xpath('//span[@class="albumtitle"]')))
    if not names.all:
        raise NotAnEventPageError()

    return Event(
        id=int(match.group(1)),
        link=canonical[0],
        names=names,
        type=None,
        start_date=_event_date(tree),
        end_date=None,
        notes=None,
    )

parse_organization(html)

Parse a captured vgmdb organization page into an :class:Organization (clean-room selectors).

Source code in src/vgmdb_client/parsers/organization.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def parse_organization(html: str) -> Organization:
    """Parse a captured vgmdb organization page into an :class:`Organization` (clean-room selectors)."""
    tree = _dom.parse_tree(html)

    canonical = tree.xpath('//link[@rel="canonical"]/@href')
    match = _ORG_ID.search(canonical[0]) if canonical else None
    if match is None:
        raise NotAnOrganizationPageError()

    names = _name(tree)
    if not names.all:
        raise NotAnOrganizationPageError()

    return Organization(
        id=int(match.group(1)),
        link=canonical[0],
        names=names,
        type=_field_text(tree, "Type"),
        notes=_field_text(tree, "Description"),
    )

parse_product(html)

Parse a captured vgmdb product page into a :class:Product (clean-room selectors).

Source code in src/vgmdb_client/parsers/product.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def parse_product(html: str) -> Product:
    """Parse a captured vgmdb product page into a :class:`Product` (clean-room selectors)."""
    tree = _dom.parse_tree(html)

    canonical = tree.xpath('//link[@rel="canonical"]/@href')
    match = _PRODUCT_ID.search(canonical[0]) if canonical else None
    if match is None:
        raise NotAProductPageError()

    # Scope the name to the <h1> heading: other albumtitle spans (franchise/discography values) live
    # outside it, so the heading holds only the product's own multilingual name.
    names = _dom.localized_text(_leading_group(tree.xpath('//h1//span[@class="albumtitle"]')))
    if not names.all:
        raise NotAProductPageError()

    return Product(
        id=int(match.group(1)),
        link=canonical[0],
        names=names,
        type=None,
        notes=None,
        franchises=_franchises(tree),
        organizations=_organizations(tree),
    )

Parse a captured vgmdb search-results page into :class:SearchResults.

Source code in src/vgmdb_client/parsers/search.py
17
18
19
20
21
22
23
24
25
26
27
def parse_search(html: str) -> SearchResults:
    """Parse a captured vgmdb search-results page into :class:`SearchResults`."""
    tree = _dom.parse_tree(html)

    container = tree.xpath('//*[@id="albumresults"]')
    if not container:
        raise NotASearchPageError()

    query = _query(tree)
    albums = [_result_row(row) for row in container[0].xpath('.//table[contains(@class, "results")]//tr[@rel]')]
    return SearchResults(query=query, albums=[a for a in albums if a is not None])