2010-10-25 5 views
0

Ce qui devrait être une extraction assez simple regex me confond. Impossible de trouver une question similaire sur SO, si heureux d'être pointé vers un si elle existe. Étant donné le code HTML suivant:Regex correspondant éléments après un en-tête en HTML

<h1 class="title">Title One</h1><p><a href="#">40.5</a><a href="#">31.3</a></p>

<h1 class="title alternate">Title Two</h1><p><a href="#">12.1</a><a href="#">82.0</a></p>

(au sein d'un document plus volumineux - les extraits seront très probablement courir à travers plusieurs lignes)

Comment puis-je construire une expression régulière qui trouve le texte dans les balises A, dans le premier P suivant un H1? L'expression rationnelle ira dans une boucle, telle que je peux passer dans l'en-tête, afin de récupérer les éléments qui suivent.

<a[^>]*>([0-9.]+?)</a><a[^>]*>([0-9.]+?)</a> évidemment correspond à tous les éléments d'une étiquette (et devrait être bien comme une balise ne peut être suivie), mais je ne peux pas les attacher à un H1.

.+Title One.+<a[^>]*>([0-9.]+?)</a></p> échoue.

Je l'avais essayé d'utiliser comme regard derrière ceci:

(?<=Title One.+)<a[^>]*>([0-9.]+?)</a></p> et quelques variations, mais il est possible que pour les matches de largeur fixe (qui ne sera pas le cas ici).

Pour le contexte, cela va utiliser le moteur de regex de Python. Je sais regex est pas nécessairement la meilleure solution pour cela, donc d'autres suggestions en utilisant DOM ou quelque chose d'autre aussi reçu avec gratitude :)


Mise à jour

Pour clarifier de ce qui précède, je voudrais retourner les éléments suivants:

{"Title One": ["40.5", "31.3"], "Title Two": ["12.1", "82.0"]}

(pas que je besoin d'aide pour composer le dictionnaire, mais il ne montre comment je besoin des valeurs à être en rapport avec le titre).

Jusqu'à présent, BeautifulSoup ressemble à la meilleure photo. LXML travaillera également probablement le code source HTML est pas vraiment tag soupe - il est assez bien structuré, au moins dans les endroits que je suis intéressé par


+0

Est-ce que cela fonctionnera dans le code Serveur ou dans le javascript dans le document? –

+0

Ceci est pour un script python autonome, pas réellement basé sur le Web. HTML est juste les données source. – majelbstoat

+0

Avez-vous le contrôle sur le HTML créé, pouvez-vous lui faire confiance pour suivre un format commun? –

Répondre

1

Est-ce le genre de chose que vous recherchez?

>>> from lxml import etree 
>>> 
>>> data = """ 
... <h1 class="title">Title One</h1><p><a href="#">40.5</a><a href="#">31.3</a></p> 
... <h1 class="title alternate">Title Two</h1><p><a href="#">12.1</a><a href="#">82.0</a></p> 
... """ 
>>> 
>>> d = etree.HTML(data) 
>>> d.xpath('//h1/following-sibling::p[1]/a/text()') 
['40.5', '31.3', '12.1', '82.0'] 

Cette solution utilise lxml.etree et une expression xpath.


Mise à jour

>>> from lxml import etree 
>>> from pprint import pprint 
>>> 
>>> data = """ 
... <h1 class="title">Title One</h1><p><a href="#">40.5</a><a href="#">31.3</a></p> 
... <h1 class="title alternate">Title Two</h1><p><a href="#">12.1</a><a href="#">82.0</a></p> 
... """ 
>>> 
>>> d = etree.HTML(data) 
>>> #d.xpath('//h1[following-sibling::*[1][local-name()="p"]]') 
... 
>>> results = {} 
>>> for h in d.xpath('//h1[following-sibling::*[1][local-name()="p"]]'): 
... r = results.setdefault(str(h.text),[]) 
... r += [ str(x) for x in h.xpath('./following-sibling::*[1][local-name()="p"]/a/text()') ] 
... 
>>> pprint(results) 
{'Title One': ['40.5', '31.3'], 'Title Two': ['12.1', '82.0']} 

Maintenant, en utilisant prédicats pour regarder vers l'avenir, cela devrait itérer <h1> balises qui sont immédiatement suivies par <p> tags. (Casting tag.text à des chaînes explicitement que j'ai un souvenir qu'ils ne sont pas des chaînes normales, vous auriez du mal à les décaper, etc.)

+0

Il est, bien qu'il y aura d'autres éléments H1 sur la page et je vais devoir savoir quelles valeurs vont avec quel titre. N'a pas considéré XPATH, va enquêter, merci. – majelbstoat

+0

Si vous publiez ce que vous êtes en train d'analyser et ce dont vous avez besoin. Je suis sûr que quelqu'un peut vous pointer dans la bonne direction avec xpath. – MattH

+0

Désolé, il a fallu si longtemps pour que je revienne à cela, mais c'est génial et exactement ce dont j'avais besoin :) Il m'a fallu environ une heure pour grok cependant - xpath pas mon fort! – majelbstoat

0

Ne pas utiliser regex pour analyser html.. Cela ne peut pas être fait, par définition. Utilisez un analyseur html à la place. Je suggère lxml.html. Le

lxml.html traite le HTML mal formé mieux que BeautifulSoup, est maintenu activement (BeautifulSoup n'est pas) et est beaucoup plus rapide puisqu'il emploie libxml2 intérieurement.

1

Vous avez raison, regex est absolument le mauvais outil pour la correspondance HTML.

Votre question, cependant, sonne exactement comme le problème pour Beautiful Soup - un analyseur HTML qui peut gérer le HTML moins que parfait.

1

L'autre réponse évidente pour résoudre ce problème est - J'aime qu'il gère le genre de html merdique que vous rencontrez souvent dans la nature aussi raisonnablement et avec élégance que vous pouvez espérer.

0

Voici une manière utilisant la manipulation de chaînes tout à fait normal

html=''' 
<h1 class="title">Title One</h1><p><a href="#">40.5</a> 
<a href="#">31.3</a></p> 
<h1 class="title alternate">Title Two</h1><p><a href="#">12.1</a><a href="#">82.0</a></p> 
''' 

for i in html.split("</a>"): 
    if "<a href" in i: 
     print i.split("<a href")[-1].split(">")[-1] 

sortie

$ python test.py 
40.5 
31.3 
12.1 
82.0 

Je ne comprends pas vraiment ce que vous voulez obtenir, mais si votre condition est simple, oui, une expression régulière ou quelques mangling de chaîne peuvent le faire. Pas nécessaire besoin d'un analyseur pour cela.

+0

Ceci est une solution simple et agréable, mais ne correspond pas aux valeurs des titres. Je vais clarifier la question. – majelbstoat