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!
3 Réponses :
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
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?
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]))
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>
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
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émenth4
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), '/')