J'ai du mal à trouver une valeur dans une soupe basée sur du texte. Voici le code

from bs4 import BeautifulSoup as bs
import requests
import re

html='http://finance.yahoo.com/q/ks?s=aapl+Key+Statistics'
r = requests.get(html)
soup = bs(r.text)
findit=soup.find("td", text=re.compile('Market Cap'))

Cela renvoie [], mais il y a absolument du texte dans une balise «td» avec «Market Cap». Quand j'utilise

soup.find_all("td")

J'obtiens un ensemble de résultats qui comprend:

<td class="yfnc_tablehead1" width="74%">Market Cap (intraday)<font size="-1"><sup>5</sup></font>:</td>
0
clg4 8 mars 2016 à 18:13

3 réponses

Meilleure réponse

Explication:

Le problème est que cette balise particulière a d'autres éléments enfants et la valeur .string, qui est vérifiée lorsque vous appliquez l'argument texte, est None (bs4 l'a documentée ici).

Solutions / solutions:

Ne spécifiez pas du tout le nom de la balise ici, recherchez le nœud de texte et montez jusqu'au parent:

soup.find(text=re.compile('Market Cap')).parent.get_text()

Ou, vous pouvez utiliser find_parent() si td n'est pas le parent direct du nœud de texte:

soup.find(text=re.compile('Market Cap')).find_parent("td").get_text()

Vous pouvez également utiliser une "fonction de recherche" pour rechercher les balises td et voyez si les nœuds enfants de texte direct ont le texte Market Cap:

soup.find(lambda tag: tag and
                      tag.name == "td" and
                      tag.find(text=re.compile('Market Cap'), recursive=False))

Ou, si vous cherchez à trouver le numéro suivant 5:

soup.find(text=re.compile('Market Cap')).next_sibling.get_text()
2
alecxe 8 mars 2016 à 15:50

Vous ne pouvez pas utiliser l'expression régulière avec une balise. Cela ne fonctionnera tout simplement pas. Je ne sais pas si c'est un bug de spécification. Je cherche juste après tout, puis je récupère le parent dans une liste de compréhension car "td" "regex" vous donnerait la balise td.

Code

from bs4 import BeautifulSoup as bs
import requests
import re

html='http://finance.yahoo.com/q/ks?s=aapl+Key+Statistics'
r = requests.get(html)
soup = bs(r.text, "lxml")

findit=soup.find_all(text=re.compile('Market Cap'))
findit=[x.parent for x in findit if x.parent.name == "td"]
print(findit)

Sortie

[<td class="yfnc_tablehead1" width="74%">Market Cap (intraday)<font size="-1"><sup>5</sup></font>:</td>]
2
Kordi 8 mars 2016 à 15:38

Regex est juste une chose terrible à intégrer dans le code d'analyse et à mon humble avis devrait être évité autant que possible.

Personnellement, je n'aime pas BeautifulSoup en raison de son manque de support XPath. Ce que vous essayez de faire, c'est le genre de chose pour laquelle XPath est idéalement adapté. Si je faisais ce que vous faites, j'utiliserais lxml pour l'analyse plutôt que l'analyse intégrée et / ou regex de BeautifulSoup. C'est vraiment assez élégant et extrêmement rapide:

from lxml import etree
import requests

source = requests.get('http://finance.yahoo.com/q/ks?s=aapl+Key+Statistics').content
parsed = etree.HTML(source)
tds_w_market_cap = parsed.xpath('//td[contains(., "Market Cap")]')

Pour info ce qui précède retourne un objet lxml plutôt que le texte de la source de la page. En lxml, vous ne travaillez pas vraiment directement avec la source en soi. Si vous devez renvoyer une liste de la source réelle pour une raison quelconque, vous ajouteriez quelque chose comme:

print [etree.tostring(i) for i in tds_w_market_cap]

Si vous devez absolument utiliser BeautifulSoup pour cette tâche, j'utiliserais une compréhension de liste:

from bs4 import BeautifulSoup as bs
import requests

source = requests.get('http://finance.yahoo.com/q/ks?s=aapl+Key+Statistics').content
parsed = bs(source, 'lxml')
tds_w_market_cap = [i for i in parsed.find_all('td') if 'Market Cap' in i.get_text()]
0
AutomaticStatic 8 mars 2016 à 23:55