3

I have this short example to demonstrate my problem:

from lxml import html

post = """<p>This a page with URLs.
<a href="http://google.com">This goes to&#xA; Google</a><br/>
<a href="http://yahoo.com">This &#xA; goes to Yahoo!</a><br/>
<a&#xA;href="http://example.com">This is invalid due to that&#xA;line feed character</p>&#xA;"""

doc = html.fromstring(post)
for link in doc.xpath('//a'):
    print link.get('href')

This outputs:

http://google.com
http://yahoo.com
None

The problem is that my data has &#xA; characters embedded in it. For my last link, it is embedded directly between the anchor and the href attribute. The line feeds outside of the elements are important to me.

doc.xpath('//a') correctly saw the <a&#xA;href="http://example.com"> as a link, but it can't access the href attribute when I do link.get('href').

How can I clean the data if link.get('href') returns None, so that I can still retrieve the discovered href attribute?

I can't strip all of the &#xA; characters from the entire post element as the ones in the text are important.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
NewGuy
  • 3,273
  • 7
  • 43
  • 61

2 Answers2

1

Module unidecode

Since you need the data outside of the tags, you could try using unidecode. It doesn't tackle Chinese and Korean, but it'll do things like change left and right quotes to ASCII quotes. It should help with these &#xA; characters as well, changing them to spaces instead of non-breaking spaces. Hopefully that's all you need in regards to preserving the other data. str.replace(u"\#xa", u" ") is less heavy handed if the ascii space is okay.

import unidecode, urllib2
from lxml import html

html_text = urllib2.urlopen("http://www.yourwebsite.com")
ascii_text = unidecode.unidecode(html_text)
html.fromstring(ascii_text)

Explanation of issue

There seems to be a known issue with this in several versions of Python. And it's C# as well. A related closed issue seems to indicate that the issue was closed because XML attribute tags aren't built to support carriage returns, so escaping it in all xml contexts would be silly. As it turns out, the W3C spec requires that the unicode be put in when parsing (see sec. 1).

All line breaks must have been normalized on input to #xA as described in 2.11 End-of-Line Handling, so the rest of this algorithm operates on text normalized in this way.

Community
  • 1
  • 1
Palu Macil
  • 1,708
  • 1
  • 24
  • 34
1

You may solve your specific problem with:

post = post.replace('&#xA;', '\n')

Resulting test program:

from lxml import html

post = """<p>This a page with URLs. 
<a href="http://google.com">This goes to&#xA; Google</a><br/>
<a href="http://yahoo.com">This &#xA; goes to Yahoo!</a><br/>
<a&#xA;href="http://example.com">This is invalid due to that&#xA;line feed character</p>&#xA;"""

post = post.replace('&#xA;', '\n')

doc = html.fromstring(post)
for link in doc.xpath('//a'):
    print link.get('href')

Output:

http://google.com
http://yahoo.com
http://example.com
Robᵩ
  • 163,533
  • 20
  • 239
  • 308