1 """The ``lxml.html`` tool set for HTML handling.
2 """
3
4 import threading
5 import re
6 try:
7 from urlparse import urljoin
8 except ImportError:
9
10 from urllib.parse import urljoin
11 import copy
12 from lxml import etree
13 from lxml.html import defs
14 from lxml import cssselect
15 from lxml.html._setmixin import SetMixin
16 try:
17 from UserDict import DictMixin
18 except ImportError:
19
20 from lxml.html._dictmixin import DictMixin
21 try:
22 set
23 except NameError:
24
25 from sets import Set as set
26 try:
27 bytes = __builtins__["bytes"]
28 except (KeyError, NameError):
29
30 bytes = str
31 try:
32 unicode = __builtins__["unicode"]
33 except (KeyError, NameError):
34
35 unicode = str
36 try:
37 basestring = __builtins__["basestring"]
38 except (KeyError, NameError):
39
40 basestring = (str, bytes)
41
43 if not s:
44 return s
45 import sys
46 if sys.version_info[0] >= 3:
47 sub = re.compile(r"^(\s*)u'", re.M).sub
48 else:
49 sub = re.compile(r"^(\s*)b'", re.M).sub
50 return sub(r"\1'", s)
51
52 __all__ = [
53 'document_fromstring', 'fragment_fromstring', 'fragments_fromstring', 'fromstring',
54 'tostring', 'Element', 'defs', 'open_in_browser', 'submit_form',
55 'find_rel_links', 'find_class', 'make_links_absolute',
56 'resolve_base_href', 'iterlinks', 'rewrite_links', 'open_in_browser', 'parse']
57
58 XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml"
59
60 _rel_links_xpath = etree.XPath("descendant-or-self::a[@rel]|descendant-or-self::x:a[@rel]",
61 namespaces={'x':XHTML_NAMESPACE})
62 _options_xpath = etree.XPath("descendant-or-self::option|descendant-or-self::x:option",
63 namespaces={'x':XHTML_NAMESPACE})
64 _forms_xpath = etree.XPath("descendant-or-self::form|descendant-or-self::x:form",
65 namespaces={'x':XHTML_NAMESPACE})
66
67 _class_xpath = etree.XPath("descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), concat(' ', $class_name, ' '))]")
68 _id_xpath = etree.XPath("descendant-or-self::*[@id=$id]")
69 _collect_string_content = etree.XPath("string()")
70 _css_url_re = re.compile(r'url\(('+'["][^"]*["]|'+"['][^']*[']|"+r'[^)]*)\)', re.I)
71 _css_import_re = re.compile(r'@import "(.*?)"')
72 _label_xpath = etree.XPath("//label[@for=$id]|//x:label[@for=$id]",
73 namespaces={'x':XHTML_NAMESPACE})
74 _archive_re = re.compile(r'[^ ]+')
75
77 if s[:1] == '"' and s[-1:] == '"' or s[:1] == "'" and s[-1:] == "'":
78 return s[1:-1], pos+1
79 else:
80 return s,pos
81
91
97
99
101 """
102 Returns the base URL, given when the page was parsed.
103
104 Use with ``urlparse.urljoin(el.base_url, href)`` to get
105 absolute URLs.
106 """
107 return self.getroottree().docinfo.URL
108 base_url = property(base_url, doc=base_url.__doc__)
109
115 forms = property(forms, doc=forms.__doc__)
116
118 """
119 Return the <body> element. Can be called from a child element
120 to get the document's head.
121 """
122 return self.xpath('//body|//x:body', namespaces={'x':XHTML_NAMESPACE})[0]
123 body = property(body, doc=body.__doc__)
124
126 """
127 Returns the <head> element. Can be called from a child
128 element to get the document's head.
129 """
130 return self.xpath('//head|//x:head', namespaces={'x':XHTML_NAMESPACE})[0]
131 head = property(head, doc=head.__doc__)
132
134 """
135 Get or set any <label> element associated with this element.
136 """
137 id = self.get('id')
138 if not id:
139 return None
140 result = _label_xpath(self, id=id)
141 if not result:
142 return None
143 else:
144 return result[0]
146 id = self.get('id')
147 if not id:
148 raise TypeError(
149 "You cannot set a label for an element (%r) that has no id"
150 % self)
151 if _nons(label.tag) != 'label':
152 raise TypeError(
153 "You can only assign label to a label element (not %r)"
154 % label)
155 label.set('for', id)
160 label = property(_label__get, _label__set, _label__del, doc=_label__get.__doc__)
161
163 """
164 Removes this element from the tree, including its children and
165 text. The tail text is joined to the previous element or
166 parent.
167 """
168 parent = self.getparent()
169 assert parent is not None
170 if self.tail:
171 previous = self.getprevious()
172 if previous is None:
173 parent.text = (parent.text or '') + self.tail
174 else:
175 previous.tail = (previous.tail or '') + self.tail
176 parent.remove(self)
177
179 """
180 Remove the tag, but not its children or text. The children and text
181 are merged into the parent.
182
183 Example::
184
185 >>> h = fragment_fromstring('<div>Hello <b>World!</b></div>')
186 >>> h.find('.//b').drop_tag()
187 >>> print(tostring(h, encoding=unicode))
188 <div>Hello World!</div>
189 """
190 parent = self.getparent()
191 assert parent is not None
192 previous = self.getprevious()
193 if self.text and isinstance(self.tag, basestring):
194
195 if previous is None:
196 parent.text = (parent.text or '') + self.text
197 else:
198 previous.tail = (previous.tail or '') + self.text
199 if self.tail:
200 if len(self):
201 last = self[-1]
202 last.tail = (last.tail or '') + self.tail
203 elif previous is None:
204 parent.text = (parent.text or '') + self.tail
205 else:
206 previous.tail = (previous.tail or '') + self.tail
207 index = parent.index(self)
208 parent[index:index+1] = self[:]
209
211 """
212 Find any links like ``<a rel="{rel}">...</a>``; returns a list of elements.
213 """
214 rel = rel.lower()
215 return [el for el in _rel_links_xpath(self)
216 if el.get('rel').lower() == rel]
217
219 """
220 Find any elements with the given class name.
221 """
222 return _class_xpath(self, class_name=class_name)
223
225 """
226 Get the first element in a document with the given id. If none is
227 found, return the default argument if provided or raise KeyError
228 otherwise.
229
230 Note that there can be more than one element with the same id,
231 and this isn't uncommon in HTML documents found in the wild.
232 Browsers return only the first match, and this function does
233 the same.
234 """
235 try:
236
237
238 return _id_xpath(self, id=id)[0]
239 except IndexError:
240 if default:
241 return default[0]
242 else:
243 raise KeyError(id)
244
245 - def text_content(self):
246 """
247 Return the text content of the tag (and the text in any children).
248 """
249 return _collect_string_content(self)
250
252 """
253 Run the CSS expression on this element and its children,
254 returning a list of the results.
255
256 Equivalent to lxml.cssselect.CSSSelect(expr)(self) -- note
257 that pre-compiling the expression can provide a substantial
258 speedup.
259 """
260 return cssselect.CSSSelector(expr)(self)
261
262
263
264
265
267 """
268 Make all links in the document absolute, given the
269 ``base_url`` for the document (the full URL where the document
270 came from), or if no ``base_url`` is given, then the ``.base_url`` of the document.
271
272 If ``resolve_base_href`` is true, then any ``<base href>``
273 tags in the document are used *and* removed from the document.
274 If it is false then any such tag is ignored.
275 """
276 if base_url is None:
277 base_url = self.base_url
278 if base_url is None:
279 raise TypeError(
280 "No base_url given, and the document has no base_url")
281 if resolve_base_href:
282 self.resolve_base_href()
283 def link_repl(href):
284 return urljoin(base_url, href)
285 self.rewrite_links(link_repl)
286
288 """
289 Find any ``<base href>`` tag in the document, and apply its
290 values to all links found in the document. Also remove the
291 tag once it has been applied.
292 """
293 base_href = None
294 basetags = self.xpath('//base[@href]|//x:base[@href]', namespaces={'x':XHTML_NAMESPACE})
295 for b in basetags:
296 base_href = b.get('href')
297 b.drop_tree()
298 if not base_href:
299 return
300 self.make_links_absolute(base_href, resolve_base_href=False)
301
303 """
304 Yield (element, attribute, link, pos), where attribute may be None
305 (indicating the link is in the text). ``pos`` is the position
306 where the link occurs; often 0, but sometimes something else in
307 the case of links in stylesheets or style tags.
308
309 Note: <base href> is *not* taken into account in any way. The
310 link you get is exactly the link in the document.
311 """
312 link_attrs = defs.link_attrs
313 for el in self.iter():
314 attribs = el.attrib
315 tag = _nons(el.tag)
316 if tag != 'object':
317 for attrib in link_attrs:
318 if attrib in attribs:
319 yield (el, attrib, attribs[attrib], 0)
320 elif tag == 'object':
321 codebase = None
322
323
324 if 'codebase' in attribs:
325 codebase = el.get('codebase')
326 yield (el, 'codebase', codebase, 0)
327 for attrib in 'classid', 'data':
328 if attrib in attribs:
329 value = el.get(attrib)
330 if codebase is not None:
331 value = urljoin(codebase, value)
332 yield (el, attrib, value, 0)
333 if 'archive' in attribs:
334 for match in _archive_re.finditer(el.get('archive')):
335 value = match.group(0)
336 if codebase is not None:
337 value = urljoin(codebase, value)
338 yield (el, 'archive', value, match.start())
339 if tag == 'param':
340 valuetype = el.get('valuetype') or ''
341 if valuetype.lower() == 'ref':
342
343
344
345
346
347
348 yield (el, 'value', el.get('value'), 0)
349 if tag == 'style' and el.text:
350 for match in _css_url_re.finditer(el.text):
351 url, start = _unquote_match(match.group(1), match.start(1))
352 yield (el, None, url, start)
353 for match in _css_import_re.finditer(el.text):
354 yield (el, None, match.group(1), match.start(1))
355 if 'style' in attribs:
356 for match in _css_url_re.finditer(attribs['style']):
357 url, start = _unquote_match(match.group(1), match.start(1))
358 yield (el, 'style', url, start)
359
360 - def rewrite_links(self, link_repl_func, resolve_base_href=True,
361 base_href=None):
362 """
363 Rewrite all the links in the document. For each link
364 ``link_repl_func(link)`` will be called, and the return value
365 will replace the old link.
366
367 Note that links may not be absolute (unless you first called
368 ``make_links_absolute()``), and may be internal (e.g.,
369 ``'#anchor'``). They can also be values like
370 ``'mailto:email'`` or ``'javascript:expr'``.
371
372 If you give ``base_href`` then all links passed to
373 ``link_repl_func()`` will take that into account.
374
375 If the ``link_repl_func`` returns None, the attribute or
376 tag text will be removed completely.
377 """
378 if base_href is not None:
379
380
381 self.make_links_absolute(base_href, resolve_base_href=resolve_base_href)
382 elif resolve_base_href:
383 self.resolve_base_href()
384 for el, attrib, link, pos in self.iterlinks():
385 new_link = link_repl_func(link.strip())
386 if new_link == link:
387 continue
388 if new_link is None:
389
390 if attrib is None:
391 el.text = ''
392 else:
393 del el.attrib[attrib]
394 continue
395 if attrib is None:
396 new = el.text[:pos] + new_link + el.text[pos+len(link):]
397 el.text = new
398 else:
399 cur = el.attrib[attrib]
400 if not pos and len(cur) == len(link):
401
402 el.attrib[attrib] = new_link
403 else:
404 new = cur[:pos] + new_link + cur[pos+len(link):]
405 el.attrib[attrib] = new
406
407
409 """
410 An object that represents a method on an element as a function;
411 the function takes either an element or an HTML string. It
412 returns whatever the function normally returns, or if the function
413 works in-place (and so returns None) it returns a serialized form
414 of the resulting document.
415 """
421 result_type = type(doc)
422 if isinstance(doc, basestring):
423 if 'copy' in kw:
424 raise TypeError(
425 "The keyword 'copy' can only be used with element inputs to %s, not a string input" % self.name)
426 doc = fromstring(doc, **kw)
427 else:
428 if 'copy' in kw:
429 copy = kw.pop('copy')
430 else:
431 copy = self.copy
432 if copy:
433 doc = copy.deepcopy(doc)
434 meth = getattr(doc, self.name)
435 result = meth(*args, **kw)
436
437 if result is None:
438
439 return _transform_result(result_type, doc)
440 else:
441 return result
442
443 find_rel_links = _MethodFunc('find_rel_links', copy=False)
444 find_class = _MethodFunc('find_class', copy=False)
445 make_links_absolute = _MethodFunc('make_links_absolute', copy=True)
446 resolve_base_href = _MethodFunc('resolve_base_href', copy=True)
447 iterlinks = _MethodFunc('iterlinks', copy=False)
448 rewrite_links = _MethodFunc('rewrite_links', copy=True)
449
452
455
458
461
462
464 """A lookup scheme for HTML Element classes.
465
466 To create a lookup instance with different Element classes, pass a tag
467 name mapping of Element classes in the ``classes`` keyword argument and/or
468 a tag name mapping of Mixin classes in the ``mixins`` keyword argument.
469 The special key '*' denotes a Mixin class that should be mixed into all
470 Element classes.
471 """
472 _default_element_classes = {}
473
474 - def __init__(self, classes=None, mixins=None):
491
492 - def lookup(self, node_type, document, namespace, name):
503
504
505
506
507
516
519 """
520 Parses several HTML elements, returning a list of elements.
521
522 The first item in the list may be a string (though leading
523 whitespace is removed). If no_leading_text is true, then it will
524 be an error if there is leading text, and it will always be a list
525 of only elements.
526
527 base_url will set the document's base_url attribute (and the tree's docinfo.URL)
528 """
529 if parser is None:
530 parser = html_parser
531
532 start = html[:20].lstrip().lower()
533 if not start.startswith('<html') and not start.startswith('<!doctype'):
534 html = '<html><body>%s</body></html>' % html
535 doc = document_fromstring(html, parser=parser, base_url=base_url, **kw)
536 assert _nons(doc.tag) == 'html'
537 bodies = [e for e in doc if _nons(e.tag) == 'body']
538 assert len(bodies) == 1, ("too many bodies: %r in %r" % (bodies, html))
539 body = bodies[0]
540 elements = []
541 if no_leading_text and body.text and body.text.strip():
542 raise etree.ParserError(
543 "There is leading text: %r" % body.text)
544 if body.text and body.text.strip():
545 elements.append(body.text)
546 elements.extend(body)
547
548
549 return elements
550
553 """
554 Parses a single HTML element; it is an error if there is more than
555 one element, or if anything but whitespace precedes or follows the
556 element.
557
558 If create_parent is true (or is a tag name) then a parent node
559 will be created to encapsulate the HTML in a single element.
560
561 base_url will set the document's base_url attribute (and the tree's docinfo.URL)
562 """
563 if parser is None:
564 parser = html_parser
565 if create_parent:
566 if not isinstance(create_parent, basestring):
567 create_parent = 'div'
568 return fragment_fromstring('<%s>%s</%s>' % (
569 create_parent, html, create_parent),
570 parser=parser, base_url=base_url, **kw)
571 elements = fragments_fromstring(html, parser=parser, no_leading_text=True,
572 base_url=base_url, **kw)
573 if not elements:
574 raise etree.ParserError(
575 "No elements found")
576 if len(elements) > 1:
577 raise etree.ParserError(
578 "Multiple elements found (%s)"
579 % ', '.join([_element_name(e) for e in elements]))
580 el = elements[0]
581 if el.tail and el.tail.strip():
582 raise etree.ParserError(
583 "Element followed by text: %r" % el.tail)
584 el.tail = None
585 return el
586
587 -def fromstring(html, base_url=None, parser=None, **kw):
649
650 -def parse(filename_or_url, parser=None, base_url=None, **kw):
651 """
652 Parse a filename, URL, or file-like object into an HTML document
653 tree. Note: this returns a tree, not an element. Use
654 ``parse(...).getroot()`` to get the document root.
655
656 You can override the base URL with the ``base_url`` keyword. This
657 is most useful when parsing from a file-like object.
658 """
659 if parser is None:
660 parser = html_parser
661 return etree.parse(filename_or_url, parser, base_url=base_url, **kw)
662
670
672 if isinstance(el, etree.CommentBase):
673 return 'comment'
674 elif isinstance(el, basestring):
675 return 'string'
676 else:
677 return _nons(el.tag)
678
679
680
681
682
787
788 HtmlElementClassLookup._default_element_classes['form'] = FormElement
789
822
824 import urllib
825
826 if method == 'GET':
827 if '?' in url:
828 url += '&'
829 else:
830 url += '?'
831 url += urllib.urlencode(values)
832 data = None
833 else:
834 data = urllib.urlencode(values)
835 return urllib.urlopen(url, data)
836
838
846 raise KeyError(
847 "You cannot remove keys from ElementDict")
851 return item in self.inputs
852
854 return '<%s for form %s>' % (
855 self.__class__.__name__,
856 self.inputs.form._name())
857
923
951
952 -class TextareaElement(InputMixin, HtmlElement):
953 """
954 ``<textarea>`` element. You can get the name with ``.name`` and
955 get/set the value with ``.value``
956 """
957
958 - def _value__get(self):
959 """
960 Get/set the value (which is the contents of this element)
961 """
962 return self.text or ''
963 - def _value__set(self, value):
965 - def _value__del(self):
967 value = property(_value__get, _value__set, _value__del, doc=_value__get.__doc__)
968
969 HtmlElementClassLookup._default_element_classes['textarea'] = TextareaElement
970
972 """
973 ``<select>`` element. You can get the name with ``.name``.
974
975 ``.value`` will be the value of the selected option, unless this
976 is a multi-select element (``<select multiple>``), in which case
977 it will be a set-like object. In either case ``.value_options``
978 gives the possible values.
979
980 The boolean attribute ``.multiple`` shows if this is a
981 multi-select.
982 """
983
985 """
986 Get/set the value of this select (the selected option).
987
988 If this is a multi-select, this is a set-like object that
989 represents all the selected options.
990 """
991 if self.multiple:
992 return MultipleSelectOptions(self)
993 for el in _options_xpath(self):
994 if 'selected' in el.attrib:
995 value = el.get('value')
996
997 return value
998 return None
999
1001 if self.multiple:
1002 if isinstance(value, basestring):
1003 raise TypeError(
1004 "You must pass in a sequence")
1005 self.value.clear()
1006 self.value.update(value)
1007 return
1008 if value is not None:
1009 for el in _options_xpath(self):
1010
1011 if el.get('value') == value:
1012 checked_option = el
1013 break
1014 else:
1015 raise ValueError(
1016 "There is no option with the value of %r" % value)
1017 for el in _options_xpath(self):
1018 if 'selected' in el.attrib:
1019 del el.attrib['selected']
1020 if value is not None:
1021 checked_option.set('selected', '')
1022
1029
1030 value = property(_value__get, _value__set, _value__del, doc=_value__get.__doc__)
1031
1033 """
1034 All the possible values this select can have (the ``value``
1035 attribute of all the ``<option>`` elements.
1036 """
1037 return [el.get('value') for el in _options_xpath(self)]
1038 value_options = property(value_options, doc=value_options.__doc__)
1039
1041 """
1042 Boolean attribute: is there a ``multiple`` attribute on this element.
1043 """
1044 return 'multiple' in self.attrib
1046 if value:
1047 self.set('multiple', '')
1048 elif 'multiple' in self.attrib:
1049 del self.attrib['multiple']
1050 multiple = property(_multiple__get, _multiple__set, doc=_multiple__get.__doc__)
1051
1052 HtmlElementClassLookup._default_element_classes['select'] = SelectElement
1053
1055 """
1056 Represents all the selected options in a ``<select multiple>`` element.
1057
1058 You can add to this set-like option to select an option, or remove
1059 to unselect the option.
1060 """
1061
1063 self.select = select
1064
1066 """
1067 Iterator of all the ``<option>`` elements.
1068 """
1069 return iter(_options_xpath(self.select))
1070 options = property(options)
1071
1073 for option in self.options:
1074 yield option.get('value')
1075
1076 - def add(self, item):
1077 for option in self.options:
1078 if option.get('value') == item:
1079 option.set('selected', '')
1080 break
1081 else:
1082 raise ValueError(
1083 "There is no option with the value %r" % item)
1084
1086 for option in self.options:
1087 if option.get('value') == item:
1088 if 'selected' in option.attrib:
1089 del option.attrib['selected']
1090 else:
1091 raise ValueError(
1092 "The option %r is not currently selected" % item)
1093 break
1094 else:
1095 raise ValueError(
1096 "There is not option with the value %r" % item)
1097
1099 return '<%s {%s} for select name=%r>' % (
1100 self.__class__.__name__,
1101 ', '.join([repr(v) for v in self]),
1102 self.select.name)
1103
1105 """
1106 This object represents several ``<input type=radio>`` elements
1107 that have the same name.
1108
1109 You can use this like a list, but also use the property
1110 ``.value`` to check/uncheck inputs. Also you can use
1111 ``.value_options`` to get the possible values.
1112 """
1113
1115 """
1116 Get/set the value, which checks the radio with that value (and
1117 unchecks any other value).
1118 """
1119 for el in self:
1120 if 'checked' in el.attrib:
1121 return el.get('value')
1122 return None
1123
1125 if value is not None:
1126 for el in self:
1127 if el.get('value') == value:
1128 checked_option = el
1129 break
1130 else:
1131 raise ValueError(
1132 "There is no radio input with the value %r" % value)
1133 for el in self:
1134 if 'checked' in el.attrib:
1135 del el.attrib['checked']
1136 if value is not None:
1137 checked_option.set('checked', '')
1138
1141
1142 value = property(_value__get, _value__set, _value__del, doc=_value__get.__doc__)
1143
1145 """
1146 Returns a list of all the possible values.
1147 """
1148 return [el.get('value') for el in self]
1149 value_options = property(value_options, doc=value_options.__doc__)
1150
1152 return '%s(%s)' % (
1153 self.__class__.__name__,
1154 list.__repr__(self))
1155
1157 """
1158 Represents a group of checkboxes (``<input type=checkbox>``) that
1159 have the same name.
1160
1161 In addition to using this like a list, the ``.value`` attribute
1162 returns a set-like object that you can add to or remove from to
1163 check and uncheck checkboxes. You can also use ``.value_options``
1164 to get the possible values.
1165 """
1166
1168 """
1169 Return a set-like object that can be modified to check or
1170 uncheck individual checkboxes according to their value.
1171 """
1172 return CheckboxValues(self)
1182 value = property(_value__get, _value__set, _value__del, doc=_value__get.__doc__)
1183
1185 return '%s(%s)' % (
1186 self.__class__.__name__, list.__repr__(self))
1187
1189
1190 """
1191 Represents the values of the checked checkboxes in a group of
1192 checkboxes with the same name.
1193 """
1194
1197
1199 return iter([
1200 el.get('value')
1201 for el in self.group
1202 if 'checked' in el.attrib])
1203
1204 - def add(self, value):
1205 for el in self.group:
1206 if el.get('value') == value:
1207 el.set('checked', '')
1208 break
1209 else:
1210 raise KeyError("No checkbox with value %r" % value)
1211
1213 for el in self.group:
1214 if el.get('value') == value:
1215 if 'checked' in el.attrib:
1216 del el.attrib['checked']
1217 else:
1218 raise KeyError(
1219 "The checkbox with value %r was already unchecked" % value)
1220 break
1221 else:
1222 raise KeyError(
1223 "No checkbox with value %r" % value)
1224
1226 return '<%s {%s} for checkboxes name=%r>' % (
1227 self.__class__.__name__,
1228 ', '.join([repr(v) for v in self]),
1229 self.group.name)
1230
1314
1315 HtmlElementClassLookup._default_element_classes['input'] = InputElement
1316
1318 """
1319 Represents a ``<label>`` element.
1320
1321 Label elements are linked to other elements with their ``for``
1322 attribute. You can access this element with ``label.for_element``.
1323 """
1324
1326 """
1327 Get/set the element this label points to. Return None if it
1328 can't be found.
1329 """
1330 id = self.get('for')
1331 if not id:
1332 return None
1333 return self.body.get_element_by_id(id)
1335 id = other.get('id')
1336 if not id:
1337 raise TypeError(
1338 "Element %r has no id attribute" % other)
1339 self.set('for', id)
1343 for_element = property(_for_element__get, _for_element__set, _for_element__del,
1344 doc=_for_element__get.__doc__)
1345
1346 HtmlElementClassLookup._default_element_classes['label'] = LabelElement
1347
1348
1349
1350
1351
1353 """Convert all tags in an HTML tree to XHTML by moving them to the
1354 XHTML namespace.
1355 """
1356 try:
1357 html = html.getroot()
1358 except AttributeError:
1359 pass
1360 prefix = "{%s}" % XHTML_NAMESPACE
1361 for el in html.iter():
1362 tag = el.tag
1363 if isinstance(tag, basestring):
1364 if tag[0] != '{':
1365 el.tag = prefix + tag
1366
1368 """Convert all tags in an XHTML tree to HTML by removing their
1369 XHTML namespace.
1370 """
1371 try:
1372 xhtml = xhtml.getroot()
1373 except AttributeError:
1374 pass
1375 prefix = "{%s}" % XHTML_NAMESPACE
1376 prefix_len = len(prefix)
1377 for el in xhtml.iter(prefix + "*"):
1378 el.tag = el.tag[prefix_len:]
1379
1380
1381
1382 __replace_meta_content_type = re.compile(
1383 r'<meta http-equiv="Content-Type"[^>]*>').sub
1384
1385 -def tostring(doc, pretty_print=False, include_meta_content_type=False,
1386 encoding=None, method="html"):
1387 """Return an HTML string representation of the document.
1388
1389 Note: if include_meta_content_type is true this will create a
1390 ``<meta http-equiv="Content-Type" ...>`` tag in the head;
1391 regardless of the value of include_meta_content_type any existing
1392 ``<meta http-equiv="Content-Type" ...>`` tag will be removed
1393
1394 The ``encoding`` argument controls the output encoding (defauts to
1395 ASCII, with &#...; character references for any characters outside
1396 of ASCII).
1397
1398 The ``method`` argument defines the output method. It defaults to
1399 'html', but can also be 'xml' for xhtml output, or 'text' to
1400 serialise to plain text without markup. Note that you can pass
1401 the builtin ``unicode`` type as ``encoding`` argument to serialise
1402 to a unicode string.
1403
1404 Example::
1405
1406 >>> from lxml import html
1407 >>> root = html.fragment_fromstring('<p>Hello<br>world!</p>')
1408
1409 >>> html.tostring(root)
1410 b'<p>Hello<br>world!</p>'
1411 >>> html.tostring(root, method='html')
1412 b'<p>Hello<br>world!</p>'
1413
1414 >>> html.tostring(root, method='xml')
1415 b'<p>Hello<br/>world!</p>'
1416
1417 >>> html.tostring(root, method='text')
1418 b'Helloworld!'
1419
1420 >>> html.tostring(root, method='text', encoding=unicode)
1421 u'Helloworld!'
1422 """
1423 html = etree.tostring(doc, method=method, pretty_print=pretty_print,
1424 encoding=encoding)
1425 if not include_meta_content_type:
1426 html = __replace_meta_content_type('', html)
1427 return html
1428
1429 tostring.__doc__ = __fix_docstring(tostring.__doc__)
1430
1432 """
1433 Open the HTML document in a web browser (saving it to a temporary
1434 file to open it).
1435 """
1436 import os
1437 import webbrowser
1438 try:
1439 write_doc = doc.write
1440 except AttributeError:
1441 write_doc = etree.ElementTree(element=doc).write
1442 fn = os.tempnam() + '.html'
1443 write_doc(fn, method="html")
1444 url = 'file://' + fn.replace(os.path.sep, '/')
1445 print(url)
1446 webbrowser.open(url)
1447
1448
1449
1450
1451
1456
1461
1463 """Create a new HTML Element.
1464
1465 This can also be used for XHTML documents.
1466 """
1467 v = html_parser.makeelement(*args, **kw)
1468 return v
1469
1470 html_parser = HTMLParser()
1471 xhtml_parser = XHTMLParser()
1472