
wxpath - exploration web déclarative avec XPath ; un langage de requête Web (WQL)
NOUVEAU : TUI - Interface terminal interactive (propulsée par Textual) pour tester des expressions wxpath et exporter des données.

Nécessite Python 3.10+.
pip install wxpath
# Pour le support TUI :
pip install "wxpath[tui]"
# Lancez immédiatement la TUI via uv :
uvx --from "wxpath[tui]" wxpath-tui
wxpath est un robot d'exploration web déclaratif où le parcours est exprimé directement en XPath. Au lieu d'écrire des boucles d'exploration impératives, wxpath vous permet de décrire quoi suivre et quoi extraire en une seule expression. wxpath exécute cette expression de manière concurrente, en largeur d'abord-ish, et diffuse les résultats au fur et à mesure de leur découverte.
Cette expression récupère une page, extrait les liens et les diffuse simultanément - pas de boucle d'exploration nécessaire :
import wxpath
expr = "url('https://quotes.toscrape.com')//a/@href"
for link in wxpath.wxpath_async_blocking_iter(expr):
print(link)
En introduisant l'opérateur url(...) et la syntaxe ///, le moteur de wxpath est capable d'effectuer une exploration web récursive (ou paginée) et une extraction :
import wxpath
path_expr = """
url('https://quotes.toscrape.com')
///url(//a/@href)
//a/@href
"""
for item in wxpath.wxpath_async_blocking_iter(path_expr, max_depth=1):
print(item)
La plupart des gratteurs web vous obligent à écrire le contrôle de flux d'exploration d'abord, et l'extraction ensuite.
wxpath fusionne ces deux étapes en une seule :
Extrayez des hiérarchies JSON propres et structurées directement depuis le graphe - alimentez vos LLM avec du signal, pas du bruit. Référez-vous à Intégration LangChain pour plus de détails.
wxpath est déterministe (lire : pas propulsé par des LLM). Bien que nous ne puissions pas garantir la stabilité du réseau, nous pouvons garantir que le parcours l'est.
La documentation est maintenant disponible ici.
url(...) et ///url(...) expliquésimport wxpath
from wxpath.settings import CRAWLER_SETTINGS
# En-têtes personnalisés pour la politesse ; nécessaires pour certains sites (ex : Wikipédia)
CRAWLER_SETTINGS.headers = {'User-Agent': 'my-app/0.4.0 (contact: [email protected])'}
# Explorer, extraire des champs, construire un graphe de connaissances
path_expr = """
url('https://en.wikipedia.org/wiki/Expression_language')
///url(
//main//a/@href[
starts-with(., '/wiki/') and not(contains(., ':'))
]
)
/map{
'title': (//span[contains(@class, "mw-page-title-main")]/text())[1] ! string(.),
'url': string(base-uri(.)),
'short_description': //div[contains(@class, 'shortdescription')]/text() ! string(.),
'forward_links': //div[@id="mw-content-text"]//a/@href ! string(.)
}
"""
for item in wxpath.wxpath_async_blocking_iter(path_expr, max_depth=1):
print(item)
Remarque : Certains sites (dont Wikipédia) peuvent bloquer les requêtes sans en-têtes appropriés.
Voir Avancé : Configuration du moteur et du robot pour définir un User-Agent personnalisé.
L'expression ci-dessus fait ce qui suit :
https://en.wikipedia.org/wiki/Expression_language.<main> qui commencent par /wiki/ et ne contiennent pas de deux-points (:).url(...) et ///url(...) expliquésurl(...) est un opérateur personnalisé qui récupère le contenu de l'URL spécifiée par l'utilisateur ou générée en interne et le retourne en tant qu'lxml.html.HtmlElement pour un traitement XPath ultérieur.///url(...) indique une exploration en profondeur. Il indique au moteur d'exécution de continuer à suivre les liens jusqu'à la max_depth spécifiée. Contrairement aux sauts url() répétés, il permet à une seule expression de décrire une exploration plus profonde du graphe. ATTENTION : À utiliser avec prudence et contraintes (via max_depth ou des prédicats XPath) pour éviter une explosion du parcours.Voir DESIGN.md pour les détails de la conception du langage. Vous y verrez les concepts fondamentaux et concevrez le langage depuis la base.
wxpath évalue une expression comme une liste d'étapes de parcours et d'extraction (appelées en interne Segments).
url(...) crée des tâches d'exploration soit statiquement (via une URL fixe) soit dynamiquement (via une URL dérivée de l'expression XPath). Les URLs sont dédupliquées globalement, au mieux, et non par profondeur.
Les segments XPath opèrent sur les documents récupérés (via les opérations url(...) immédiatement précédentes).
///url(...) indique une exploration en profondeur - elle procède en largeur d'abord-ish jusqu'à max_depth.
Les résultats sont renvoyés dès qu'ils sont prêts.
wxpath est d'abord basé sur asyncio/aiohttp, fournissant une API asynchrone pour l'exploration et l'extraction de données.
import asyncio
from wxpath import wxpath_async
items = []
async def main():
path_expr = "url('https://en.wikipedia.org/wiki/Expression_language')///url(//@href[starts-with(., '/wiki/')])//a/@href"
async for item in wxpath_async(path_expr, max_depth=1):
items.append(item)
asyncio.run(main())
wxpath fournit également une API asyncio-dans-synchrone, vous permettant d'explorer plusieurs pages simultanément tout en conservant la simplicité du code synchrone. Ceci est particulièrement utile pour les explorations dans des environnements d'exécution strictement synchrones (c'est-à-dire, pas à l'intérieur d'une boucle d'événements asyncio) où la performance est un enjeu.
from wxpath import wxpath_async_blocking_iter
path_expr = "url('https://en.wikipedia.org/wiki/Expression_language')///url(//@href[starts-with(., '/wiki/')])//a/@href"
items = list(wxpath_async_blocking_iter(path_expr, max_depth=1))
wxpath respecte robots.txt par défaut via le constructeur WXPathEngine(..., robotstxt=True).
L'API Python de wxpath renvoie des objets structurés.
Selon l'expression, les résultats peuvent inclure :
lxml.* et lxml.html.*elementpath.datatypes.* (pour les fonctionnalités XPath 3.1)WxStr (valeurs de chaîne avec provenance)Le CLI aplatit ces objets en JSON simple pour l'affichage. L'API Python préserve la structure par défaut.
wxpath utilise la bibliothèque elementpath pour fournir le support XPath 3.1, permettant des fonctionnalités XPath avancées comme les maps, les arrays, et plus. Cela vous permet d'écrire des requêtes XPath plus puissantes.
path_expr = """
url('https://en.wikipedia.org/wiki/Expression_language')
///url(//div[@id='mw-content-text']//a/@href)
/map{
'title':(//span[contains(@class, "mw-page-title-main")]/text())[1],
'short_description':(//div[contains(@class, "shortdescription")]/text())[1],
'url'://link[@rel='canonical']/@href[1]
}
"""
# [...
# {'title': 'Computer language',
# 'short_description': 'Formal language for communicating with a computer',
# 'url': 'https://en.wikipedia.org/wiki/Computer_language'},
# {'title': 'Machine-readable medium and data',
# 'short_description': 'Medium capable of storing data in a format readable by a machine',
# 'url': 'https://en.wikipedia.org/wiki/Machine-readable_medium_and_data'},
# {'title': 'Domain knowledge',
# 'short_description': 'Specialist knowledge within a specific field',
# 'url': 'https://en.wikipedia.org/wiki/Domain_knowledge'},
# ...]
wxpath fournit une barre de progression (via tqdm) pour suivre la progression de l'exploration. Particulièrement utile pour les explorations longues.
Activez-la en définissant engine.run(..., progress=True), ou passez progress=True à l'une des fonctions wxpath_async*(...).
items = wxpath.wxpath_async_blocking("...", progress=True)
> 100%|██████████████████████████████████████████████████████████▎| 469/471 [00:05<00:00, 72.00it/s, depth=2, yielded=457]
wxpath fournit une interface en ligne de commande (CLI) pour expérimenter et exécuter rapidement des expressions wxpath directement depuis le terminal.
L'exemple suivant montre comment explorer Wikipédia en partant de la page "Expression language", extraire les liens vers d'autres pages wiki, et récupérer des champs spécifiques de chaque page liée.
NOTE : En raison de la nature changeante du contenu web, la sortie peut varier dans le temps.
> wxpath --depth 1 \
--header "User-Agent: my-app/0.1 (contact: [email protected])" \
"url('https://en.wikipedia.org/wiki/Expression_language') \
///url(//div[@id='mw-content-text']//a/@href[starts-with(., '/wiki/') \
and not(matches(@href, '^(?:/wiki/)?(?:Wikipedia|File|Template|Special|Template_talk|Help):'))]) \
/map{ \
'title':(//span[contains(@class, 'mw-page-title-main')]/text())[1], \
'short_description':(//div[contains(@class, 'shortdescription')]/text())[1], \
'url':string(base-uri(.)), \
'backlink':wx:backlink(.), \
'depth':wx:depth(.) \
}"
{"title": "Computer language", "short_description": "Formal language for communicating with a computer", "url": "https://en.wikipedia.org/wiki/Computer_language", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Machine-readable medium and data", "short_description": "Medium capable of storing data in a format readable by a machine", "url": "https://en.wikipedia.org/wiki/Machine_readable", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Domain knowledge", "short_description": "Specialist knowledge within a specific field", "url": "https://en.wikipedia.org/wiki/Domain_knowledge", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Advanced Boolean Expression Language", "short_description": "Hardware description language and software", "url": "https://en.wikipedia.org/wiki/Advanced_Boolean_Expression_Language", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Data Analysis Expressions", "short_description": "Formula and data query language", "url": "https://en.wikipedia.org/wiki/Data_Analysis_Expressions", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Jakarta Expression Language", "short_description": "Computer programming language", "url": "https://en.wikipedia.org/wiki/Jakarta_Expression_Language", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Rights Expression Language", "short_description": [], "url": "https://en.wikipedia.org/wiki/Rights_Expression_Language", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
{"title": "Computer science", "short_description": "Study of computation", "url": "https://en.wikipedia.org/wiki/Computer_science", "backlink": "https://en.wikipedia.org/wiki/Expression_language", "depth": 1.0}
Options de la ligne de commande :
--depth <depth> Profondeur d'exploration maximale
--verbose [true|false] Fournit des informations CLI superficielles
--debug [true|false] Fournit une sortie d'exécution et des informations détaillées
--concurrency <concurrency> Nombre de récupérations simultanées
--concurrency-per-host <concurrency> Nombre de récupérations simultanées par hôte
--header "Key:Value" Ajouter un en-tête personnalisé (ex : 'Key:Value'). Peut être utilisé plusieurs fois.
--respect-robots [true|false] (Par défaut : True) Respecte robots.txt
--cache [true|false] (Par défaut : False) Persiste les résultats d'exploration dans une base de données locale
wxpath fournit une interface terminal (TUI) pour les tests interactifs d'expressions et l'extraction de données.
Voir Démarrage rapide TUI pour plus de détails.
wxpath peut optionnellement persister les résultats d'exploration dans une base de données locale. Cela est particulièrement utile lorsque vous explorez un grand nombre d'URLs et que vous décidez de mettre en pause l'exploration, de changer les expressions d'extraction, ou de devoir redémarrer l'exploration.
wxpath supporte deux moteurs : sqlite et redis. SQLite est idéal pour les explorations à petite échelle, avec un seul travailleur (c'est-à-dire engine.crawler.concurrency == 1). Redis est idéal pour les explorations à grande échelle, avec plusieurs travailleurs. Vous rencontrerez un avertissement si min(engine.crawler.concurrency, engine.crawler.per_host) > 1 lors de l'utilisation du moteur sqlite.
Pour l'utiliser, vous devez installer la dépendance optionnelle appropriée :
pip install wxpath[cache-sqlite]
pip install wxpath[cache-redis]
Une fois la dépendance installée, vous devez activer le cache :
from wxpath.settings import SETTINGS
# Pour activer la mise en cache ; sqlite est le défaut
SETTINGS.http.client.cache.enabled = True
# Pour le moteur redis
SETTINGS.http.client.cache.enabled = True
SETTINGS.http.client.cache.backend = "redis"
SETTINGS.http.client.cache.redis.address = "redis://localhost:6379/0"
# Exécutez wxpath comme d'habitude
items = list(wxpath_async_blocking_iter('...', max_depth=1, engine=engine))
Voir settings.py pour les détails des paramètres.
wxpath supporte un système de hooks enfichables qui vous permet de modifier le comportement d'exploration et d'extraction. Vous pouvez enregistrer des hooks pour prétraiter les URLs, post-traiter le HTML, filtrer les valeurs extraites, et plus encore. Les hooks seront exécutés dans l'ordre où ils sont enregistrés. Les hooks peuvent impacter les performances.
from wxpath import hooks
@hooks.register
class OnlyEnglish:
def post_parse(self, ctx, elem):
lang = elem.xpath('string(/html/@lang)').lower()[:2]
return elem if lang in ("en", "") else None
NOTE : Les hooks peuvent être synchrones ou asynchrones, mais tous les hooks d'un projet doivent suivre le même style. Mélanger des hooks synchrones et asynchrones n'est pas supporté et peut conduire à un comportement inattendu.
from wxpath import hooks
@hooks.register
class OnlyEnglish:
async def post_parse(self, ctx, elem):
lang = elem.xpath('string(/html/@lang)').lower()[:2]
return elem if lang in ("en", "") else None
JSONLWriter (alias NDJSONWriter) est un hook intégré qui écrit les données extraites dans un fichier JSON délimité par des retours à la ligne. Utile pour stocker les résultats dans un format structuré facilement traitable par la suite.
from wxpath import hooks
hooks.register(hooks.JSONLWriter)
Nécessite Python 3.10+.
pip install wxpath
Pour la persistance / mise en cache, wxpath supporte les moteurs suivants :
pip install wxpath[cache-sqlite]
pip install wxpath[cache-redis]
Voir EXAMPLES.md pour plus d'exemples d'utilisation.
Voir COMPARISONS.md pour des comparaisons avec d'autres outils de gratte-web.
Vous pouvez modifier le comportement du moteur et du robot comme suit :
from wxpath import wxpath_async_blocking_iter
from wxpath.core.runtime import WXPathEngine
from wxpath.http.client.crawler import Crawler
crawler = Crawler(
concurrency=8,
per_host=2,
timeout=10,
respect_robots=False,
headers={
"User-Agent": "my-app/0.1.0 (contact: [email protected])", # Les sites comme Wikipédia apprécieront
},
)
# Si `crawler` n'est pas spécifié, un Crawler par défaut sera créé avec
# les valeurs de concurrence, per_host et respect_robots fournies, ou avec les valeurs par défaut.
engine = WXPathEngine(
# concurrency: int = 16,
# per_host: int = 8,
# respect_robots: bool = True,
# allowed_response_codes: set[int] = {200},
# allow_redirects: bool = True,
crawler=crawler,
)
path_expr = "url('https://en.wikipedia.org/wiki/Expression_language')//url(//main//a/@href)"
items = list(wxpath_async_blocking_iter(path_expr, max_depth=1, engine=engine))
wxpath_async*)max_depth: int = 1progress: bool = Falseengine: WXPathEngine | None = Noneyield_errors: bool = FalseVous pouvez également utiliser settings.py pour activer la mise en cache, la limitation de débit, la concurrence et plus encore.
max_depth est atteinte.Les fonctionnalités suivantes ne sont pas encore supportées :
Ce projet est en développement précoce. Les concepts de base sont stables, mais l'API et les fonctionnalités peuvent changer. Veuillez signaler les problèmes - en particulier les explorations bloquées ou les comportements inattendus - ainsi que toutes les fonctionnalités que vous souhaiteriez voir (aucune garantie qu'elles soient implémentées).
///) nécessitent de la discipline de la part de l'utilisateur pour éviter une expansion illimitée (explosion du parcours).max_depth, ainsi que des prédicats et filtres XPath pour limiter la portée de l'exploration.Si vous avez besoin d'aide pour construire ou exploiter des robots d'exploration / flux de données avec wxpath (extraction, planification, surveillance, correction de pannes) ou d'autres besoins en grattage web, veuillez me contacter à : [email protected].
Si vous aimez wxpath et souhaitez soutenir son développement, veuillez envisager de faire un don.
wxpath suit semver : <MAJOR>.<MINOR>.<PATCH>.
Cependant, avant la version 1.0.0, elle suit 0.<MAJOR>.<MINOR|PATCH>.
AGPL-3.0