Entity Extraction with spaCy

Master entity extraction with spaCy in this hands-on Applied AI engineering tutorial. Learn core concepts, practical steps, troubleshooting, and next steps.

Focus: entity extraction with spacy

Sponsored

You've got a messy pile of text — emails, support tickets, legal docs — and you need to pull out the people, places, and organizations mentioned in it. Doing that by hand is soul-crushing and slow. That's the pain this lesson kills: entity extraction with spaCy lets you automate named-entity recognition (NER) in a few lines of Python, turning raw text into structured data you can filter, search, or feed into other AI pipelines. By the end of this lesson, you'll not only understand how spaCy's NER engine works, but you'll have written a working extraction script and know exactly when to reach for it versus other approaches.

The problem this lesson solves

When your app processes free-form text, you're drowning in unstructured data. A support ticket contains a customer name, a product ID, a date, and maybe an urgent issue — but they're all glued together in prose. Manually parsing that is impractical at scale, and even simple regex rules fail when language gets creative: "Apple" could be a company or a fruit; "Paris" could be a city or a person's name.

That's where entity extraction (also called named-entity recognition or NER) steps in. NER is a subtask of natural language processing (NLP) that identifies spans of text that refer to real-world entities and classifies them into categories like PERSON, GPE (geopolitical entity, e.g., countries/cities), ORG (organizations), DATE, MONEY, and more. spaCy is a popular Python NLP library that ships with pre-trained models capable of doing this out of the box, with a clean and fast API.

This lesson directly supports the Applied AI engineering track. Before this, you've likely worked with text preprocessing and basic NLP concepts; after this, you'll be ready to integrate entity extraction into retrieval pipelines, chatbots, or data processing systems.

Core concept / mental model

Think of spaCy's NER as a highlighter for meaning. You feed it a sentence, and it paints each recognized entity with a color-coded category. The model doesn't use rule-based patterns (unless you add them); it learns from thousands of labeled examples to predict which tokens form an entity and what type it is.

Here's the mental model to hold onto:

  • Entities are spans — contiguous sequences of tokens that together form a single named entity. For example, in "New York City", the three tokens belong to one entity of type GPE.
  • NER is a sequence labeling task — spaCy processes text token-by-token and decides for each token whether it starts, continues, or is outside an entity.
  • ​Models are language-specific and pipeline-based — spaCy's en_core_web_sm is a compact English model that processes text in a pipeline: sentence-boundary detection, part-of-speech tagging, dependency parsing, and finally NER.

Pro tip: NER in spaCy is not magic — it's a trained statistical model. The quality depends on the training data and the model size. For domain-specific text (like medical records or financial filings), you'll likely need to fine-tune or add custom rules.

Another way to visualize it: a text document is a map, and entities are the landmarks. spaCy's NER tags each landmark with a label — "this is a city", "this is a person" — so you can navigate the text without reading every word.

How it works step by step

Let's break down the process of using spaCy for entity extraction into clear steps:

  1. Install spaCy and download a model — You need the library and a language pipeline. The smallest English model is en_core_web_sm, but for better accuracy you might use en_core_web_md or en_core_web_lg (the larger, the more accurate but slower).
  2. Load the pipelinenlp = spacy.load("en_core_web_sm") gives you an nlp object that processes raw text.
  3. Process the text — Call doc = nlp("text here"). This runs the entire pipeline and produces a Doc object that holds the tokens, their linguistic features, and the entities.
  4. Access entities — Iterate over doc.ents to get each entity span. Each entity has .text (string), .label_ (like "PERSON"), and .start_char / .end_char (character offsets in the original text).
  5. Format or store results — Convert entities into a list of dictionaries, JSON, or a Pandas DataFrame for further use.

Understanding the pipeline

The en_core_web_sm pipeline is a series of components: a tok2vec layer that turns words into vectors, a tagger for part-of-speech, a parser for dependencies, and an NER component that identifies entities. When you call nlp(text), all components run in sequence and enrich the Doc.

You can also disable components you don't need to speed things up:

nlp = spacy.load("en_core_web_sm", disable=["parser", "tagger"])

But note: the NER component may rely on features from earlier components for best accuracy, and disabling them can hurt performance. For most tasks, keep the default pipeline.

Entity types you'll see

spaCy's standard model includes a range of entity types, but you'll commonly encounter these:

Type Meaning Example
PERSON People, including fictional "Ada Lovelace"
ORG Companies, agencies, institutions "OpenAI"
GPE Countries, cities, states "Berlin"
LOC Non-GPE locations, mountain ranges "Mount Everest"
DATE Absolute or relative dates "last Monday"
TIME Times smaller than a day "9:00 AM"
MONEY Monetary values "$25,000"
PRODUCT Objects, vehicles, foods "iPhone"

Hands-on walkthrough

Now let's put theory into practice. You'll write a script that extracts entities from a sample text and outputs them in a clean format.

Step 1: Install and load the model

First, ensure you have spaCy installed and the model downloaded:

pip install spacy
python -m spacy download en_core_web_sm

If you're in a Jupyter notebook, you can also run those with ! prefix.

Step 2: Extract entities from a sample text

Create a Python script or notebook cell:

import spacy

# Load the small English pipeline
nlp = spacy.load("en_core_web_sm")

text = """Microsoft Corporation announced today that CEO Satya Nadella will visit Paris next Monday. The company plans to invest €1.5 million in a new AI research lab."""

doc = nlp(text)

# Iterate over detected entities
for ent in doc.ents:
    print(f"{ent.text:<30} {ent.label_:<10} {ent.start_char:>3}–{ent.end_char:<3}")

Expected output

Microsoft Corporation        ORG         0–21
Satya Nadella                PERSON     45–58
Paris                        GPE        73–78
next Monday                  DATE       87–98
€1.5 million                 MONEY      149–160

Notice how "next Monday" is treated as a single DATE entity, and "€1.5 million" as a MONEY entity. The character offsets let you locate the entity in the original string.

Step 3: Convert to structured data

For real-world use, you'll want a dictionary or JSON output. Here's how to build a list of entity dictionaries:

entities = [
    {"text": ent.text, "label": ent.label_, "start": ent.start_char, "end": ent.end_char}
    for ent in doc.ents
]

import json
print(json.dumps(entities, indent=2))

Expected output

[
  {
    "text": "Microsoft Corporation",
    "label": "ORG",
    "start": 0,
    "end": 21
  },
  {
    "text": "Satya Nadella",
    "label": "PERSON",
    "start": 45,
    "end": 58
  },
  ...
]

Step 4: Visualize entities (optional but useful)

spaCy ships with displacy, a visualizer that renders entities in a browser:

from spacy import displacy

displacy.render(doc, style="ent", jupyter=True)  # in Jupyter
# Or save to HTML: displacy.serve(doc, style="ent")

This is a great debugging aid to see what the model caught and missed.

Compare options / when to choose what

spaCy's built-in NER is not the only way to extract entities. Depending on your use case, you might consider alternatives:

Approach Pros Cons Best for
spaCy NER (pre-trained) Fast, easy, covers common categories, works out of the box General model may miss domain-specific entities, requires fine-tuning for custom types General-purpose extraction in production apps, quick prototyping
Custom spaCy NER (training your own model) Can learn domain entities (e.g., product names, medical terms) Requires large labeled dataset, training time, more expertise Niche domains where accuracy is critical
LLM-based extraction (e.g., GPT-4 with structured outputs) Handles ambiguous text, can follow custom schemas, zero-shot capability Slower, higher cost, non-deterministic, needs careful prompt engineering Complex entities, when you need context understanding, or when you want extraction + reasoning
Regex / rule-based Instant, zero overhead, full control Fragile, misses variations, doesn't handle context Simple, well-defined patterns (e.g., email addresses, IDs)

Deciding factor

For most pipeline starters, spaCy's pre-trained NER is the sweet spot: it's fast, deterministic, and good enough to bootstrap. If you need higher accuracy on specialized text, you can either fine-tune spaCy or pair it with an LLM for verification.

Pro tip: Start with spaCy's default model, evaluate its output on a sample of your real data, and only then invest in fine-tuning or an LLM. Better to measure than assume.

Troubleshooting & edge cases

Even seasoned developers hit snags. Here are common problems and how to fix them:

  1. Model not found error — If you get OSError: [E050] Can't find model 'en_core_web_sm', it means the model isn't downloaded. Run python -m spacy download en_core_web_sm and make sure you're in the same Python environment.
  2. Entities are merged or split incorrectly — The model may group "Paris Hilton" as a PERSON instead of GPE + PERSON, or fail to split "New York" from "New York Times". This is a model limitation. You can fix it by adding rules with nlp.add_pipe("entity_ruler", before="ner") to override patterns for your domain.
  3. Misclassification due to ambiguous words — "Apple" is often labeled ORG, but in a fruit context it might be a PRODUCT or just not an entity. The model can't reliably disambiguate without context. Consider using a larger model (en_core_web_lg) or a custom pipeline.
  4. Entities are missing entirely — If you expect entities and none appear, the text might be too short or the model might confuse the language. Ensure you're using the right language model (e.g., en_core_web_sm for English). Also check that your input isn't truncated; entities long spans need enough context.
  5. Performance issues with large texts — NER on a 10MB document can be slow. Split the text into chunks (paragraphs) and process each with nlp.pipe(texts, batch_size=..) to speed up with parallel processing.

Example: Adding a custom rule to fix a recurring error

Suppose your data has many mentions of "PythonSkillset" and the model doesn't catch it as an entity. Here's how to add a rule:

import spacy
from spacy.pipeline import EntityRuler

nlp = spacy.load("en_core_web_sm")
ruler = nlp.add_pipe("entity_ruler", before="ner")
patterns = [{"label": "ORG", "pattern": "PythonSkillset"}]
ruler.add_patterns(patterns)

doc = nlp("PythonSkillset is the best learning platform.")
for ent in doc.ents:
    print(ent.text, ent.label_)  # Output: PythonSkillset ORG

Now the rule overrides the model and reliably tags PythonSkillset as an ORG.

What you learned & what's next

You've now understand entity extraction with spaCy from the ground up: you know the problem it solves, you have a mental model of how NER works inside spaCy's pipeline, you walked through hands-on extraction steps, compared it to alternatives, and learned how to fix common pitfalls. You can explain the core idea behind entity extraction with spaCy and you've completed a practical exercise that extracts entities and converts them to structured data.

Your next step in the Applied AI engineering track is to integrate entity extraction into a larger application. For example, you might combine it with retrieval-augmented generation where you extract entities from queries to filter your knowledge base, or use it in a data processing pipeline to automatically tag documents. The natural next lesson in this track is likely "Building an entity-based search" or "Fine-tuning spaCy NER for custom domains" — either way, you now have the core skill to make that leap.

Keep practicing by running the script on your own texts, and don't forget to check the official spaCy documentation for more advanced options like training your own model.

Practice recap

Try extracting entities from a text of your choice, such as a news article, and print them as a table. Then identify two entities that the model got wrong and fix them using the EntityRuler as shown above. This hands-on practice will solidify your understanding and prepare you for the next lesson in the track.

Common mistakes

  • Forgetting to download the language model before loading it — always run python -m spacy download en_core_web_sm.
  • Assuming the pre-trained model knows your specific domain entities — it doesn't. Add custom rules or fine-tune if you need custom types.
  • Iterating doc.ents and expecting all tokens to belong to an entity — many tokens are not entities; that's expected.
  • Using a small model when accuracy matters — the default sm model is fast but may miss or misclassify entities; try md or lg for better coverage.
  • Treating entity offsets as token indices — they are character offsets; use doc.text[start:end] to get the substring, not slicing by token position.

Variations

  1. Use a larger spaCy model like en_core_web_lg for better accuracy at the cost of slower processing.
  2. Combine spaCy NER with an LLM (like a GPT model) to verify or fix ambiguous entities — get the best of both worlds.
  3. Build a custom NER model using spaCy's spacy.training API to recognize entities unique to your domain.

Real-world use cases

  • Automatically extract customer names, product IDs, and issue types from support tickets for routing and prioritization.
  • Index legal contracts to pull out parties, dates, and monetary amounts for compliance and automated review.
  • Extract medical entities like drug names and symptoms from clinical notes to power search and research analytics.

Key takeaways

  • Entity extraction with spaCy turns messy text into structured data by identifying spans of entities and labeling them.
  • spaCy's NER runs as part of a pipeline; you load a model, process text with nlp(), and access entities via doc.ents.
  • Each entity has .text, .label_, and character offsets — use these to build structured outputs like JSON.
  • Pre-trained models are a fast starting point, but they are not domain-specific; add custom rules or fine-tune when needed.
  • Choose the right tool: regex for simple patterns, spaCy for general extraction, LLMs for complex or ambiguous entities.
  • Troubleshooting NER is about tuning the model, adding rules, and testing on your own data — not re-inventing the wheel.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.