wiktextract

repository·master·Indexed 22 days ago

https://github.com/tatuylonen/wiktextract

A high-fidelity Python-based utility and library for extracting structured linguistic data from Wiktionary XML dumps. It expands Lua macros and templates to provide accurate word senses, pronunciations, and morphological information, outputting data in JSON Lines (.jsonl) format. Key features include extraction of parts-of-speech, declensions, conjugations, and translations, with support for both a command-line tool (wiktwords) and a Python API.

Tokens
20.1K
Snippets
43
Records
82
Agent score
78%

What's inside wiktextract

  1. Overview of Wiktextract

    master

    Wiktextract is a Python package and command-line tool designed to extract structured information from Wiktionary data dumps. Unlike many other extractors, it expands Wiktionary templates and Lua macros, which allows for much higher accuracy in extracting glosses, word senses, inflected forms, pronunciations, and semantic annotations (like colors, numbers, or SI units).

    Key features include:

    • High Fidelity: Expands Lua modules for superior extraction quality.
    • Comprehensive Data: Extracts parts-of-speech, declension/conjugation, translations, pronunciations (with audio links), qualifiers, and links to Wikipedia/Wikidata.
    • Output Format: Produces JSON Lines (.jsonl) files, where each line is a separate JSON object representing a word entry.
    • Versatility: Useful for NLP, machine translation, and morphological research.
  2. Understand Wiktextract performance and scaling

    master

    Extraction performance is approximately linear with the number of processor cores, provided you have sufficient memory.

    Memory Requirements:

    • It is recommended to have approximately 10GB of RAM per core (or 5GB per hyperthread).

    Scaling:

    • You can control the level of parallelism using the --num-processes option. By default, the tool uses all available cores/hyperthreads.
    • Note that expanding Lua modules is computationally expensive but necessary for high-quality extraction.
    • Extracting the entire English Wiktionary can take anywhere from one hour to several days depending on your hardware.
  3. Understand the format of extracted word entries

    master

    Each extracted word is represented as a dictionary. The primary keys available in a word entry include:

    • word: The word form.
    • pos: Part-of-speech (e.g., "noun", "verb", "adj"). See wiktextract.PARTS_OF_SPEECH for the full list.
    • lang: Language name (e.g., "English").
    • lang_code: Wiktionary language code (e.g., "en").
    • senses: A list of dictionaries representing word senses.
    • forms: A list of dictionaries for inflected or alternative forms (e.g., plural, comparative), containing form and tags keys.
    • sounds: A list of dictionaries for pronunciation, hyphenation, and rhyming information.
    • categories: List of non-disambiguated categories.
    • topics: List of non-disambiguated topics.
    • translations: Non-disambiguated translation entries.
    • etymology_text: Cleaned etymology text.
    • etymology_templates: List of templates and arguments from the etymology section.
    • etymology_number: The etymology number (as a string) for words with multiple etymologies.
    • descendants: List of descendant words.
    • synonyms, antonyms, hypernyms, holonyms, meronyms, derived, related, coordinate_terms: Lists of non-disambiguated linkages.
    • wikidata: Wikidata identifier.
    • wiktionary: Wikipedia page title.
    • head_templates: Part-of-speech specific head tags.
    • inflection_templates: Conjugation and declension templates.
  4. Understand the format of extracted redirects

    master

    When extracting data from Wiktionary, some pages are redirects. If redirect extraction is enabled, the extractor will return entries with a specific structure:

    • The dictionary contains a redirect key, which holds the page title that the entry redirects to.
    • The title key contains the word or term that triggers the redirect.
    • Redirect entries do not contain a pos (part of speech) key or any other standard entry fields.
    • Redirects are not associated with any specific language; they are returned regardless of the captured languages requested.
  5. Understand the WiktextractContext object

    master
    The WiktextractContext object acts as a container for the processing environment. It holds a wikitextprocessor.Wtp context and a WiktionaryConfig object. This design allows the library to pass around all necessary configuration and processing state in a single object.
  6. Three ways to extract template data

    master

    When building an extractor, you can handle Wikitext templates using one of three strategies depending on the template's behavior:

    1. Expand template and find data in expanded nodes: Use this when data is only available after expansion or is easier to extract from the resulting HTML/node structure (e.g., finding a specific <span> with a class). Use wxr.wtp.parse(node_to_wikitext(t_node), expand_all=True).
    2. Don't expand template, find data in template parameters: Use this when a template simply displays its parameters without complex logic. You can access the parameters directly from the TemplateNode.
    3. Convert template node to text using clean_node: Use this when the expanded template text can be used directly or requires minimal cleaning to become a usable string.
  7. Understand the structure of word senses

    master

    A word entry can contain multiple glosses under the senses key. Each sense is a dictionary containing:

    • glosses: Cleaned gloss strings.
    • raw_glosses: Less cleaned gloss strings (includes parenthesized tags/topics).
    • tags: List of qualifiers (e.g., "archaic", "colloquial").
    • categories / topics: Sense-disambiguated category and topic names.
    • alt_of: List of words this sense is an alternative form of.
    • form_of: List of words this sense is an inflected form of.
    • translations: Sense-disambiguated translation entries.
    • synonyms, antonyms, hypernyms, holonyms, meronyms, coordinate_terms, derived, related: Sense-disambiguated linkages.
    • senseid: Textual identifiers for the sense.
    • wikidata: List of QIDs (e.g., Q123).
    • wikipedia: List of Wikipedia page titles.
    • examples: List of usage examples. Each example dictionary contains text, and optionally ref, english (translation), type (example or quotation), roman, or note.
  8. Extract data from TemplateNodes

    master

    To extract data from a Wiktionary template, you typically expand the template into its HTML representation and then traverse the resulting nodes.

    Key steps in the extraction pattern:

    1. Expand the template: Use wxr.wtp.parse(wxr.wtp.node_to_wikitext(node), expand_all=True) to get the full HTML structure.
    2. Find specific elements: Use methods like find_html or find_html_recursively to locate specific tags (e.g., <span> with a specific class like Jpan or tr).
    3. Clean the content: Use clean_node(wxr, context, node) to extract the text content without the underlying markup.
    4. Calculate offsets: Use utility functions like calculate_bold_offsets to track the positions of bolded text within the cleaned strings.
    def extract_quote_template(
        wxr: WiktextractContext, sense: Sense, t_node: TemplateNode
    ) -> str:
        # ...
        expanded_node = wxr.wtp.parse(
            wxr.wtp.node_to_wikitext(node), expand_all=True
        )
        for span_tag in expanded_node.find_html_recursively("span"):
            span_class = span_tag.attrs.get("class", "")
            if "cited-source" == span_class:
                example_data.ref = clean_node(wxr, None, span_tag)
        # ...
        return example.ref
  9. Coordinate wiktextract and wikitextprocessor versions

    master
    Because wiktextract is built using the wikitextprocessor module, you should ensure both packages have version parity if you are installing from GitHub. Mixing a newer version of wiktextract with an older PyPI version of wikitextprocessor (or vice versa) can lead to bugs, as these packages are developed in parallel.
  10. Extract sound files and hyphenation in a new extractor

    master

    When building a new Wiktionary extractor, you can implement logic to extract audio URLs and hyphenation data by following these steps:

    1. Define Pydantic models: Extend your WordEntry model (inheriting from EnglishBaseModel) to include new fields like sounds: list[Sound] and hyphenations: list[Hyphenation].
    2. Implement extraction logic: Create functions that traverse the LevelNode and identify specific TemplateNode types (e.g., audio or hyphenation).
    3. Handle template expansion: Use wxr.wtp.parse(..., expand_all=True) on the template's wikitext to access the expanded HTML content, which is often necessary to find metadata like pronunciation labels or specific language spans.
    4. Register the section parser: Update the parse_section function in your page.py to check for specific section titles (e.g., "Pronunciation") and call your new extraction functions.

    Note: For audio, use set_sound_file_url_fields to automatically populate URL fields like mp3_url and ogg_url from a filename.

    # Example Pydantic models
    class Sound(EnglishBaseModel):
        audio: str = ""
        ogg_url: str = ""
        mp3_url: str = ""
        tags: list[str] = []
    
    class Hyphenation(EnglishBaseModel):
        parts: list[str] = []
    
    class WordEntry(EnglishBaseModel):
        sounds: list[Sound] = []
        hyphenations: list[Hyphenation] = []
    
    # Example section registration in page.py
    def parse_section(wxr, page_data, base_data, level_node):
        title_text = clean_node(wxr, None, level_node.largs)
        if title_text == "Pronunciation":
            extract_sound_section(wxr, base_data, level_node)
  11. Handle inconsistent Linkage section placement

    master

    Linkage sections (like Synonyms or Antonyms) may appear at different levels relative to POS sections.

    • If a linkage section is at the same level as a POS (e.g., LEVEL3), you may need to iterate through page_data to ensure the data is added to all relevant WordEntry objects for that language.
    • If it is a child of a POS section, it should only be added to the current WordEntry (the last item in page_data).
    def extract_linkage_section(
        wxr: WiktextractContext,
        page_data: list[WordEntry],
        level_node: LevelNode,
        linkage_type: str,
    ):
        linkage_data = []
        if level_node.kind == NodeKind.LEVEL3:
            for data in page_data:
                if data.lang_code == page_data[-1].lang_code:
                    getattr(data, linkage_type).extend(linkage_data)
        else:
            getattr(page_data[-1], linkage_type).extend(linkage_data)