The FullTextXMLParser provides comprehensive capabilities for extracting structured data from Europe PMC full-text XML documents.
from pyeuropepmc import FullTextXMLParser
# Load XML content
with open("article.xml") as f:
xml_content = f.read()
parser = FullTextXMLParser(xml_content)
# Extract metadata
metadata = parser.extract_metadata()
print(f"Title: {metadata['title']}")
print(f"Authors: {metadata['authors']}")
# Extract tables
tables = parser.extract_tables()
print(f"Found {len(tables)} tables")
# Convert to markdown
markdown = parser.to_markdown()
Extract comprehensive article metadata:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
metadata = parser.extract_metadata()
# Available metadata fields
title = metadata['title'] # Article title
authors = metadata['authors'] # List of author dicts
journal = metadata['journal'] # Journal info dict (title, volume, issue)
pub_date = metadata['publication_date'] # Publication date
doi = metadata['doi'] # Digital Object Identifier
pmid = metadata['pmid'] # PubMed ID
pmcid = metadata['pmcid'] # PMC ID
abstract = metadata['abstract'] # Abstract text
keywords = metadata['keywords'] # List of keywords
affiliations = metadata['affiliations'] # Author affiliations
# Author information
for author in authors:
print(f"{author['given_names']} {author['surname']}")
print(f" Affiliation: {author.get('affiliation', 'N/A')}")
print(f" Email: {author.get('email', 'N/A')}")
| Field | Type | Description |
|---|---|---|
title |
str | Article title |
authors |
list[dict] | Author information |
journal |
dict | Journal info with ‘title’, ‘volume’, ‘issue’ keys |
publication_date |
str | Publication date (ISO format) |
doi |
str | DOI identifier |
pmid |
str | PubMed ID |
pmcid |
str | PMC ID |
abstract |
str | Abstract text |
keywords |
list[str] | Article keywords |
affiliations |
list[str] | Author affiliations |
article_type |
str | Article type |
pages |
str | Page range |
copyright |
str | Copyright statement |
license |
str | License information |
Extract structured table data:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
tables = parser.extract_tables()
for i, table in enumerate(tables):
print(f"\nTable {i+1}")
print(f"Caption: {table['caption']}")
print(f"Label: {table['label']}")
# Table headers
headers = table['headers']
print(f"Headers: {headers}")
# Table data
for row in table['data']:
print(row)
Each table is returned as a dictionary:
{
'label': 'Table 1',
'caption': 'Patient demographics and baseline characteristics',
'headers': ['Parameter', 'Group A', 'Group B', 'P-value'],
'data': [
['Age (years)', '45.3 ± 12.1', '43.8 ± 11.5', '0.23'],
['Gender (M/F)', '12/8', '14/6', '0.51'],
# ...
]
}
# Convert table to pandas DataFrame
import pandas as pd
for table in tables:
df = pd.DataFrame(table['data'], columns=table['headers'])
print(f"\n{table['caption']}")
print(df)
# Save to CSV
df.to_csv(f"table_{table['label']}.csv", index=False)
Extract bibliography and citations:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
references = parser.extract_references()
for i, ref in enumerate(references, 1):
print(f"\n[{i}] {ref['title']}")
print(f" Authors: {ref['authors']}")
print(f" Journal: {ref['journal']}")
print(f" Year: {ref['year']}")
print(f" DOI: {ref.get('doi', 'N/A')}")
print(f" PMID: {ref.get('pmid', 'N/A')}")
{
'id': 'ref1',
'title': 'Original research title',
'authors': 'Smith J, Jones M, Brown L',
'journal': 'Nature',
'year': '2020',
'volume': '123',
'pages': '45-52',
'doi': '10.1038/...',
'pmid': '12345678'
}
Extract clean plaintext from XML:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
# Get full plaintext
plaintext = parser.to_plaintext()
# Save to file
with open("article.txt", "w", encoding="utf-8") as f:
f.write(plaintext)
Generate formatted Markdown:
parser = FullTextXMLParser(xml_content)
# Generate markdown
markdown = parser.to_markdown()
# Save to file
with open("article.md", "w", encoding="utf-8") as f:
f.write(markdown)
Markdown includes:
# Machine Learning in Genomics: A Review
**Authors:** John Smith¹, Jane Doe², Mary Johnson¹
¹ Department of Computer Science, Stanford University
² Department of Biology, MIT
## Abstract
Machine learning has revolutionized genomics research...
## Introduction
The application of computational methods to biological data...
### Background
Recent advances in sequencing technology...
## Methods
### Data Collection
We collected genomic data from...
| Sample ID | Tissue Type | Read Count |
|-----------|-------------|------------|
| S001 | Brain | 1,234,567 |
| S002 | Liver | 987,654 |
## References
1. Smith et al. (2020). "Previous work." *Nature*, 123:45-52.
Extract specific sections from the article:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
# Extract introduction
intro = parser.extract_section('introduction')
# Extract methods
methods = parser.extract_section('methods')
# Extract all sections
sections = parser.extract_all_sections()
for section in sections:
print(f"\n{section['title']}")
print(f"{section['content'][:200]}...") # First 200 chars
Analyze how well the parser recognizes XML elements:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
# Validate schema coverage
coverage = parser.validate_schema_coverage()
print(f"Overall coverage: {coverage['coverage_percentage']:.1f}%")
print(f"Recognized elements: {coverage['recognized_count']}")
print(f"Unrecognized elements: {coverage['unrecognized_count']}")
# See unrecognized elements
if coverage['unrecognized_elements']:
print("\nUnrecognized elements:")
for elem, count in coverage['unrecognized_elements'].items():
print(f" {elem}: {count} occurrences")
{
'coverage_percentage': 85.5,
'recognized_count': 342,
'unrecognized_count': 58,
'total_elements': 400,
'recognized_elements': {
'article-title': 1,
'contrib': 5,
'p': 45,
'table': 3,
# ...
},
'unrecognized_elements': {
'custom-meta': 12,
'inline-formula': 8,
# ...
}
}
Customize XML element recognition for specialized XML schemas:
from pyeuropepmc import FullTextXMLParser, ElementPatterns
# Create custom element patterns
custom_patterns = ElementPatterns(
title_paths=['./front/article-meta/title-group/article-title'],
author_paths=['./front/article-meta/contrib-group/contrib'],
abstract_paths=['./front/article-meta/abstract'],
# Add custom patterns for specialized elements
custom_patterns={
'supplementary': './back/app-group/app',
'data_availability': './back/sec[@sec-type="data-availability"]'
}
)
# Use custom patterns
parser = FullTextXMLParser(xml_content, element_patterns=custom_patterns)
metadata = parser.extract_metadata()
ElementPatterns(
# Metadata patterns
title_paths=[...],
author_paths=[...],
abstract_paths=[...],
keywords_paths=[...],
# Content patterns
body_paths=[...],
section_paths=[...],
paragraph_paths=[...],
# Table patterns
table_paths=[...],
table_caption_paths=[...],
# Reference patterns
ref_list_paths=[...],
ref_paths=[...],
# Custom patterns (dict)
custom_patterns={}
)
Complete extraction workflow:
from pyeuropepmc import FullTextXMLParser
parser = FullTextXMLParser(xml_content)
# Extract everything
data = {
'metadata': parser.extract_metadata(),
'tables': parser.extract_tables(),
'references': parser.extract_references(),
'plaintext': parser.to_plaintext(),
'coverage': parser.validate_schema_coverage()
}
# Save to JSON
import json
with open("article_data.json", "w") as f:
json.dump(data, f, indent=2)
Process multiple XML files:
import os
from pyeuropepmc import FullTextXMLParser
xml_dir = "./xml_files"
output_dir = "./processed"
for filename in os.listdir(xml_dir):
if filename.endswith('.xml'):
# Read XML
with open(os.path.join(xml_dir, filename)) as f:
xml_content = f.read()
# Parse
parser = FullTextXMLParser(xml_content)
# Extract and save metadata
metadata = parser.extract_metadata()
pmcid = metadata.get('pmcid', filename.replace('.xml', ''))
# Save markdown
markdown = parser.to_markdown()
with open(f"{output_dir}/{pmcid}.md", "w") as f:
f.write(markdown)
print(f"Processed: {pmcid}")
Export all tables to Excel:
from pyeuropepmc import FullTextXMLParser
import pandas as pd
parser = FullTextXMLParser(xml_content)
tables = parser.extract_tables()
# Create Excel writer
with pd.ExcelWriter('article_tables.xlsx') as writer:
for i, table in enumerate(tables):
# Convert to DataFrame
df = pd.DataFrame(table['data'], columns=table['headers'])
# Write to Excel sheet
sheet_name = f"Table_{i+1}"
df.to_excel(writer, sheet_name=sheet_name, index=False)
# Add caption as note (requires openpyxl)
worksheet = writer.sheets[sheet_name]
worksheet.insert_rows(0)
worksheet['A1'] = table['caption']
Build citation network from references:
from pyeuropepmc import FullTextXMLParser
import networkx as nx
# Parse multiple papers
papers_data = []
for xml_file in xml_files:
with open(xml_file) as f:
parser = FullTextXMLParser(f.read())
papers_data.append({
'metadata': parser.extract_metadata(),
'references': parser.extract_references()
})
# Build citation graph
G = nx.DiGraph()
for paper in papers_data:
pmid = paper['metadata']['pmid']
G.add_node(pmid, title=paper['metadata']['title'])
for ref in paper['references']:
if ref.get('pmid'):
G.add_edge(pmid, ref['pmid'])
# Analyze
print(f"Papers: {G.number_of_nodes()}")
print(f"Citations: {G.number_of_edges()}")
# Find most cited papers
in_degree = dict(G.in_degree())
most_cited = sorted(in_degree.items(), key=lambda x: x[1], reverse=True)[:10]
Analyze parser coverage across multiple files:
from pyeuropepmc import FullTextXMLParser
from collections import defaultdict
all_unrecognized = defaultdict(int)
for xml_file in xml_files:
with open(xml_file) as f:
parser = FullTextXMLParser(f.read())
coverage = parser.validate_schema_coverage()
# Aggregate unrecognized elements
for elem, count in coverage['unrecognized_elements'].items():
all_unrecognized[elem] += count
# Report most common unrecognized elements
print("Most common unrecognized elements across all files:")
sorted_unrecognized = sorted(all_unrecognized.items(), key=lambda x: x[1], reverse=True)
for elem, count in sorted_unrecognized[:20]:
print(f" {elem}: {count} occurrences")
# Bad: Creating new parser for each operation
xml_content = load_xml()
parser1 = FullTextXMLParser(xml_content)
metadata = parser1.extract_metadata()
parser2 = FullTextXMLParser(xml_content)
tables = parser2.extract_tables()
# Good: Reuse parser instance
parser = FullTextXMLParser(xml_content)
metadata = parser.extract_metadata()
tables = parser.extract_tables()
references = parser.extract_references()
# Bad: Extract everything if you only need metadata
parser = FullTextXMLParser(xml_content)
metadata = parser.extract_metadata()
tables = parser.extract_tables() # Unnecessary
references = parser.extract_references() # Unnecessary
# Good: Extract only what you need
parser = FullTextXMLParser(xml_content)
metadata = parser.extract_metadata()
# Schema validation is computationally expensive
# Only run when needed (e.g., during development)
if debug_mode:
coverage = parser.validate_schema_coverage()
if coverage['coverage_percentage'] < 80:
print("Warning: Low coverage")
from pyeuropepmc import FullTextXMLParser
from xml.etree.ElementTree import ParseError
try:
parser = FullTextXMLParser(xml_content)
metadata = parser.extract_metadata()
if not metadata.get('title'):
print("Warning: No title found")
except ParseError as e:
print(f"Invalid XML: {e}")
except Exception as e:
print(f"Parser error: {e}")
The parser provides an extensions package (pyeuropepmc.processing.extensions) with 10 specialized modules for advanced use cases:
| Module | Purpose |
|---|---|
| Content Blocks | Typed content blocks (paragraph, list, figure, formula, etc.) preserving document structure for RAG/LLM pipelines |
| lxml Backend | Optional high-performance lxml parser with secure defaults |
| Peer Review | Extract peer review materials from <sub-article> elements |
| MathML Conversion | Convert MathML to LaTeX for formula extraction |
| JATS4R Validation | Compliance checking against NISO JATS4R recommendations |
| Batch Processing | Concurrent XML parsing with rate limiting and progress callbacks |
| Image Fetcher | Extract figure/supplementary/media references and download assets |
| Reference Resolver | Enrich references via Europe PMC API lookup |
| Pydantic Helpers | Convert dataclasses to Pydantic v2 models dynamically |
| Local Processing | Parse XML files, directories, or strings with helper utilities |
See the XML Parser Extensions Reference for detailed usage and API documentation.
from pyeuropepmc import FullTextXMLParser
from pyeuropepmc.processing.extensions import (
ContentBlockExtractor,
JATS4RValidator,
MathMLConverter,
PeerReviewExtractor,
LocalXMLProcessor,
BatchProcessor,
)
# Parse XML
parser = FullTextXMLParser(xml_content)
# 1. Structured content blocks (for RAG/LLM pipelines)
extractor = ContentBlockExtractor(parser.root)
sections = extractor.extract_sections()
for section in sections:
print(f"Section: {section.title}")
for block in section.content:
print(f" [{block.type.value}]: {block.text[:80]}")
# 2. JATS4R compliance validation
validator = JATS4RValidator(parser.root)
report = validator.validate()
print(f"Compliance score: {report.compliance_score:.2f}")
for finding in report.findings:
print(f" {finding.category}: {finding.message}")
# 3. Peer review extraction
review_extractor = PeerReviewExtractor(parser.root)
review_sets = review_extractor.extract_all()
for review_set in review_sets:
print(f"Round {review_set.revision_round}: {len(review_set.reviews)} reviews")
# 4. MathML to LaTeX conversion
converter = MathMLConverter()
for formula in parser.root.findall(".//disp-formula"):
mathml = formula.find(".//mml:math", converter.namespaces)
if mathml is not None:
latex = converter.convert(ET.tostring(mathml, encoding="unicode"))
print(f"LaTeX: {latex}")
# 5. Local processing convenience
LocalXMLProcessor.write_markdown(xml_content, "output.md")
# 6. Batch processing
processor = BatchProcessor(rate_per_second=5)
results = processor.process_directory("xml_files/")
Use the extensions with the FullTextClient workflow:
from pyeuropepmc import FullTextClient
from pyeuropepmc.processing.extensions import (
ContentBlockExtractor,
JATS4RValidator,
BatchProcessor,
)
from xml.etree import ElementTree as ET
# Download and process articles
with FullTextClient() as client:
pmcids = ["PMC3258128", "PMC3359999"]
for pmcid in pmcids:
xml_path = client.download_xml_by_pmcid(pmcid)
with open(xml_path) as f:
xml_content = f.read()
parser = FullTextXMLParser(xml_content)
# Content blocks for structured output
extractor = ContentBlockExtractor(parser.root)
sections = extractor.extract_sections()
# JATS4R validation
report = JATS4RValidator(parser.root).validate()
print(f"{pmcid}: compliance = {report.compliance_score:.2f}")