Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract Text from HTML div using Python and lxml

Tags:

python

html

lxml

I'm trying to get python to extract text from one spot of a website. I've identified the HTML div:

<div class="number">76</div>

which is in:

...div/div[1]/div/div[2]

I'm trying to use lxml to extract the '76' from that, but can't get a return out of it other than: []

Here's my code:

from lxml import html
import requests
url = 'https://sleepiq.sleepnumber.com/#/#@1'
values = {'username': '[email protected]',
          'password': 'mypassword'}

page = requests.get(url, data=values)
tree = html.fromstring(page.content)
hr = tree.xpath('//div[@class="number"]/text()')
print hr

Any suggestions? I feel this should be pretty easy, thanks in advance!

Update: the element I want is not contained in the page.content from requests.get

Updated Update: It looks like this is not logging me in to the page where the content I want is. It is only getting the login screen content.

like image 760
Mike Avatar asked Sep 21 '26 18:09

Mike


2 Answers

Have you tried printing your page.content to make sure your requests.get is retrieving the content you want? That is often where things break. And your empty list returned off the xpath search indicates "not found."

Assuming that's okay, your parsing is close. I just tried the following, which is successful:

from lxml import html

tree = html.fromstring('<body><div class="number">76</div></body>')
number = tree.xpath('//div[@class="number"]/text()')[0]

number now equals '76'. Note the [0] indexing, because xpath always returns a list of what's found. You have to dereference to find the content.

A common gotcha here is that the XPath text() function isn't as inclusive or straightforward as it might seem. If there are any sub-elements to the div--e.g. if the text is really <div class="number"><strong>76</strong></div> then text() will return an empty list, because the text belongs to the strong not the div. In real-world HTML--especially HTML that's ever been cut-and-pasted from a word processor, or otherwise edited by humans--such extra elements are entirely common.

While it won't solve all known text management issues, one handy workaround is to use the // multi-level indirection instead of the / single-level indirection to text:

number = ''.join(tree.xpath('//div[@class="number"]//text()'))

Now, regardless of whether there are sub-elements or not, the total text will be concatenated and returned.

Update Ok, if your problem is logging in, you probably want to try a requests.post (rather than .get) at minimum. In simpler cases, just that change might work. In others, the login needs to be done to a separate page than the page you want to retrieve/scape. In that case, you probably want to use a session object:

with requests.Session() as session:
    # First POST to the login page
    landing_page = session.post(login_url, data=values)

    # Now make authenticated request within the session
    page = session.get(url)
    # ...use page as above...

This is a bit more complex, but shows the logic for a separate login page. Many sites (e.g. WordPress sites) require this. Post-authentication, they often take you to pages (like the site home page) that isn't interesting content (though it can be scraped to identify whether the login was successful). This altered login workflow doesn't change any of the parsing techniques, which work as above.

like image 64
Jonathan Eunice Avatar answered Sep 24 '26 08:09

Jonathan Eunice


Beautiful Soup(http://www.pythonforbeginners.com/beautifulsoup/web-scraping-with-beautifulsoup) will help u out.

another way http://docs.python-guide.org/en/latest/scenarios/scrape/

like image 24
hussain Avatar answered Sep 24 '26 09:09

hussain



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!