2
votes

Obtenez xpath pour marquer qui contient du texte

J'essaie de trouver le xpath vers un texte sur une page Web. Si vous deviez aller à https://www.york.ac. uk / enseignement / cws / wws / webpage1.html et essayez d'obtenir le xpath de "EXERCICE" il ressemblerait à "html body html table tbody tr td div h4". Si vous allez sur cette page, faites un clic droit sur "EXERCICE" et inspectez-la, vous pouvez voir le chemin en bas du code (en chrome).

J'ai essayé de nombreux chemins. Aucun d'entre eux n'a obtenu le résultat souhaité. C'est le plus proche que j'ai obtenu:

soup = BS(page, 'html.parser')
tags = [{"name":tag.name,"text":tag.text,"attributes":tag.attributes} for tag in soup.find_all()]
s = ''
for t in tags:
    if "EXERCISE" in t['text']:
        s = s + t['name'] + " "
print(s)

Au début, j'ai besoin d'obtenir "html body html table tbody tr td div h4", mais finalement avec des pages plus compliquées, j'ai besoin pour obtenir les attributs de balise également

Merci!


8 commentaires

Vous n'utilisez pas xpath pour localiser l'élément. Est-ce là l'intention?


Exactement, j'utilise le texte exact pour localiser l'élément et je souhaite lui renvoyer le chemin.


Vous avez tagué BeautifulSoup - voulez-vous juste xpath? Cela peut être fait de différentes manières ... avec le sélecteur CSS également


Je serais heureux d'utiliser de toute façon vraiment. J'ai juste besoin de trouver le chemin que je peux redonner à soup.select () pour qu'il puisse à nouveau renvoyer le texte. La raison pour laquelle j'ai tagué xpath est que je l'ai utilisé dans l'une de mes tentatives


Pouvez-vous utiliser lxml?


Ouais je peux. Peut vraiment utiliser n'importe quelle méthode


Le XPath pour sélectionner n'importe quel élément avec un nœud de texte contenant la chaîne 'EXERCISE' est //*[text()[contains(.,'EXERCISE ')]] . Ce / html / body / hmtl / table / tr / td / div / h4 est juste une expression XPath pour sélectionner cet élément h4 sans aucune autre signification sémantique.


Il y a d'autres questions et réponses sur le site pour obtenir un chemin absolu pour un nœud sélectionné, même s'il y en a un depuis Spécifications XPath 2.0 : fn: string-join (pour $ n dans ancestor-or-self :: * return name ($ n), '/')


3 Réponses :


0
votes

Si vous savez que la balise que vous voulez aura toujours le texte exact de "EXERCICE" (pas de guillemets, ou différents cas ultérieurs, espace blanc, etc.), alors vous pouvez simplement utiliser un .find sur le texte exact. Bien que vous puissiez également utiliser une expression régulière à la place au cas où vous voudriez vérifier les variations d'espaces blancs et autres.

À partir de là, vous pouvez utiliser .parents pour obtenir une liste des objets ancêtres , c'est-à-dire l'élément qui le contient, l'élément qui contient cet élément, et ainsi de suite jusqu'en haut du document. Ensuite, il suffit d'extraire les noms de balises, d'inverser la liste et de tout joindre.

parent_tags = [ p.name for p in list(thetag.parents)[:-1] ]
print('/' + '/'.join(parent_tags[::-1]))

Résultat:

[document] / html / corps / hmtl / table / tr / td / div / h4

Si vous ne voulez pas que " [document] " au début, vous pouvez le retirer en cours de route de plusieurs façons, par exemple utiliser ces lignes à la place des deux derniers:

thetag = soup.find(string="EXERCISE")
parent_tags = [ p.name for p in list(thetag.parents) ]
print('/'.join(parent_tags[::-1]))

Sortie:

/ html / body / hmtl / table / tr / td / div / h4


1 commentaires

Merci pour cela, cela a du sens et semble être très efficace. Le problème est que la sortie est [document] / hmtl / body / table / tr / td / div / p / p / p / p / h4 donc il contient les p. ils s'ouvrent et se ferment avant le h4, donc le h4 n'est pas réellement à l'intérieur d'eux si cela a du sens?



0
votes

Le sélecteur CSS : contains (EXERCISE): not (: has (: contains (EXERCISE))) sélectionnera la balise la plus interne contenant la chaîne "EXERCISE".

Ensuite, nous utilisons la méthode find_parents () pour trouver tous les parents de cette balise et imprimer leurs noms:

[document] > hmtl > body > table > tr > td > div > p > p > p > p > h4

Impressions:

import requests
from bs4 import BeautifulSoup

url = 'https://www.york.ac.uk/teaching/cws/wws/webpage1.html'

soup = BeautifulSoup(requests.get(url).text, 'html.parser')

t = soup.select_one(':contains(EXERCISE):not(:has(:contains(EXERCISE)))')
# you can use also this:
# t = soup.find(text="EXERCISE").find_parent()    

#lets print the path
tag_names = [t.name, *[t.name for t in t.find_parents()]]
print(' > '.join(tag_names[::-1]))


0 commentaires

0
votes

Utilisation de lxml:

url = 'https://www.york.ac.uk/teaching/cws/wws/webpage1.html'

import requests
from lxml import etree
parser = etree.HTMLParser()
page  = requests.get(url,headers={"User-Agent":"Mozilla/5.0"})

root = etree.fromstring(page.content,parser)

tree = etree.ElementTree(root)
e = root.xpath('.//*[text()="EXERCISE"]')
print(tree.getpath(e[0]))

Sortie:

/ html / body / html / table / tr / td / div [2] / h4 p>


1 commentaires

Cela a beaucoup aidé. J'ai fini par utiliser ceci, en divisant la sortie, puis en parcourant chacun d'eux en faisant un soup.select () et en saisissant les attributs. Joli