home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 February / maximum-cd-2011-02.iso / DiscContents / digsby_setup85.exe / lib / util / BeautifulSoup.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-11-24  |  45.2 KB  |  1,589 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.6)
  3.  
  4. from __future__ import generators
  5. __author__ = 'Leonard Richardson (crummy.com)'
  6. __contributors__ = [
  7.     'Sam Ruby (intertwingly.net)',
  8.     'the unwitting Mark Pilgrim (diveintomark.org)',
  9.     'http://www.crummy.com/software/BeautifulSoup/AUTHORS.html']
  10. __version__ = '3.0.3'
  11. __copyright__ = 'Copyright (c) 2004-2006 Leonard Richardson'
  12. __license__ = 'PSF'
  13. from sgmllib import SGMLParser as _SGMLParser
  14.  
  15. class SGMLParser(_SGMLParser):
  16.     
  17.     def convert_charref(self, name):
  18.         
  19.         try:
  20.             n = int(name)
  21.         except ValueError:
  22.             return None
  23.  
  24.         if n <= n:
  25.             pass
  26.         elif not n <= 127:
  27.             return None
  28.         return self.convert_codepoint(n)
  29.  
  30.  
  31. from sgmllib import SGMLParseError
  32. import codecs
  33. import types
  34. import re
  35. import sgmllib
  36. from htmlentitydefs import name2codepoint
  37. sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
  38. sgmllib.charref = re.compile('&#(\\d+|x[0-9a-fA-F]+);')
  39. DEFAULT_OUTPUT_ENCODING = 'utf-8'
  40.  
  41. class PageElement:
  42.     
  43.     def setup(self, parent = None, previous = None):
  44.         self.parent = parent
  45.         self.previous = previous
  46.         self.next = None
  47.         self.previousSibling = None
  48.         self.nextSibling = None
  49.         if self.parent and self.parent.contents:
  50.             self.previousSibling = self.parent.contents[-1]
  51.             self.previousSibling.nextSibling = self
  52.         
  53.  
  54.     
  55.     def replaceWith(self, replaceWith):
  56.         oldParent = self.parent
  57.         myIndex = self.parent.contents.index(self)
  58.         if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:
  59.             index = self.parent.contents.index(replaceWith)
  60.             if index and index < myIndex:
  61.                 myIndex = myIndex - 1
  62.             
  63.         
  64.         self.extract()
  65.         oldParent.insert(myIndex, replaceWith)
  66.  
  67.     
  68.     def extract(self):
  69.         if self.parent:
  70.             
  71.             try:
  72.                 self.parent.contents.remove(self)
  73.             except ValueError:
  74.                 pass
  75.             except:
  76.                 None<EXCEPTION MATCH>ValueError
  77.             
  78.  
  79.         None<EXCEPTION MATCH>ValueError
  80.         lastChild = self._lastRecursiveChild()
  81.         nextElement = lastChild.next
  82.         if self.previous:
  83.             self.previous.next = nextElement
  84.         
  85.         if nextElement:
  86.             nextElement.previous = self.previous
  87.         
  88.         self.previous = None
  89.         lastChild.next = None
  90.         self.parent = None
  91.         if self.previousSibling:
  92.             self.previousSibling.nextSibling = self.nextSibling
  93.         
  94.         if self.nextSibling:
  95.             self.nextSibling.previousSibling = self.previousSibling
  96.         
  97.         self.previousSibling = None
  98.         self.nextSibling = None
  99.  
  100.     
  101.     def _lastRecursiveChild(self):
  102.         lastChild = self
  103.         while hasattr(lastChild, 'contents') and lastChild.contents:
  104.             lastChild = lastChild.contents[-1]
  105.         return lastChild
  106.  
  107.     
  108.     def insert(self, position, newChild):
  109.         if (isinstance(newChild, basestring) or isinstance(newChild, unicode)) and not isinstance(newChild, NavigableString):
  110.             newChild = NavigableString(newChild)
  111.         
  112.         position = min(position, len(self.contents))
  113.         if hasattr(newChild, 'parent') and newChild.parent != None:
  114.             if newChild.parent == self:
  115.                 index = self.find(newChild)
  116.                 if index and index < position:
  117.                     position = position - 1
  118.                 
  119.             
  120.             newChild.extract()
  121.         
  122.         newChild.parent = self
  123.         previousChild = None
  124.         if position == 0:
  125.             newChild.previousSibling = None
  126.             newChild.previous = self
  127.         else:
  128.             previousChild = self.contents[position - 1]
  129.             newChild.previousSibling = previousChild
  130.             newChild.previousSibling.nextSibling = newChild
  131.             newChild.previous = previousChild._lastRecursiveChild()
  132.         if newChild.previous:
  133.             newChild.previous.next = newChild
  134.         
  135.         newChildsLastElement = newChild._lastRecursiveChild()
  136.         if position >= len(self.contents):
  137.             newChild.nextSibling = None
  138.             parent = self
  139.             parentsNextSibling = None
  140.             while not parentsNextSibling:
  141.                 parentsNextSibling = parent.nextSibling
  142.                 parent = parent.parent
  143.                 if not parent:
  144.                     break
  145.                     continue
  146.             if parentsNextSibling:
  147.                 newChildsLastElement.next = parentsNextSibling
  148.             else:
  149.                 newChildsLastElement.next = None
  150.         else:
  151.             nextChild = self.contents[position]
  152.             newChild.nextSibling = nextChild
  153.             if newChild.nextSibling:
  154.                 newChild.nextSibling.previousSibling = newChild
  155.             
  156.             newChildsLastElement.next = nextChild
  157.         if newChildsLastElement.next:
  158.             newChildsLastElement.next.previous = newChildsLastElement
  159.         
  160.         self.contents.insert(position, newChild)
  161.  
  162.     
  163.     def findNext(self, name = None, attrs = { }, text = None, **kwargs):
  164.         return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
  165.  
  166.     
  167.     def findAllNext(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  168.         return self._findAll(name, attrs, text, limit, self.nextGenerator)
  169.  
  170.     
  171.     def findNextSibling(self, name = None, attrs = { }, text = None, **kwargs):
  172.         return self._findOne(self.findNextSiblings, name, attrs, text, **kwargs)
  173.  
  174.     
  175.     def findNextSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  176.         return self._findAll(name, attrs, text, limit, self.nextSiblingGenerator, **kwargs)
  177.  
  178.     fetchNextSiblings = findNextSiblings
  179.     
  180.     def findPrevious(self, name = None, attrs = { }, text = None, **kwargs):
  181.         return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
  182.  
  183.     
  184.     def findAllPrevious(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  185.         return self._findAll(name, attrs, text, limit, self.previousGenerator, **kwargs)
  186.  
  187.     fetchPrevious = findAllPrevious
  188.     
  189.     def findPreviousSibling(self, name = None, attrs = { }, text = None, **kwargs):
  190.         return self._findOne(self.findPreviousSiblings, name, attrs, text, **kwargs)
  191.  
  192.     
  193.     def findPreviousSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  194.         return self._findAll(name, attrs, text, limit, self.previousSiblingGenerator, **kwargs)
  195.  
  196.     fetchPreviousSiblings = findPreviousSiblings
  197.     
  198.     def findParent(self, name = None, attrs = { }, **kwargs):
  199.         r = None
  200.         l = self.findParents(name, attrs, 1)
  201.         if l:
  202.             r = l[0]
  203.         
  204.         return r
  205.  
  206.     
  207.     def findParents(self, name = None, attrs = { }, limit = None, **kwargs):
  208.         return self._findAll(name, attrs, None, limit, self.parentGenerator, **kwargs)
  209.  
  210.     fetchParents = findParents
  211.     
  212.     def _findOne(self, method, name, attrs, text, **kwargs):
  213.         r = None
  214.         l = method(name, attrs, text, 1, **kwargs)
  215.         if l:
  216.             r = l[0]
  217.         
  218.         return r
  219.  
  220.     
  221.     def _findAll(self, name, attrs, text, limit, generator, **kwargs):
  222.         if isinstance(name, SoupStrainer):
  223.             strainer = name
  224.         else:
  225.             strainer = SoupStrainer(name, attrs, text, **kwargs)
  226.         results = ResultSet(strainer)
  227.         g = generator()
  228.         while True:
  229.             
  230.             try:
  231.                 i = g.next()
  232.             except StopIteration:
  233.                 break
  234.  
  235.             if i:
  236.                 found = strainer.search(i)
  237.                 if found:
  238.                     results.append(found)
  239.                     if limit and len(results) >= limit:
  240.                         break
  241.                     
  242.                 
  243.             found
  244.         return results
  245.  
  246.     
  247.     def nextGenerator(self):
  248.         i = self
  249.         while i:
  250.             i = i.next
  251.             yield i
  252.  
  253.     
  254.     def nextSiblingGenerator(self):
  255.         i = self
  256.         while i:
  257.             i = i.nextSibling
  258.             yield i
  259.  
  260.     
  261.     def previousGenerator(self):
  262.         i = self
  263.         while i:
  264.             i = i.previous
  265.             yield i
  266.  
  267.     
  268.     def previousSiblingGenerator(self):
  269.         i = self
  270.         while i:
  271.             i = i.previousSibling
  272.             yield i
  273.  
  274.     
  275.     def parentGenerator(self):
  276.         i = self
  277.         while i:
  278.             i = i.parent
  279.             yield i
  280.  
  281.     
  282.     def substituteEncoding(self, str, encoding = None):
  283.         if not encoding:
  284.             pass
  285.         encoding = 'utf-8'
  286.         return str.replace('%SOUP-ENCODING%', encoding)
  287.  
  288.     
  289.     def toEncoding(self, s, encoding = None):
  290.         if isinstance(s, unicode):
  291.             if encoding:
  292.                 s = s.encode(encoding)
  293.             
  294.         elif isinstance(s, str):
  295.             if encoding:
  296.                 s = s.encode(encoding)
  297.             else:
  298.                 s = unicode(s)
  299.         elif encoding:
  300.             s = self.toEncoding(str(s), encoding)
  301.         else:
  302.             s = unicode(s)
  303.         return s
  304.  
  305.  
  306.  
  307. class NavigableString(unicode, PageElement):
  308.     
  309.     def __getattr__(self, attr):
  310.         if attr == 'string':
  311.             return self
  312.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
  313.  
  314.     
  315.     def __unicode__(self):
  316.         return __str__(self, None)
  317.  
  318.     
  319.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  320.         if encoding:
  321.             return self.encode(encoding)
  322.         return self
  323.  
  324.  
  325.  
  326. class CData(NavigableString):
  327.     
  328.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  329.         return '<![CDATA[%s]]>' % NavigableString.__str__(self, encoding)
  330.  
  331.  
  332.  
  333. class ProcessingInstruction(NavigableString):
  334.     
  335.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  336.         output = self
  337.         if '%SOUP-ENCODING%' in output:
  338.             output = self.substituteEncoding(output, encoding)
  339.         
  340.         return '<?%s?>' % self.toEncoding(output, encoding)
  341.  
  342.  
  343.  
  344. class Comment(NavigableString):
  345.     
  346.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  347.         return '<!--%s-->' % NavigableString.__str__(self, encoding)
  348.  
  349.  
  350.  
  351. class Declaration(NavigableString):
  352.     
  353.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  354.         return '<!%s>' % NavigableString.__str__(self, encoding)
  355.  
  356.  
  357.  
  358. class Tag(PageElement):
  359.     XML_ENTITIES_TO_CHARS = {
  360.         'apos': "'",
  361.         'quot': '"',
  362.         'amp': '&',
  363.         'lt': '<',
  364.         'gt': '>' }
  365.     BARE_AMPERSAND = re.compile('&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)')
  366.     
  367.     def __init__(self, parser, name, attrs = None, parent = None, previous = None):
  368.         self.parserClass = parser.__class__
  369.         self.isSelfClosing = parser.isSelfClosingTag(name)
  370.         self.convertHTMLEntities = parser.convertHTMLEntities
  371.         self.name = name
  372.         if attrs == None:
  373.             attrs = []
  374.         
  375.         self.attrs = attrs
  376.         self.contents = []
  377.         self.setup(parent, previous)
  378.         self.hidden = False
  379.         self.containsSubstitutions = False
  380.  
  381.     
  382.     def get(self, key, default = None):
  383.         return self._getAttrMap().get(key, default)
  384.  
  385.     
  386.     def has_key(self, key):
  387.         return self._getAttrMap().has_key(key)
  388.  
  389.     
  390.     def __getitem__(self, key):
  391.         return self._getAttrMap()[key]
  392.  
  393.     
  394.     def __iter__(self):
  395.         return iter(self.contents)
  396.  
  397.     
  398.     def __len__(self):
  399.         return len(self.contents)
  400.  
  401.     
  402.     def __contains__(self, x):
  403.         return x in self.contents
  404.  
  405.     
  406.     def __nonzero__(self):
  407.         return True
  408.  
  409.     
  410.     def __setitem__(self, key, value):
  411.         self._getAttrMap()
  412.         self.attrMap[key] = value
  413.         found = False
  414.         for i in range(0, len(self.attrs)):
  415.             if self.attrs[i][0] == key:
  416.                 self.attrs[i] = (key, value)
  417.                 found = True
  418.                 continue
  419.         
  420.         if not found:
  421.             self.attrs.append((key, value))
  422.         
  423.         self._getAttrMap()[key] = value
  424.  
  425.     
  426.     def __delitem__(self, key):
  427.         for item in self.attrs:
  428.             if item[0] == key:
  429.                 self.attrs.remove(item)
  430.             
  431.             self._getAttrMap()
  432.             if self.attrMap.has_key(key):
  433.                 del self.attrMap[key]
  434.                 continue
  435.         
  436.  
  437.     
  438.     def __call__(self, *args, **kwargs):
  439.         return apply(self.findAll, args, kwargs)
  440.  
  441.     
  442.     def __getattr__(self, tag):
  443.         if len(tag) > 3 and tag.rfind('Tag') == len(tag) - 3:
  444.             return self.find(tag[:-3])
  445.         if tag.find('__') != 0:
  446.             return self.find(tag)
  447.  
  448.     
  449.     def __eq__(self, other):
  450.         if not hasattr(other, 'name') and not hasattr(other, 'attrs') and not hasattr(other, 'contents') and self.name != other.name and self.attrs != other.attrs or len(self) != len(other):
  451.             return False
  452.         for i in range(0, len(self.contents)):
  453.             if self.contents[i] != other.contents[i]:
  454.                 return False
  455.         
  456.         return True
  457.  
  458.     
  459.     def __ne__(self, other):
  460.         return not (self == other)
  461.  
  462.     
  463.     def __repr__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  464.         return self.__str__(encoding)
  465.  
  466.     
  467.     def __unicode__(self):
  468.         return self.__str__(None)
  469.  
  470.     
  471.     def _convertEntities(self, match):
  472.         x = match.group(1)
  473.         if x in name2codepoint:
  474.             return unichr(name2codepoint[x])
  475.         if '&' + x + ';' in self.XML_ENTITIES_TO_CHARS:
  476.             return '&%s;' % x
  477.         return '&%s;' % x
  478.  
  479.     
  480.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  481.         encodedName = self.toEncoding(self.name, encoding)
  482.         attrs = []
  483.         if self.attrs:
  484.             for key, val in self.attrs:
  485.                 fmt = '%s="%s"'
  486.                 if isString(val):
  487.                     if self.containsSubstitutions and '%SOUP-ENCODING%' in val:
  488.                         val = self.substituteEncoding(val, encoding)
  489.                     
  490.                     if '"' in val:
  491.                         if "'" in val:
  492.                             val = val.replace('"', '"')
  493.                         else:
  494.                             fmt = "%s='%s'"
  495.                     
  496.                     if self.convertHTMLEntities:
  497.                         val = re.sub('&(\\w+);', self._convertEntities, val)
  498.                     
  499.                     val = val.replace('<', '<').replace('>', '>')
  500.                     val = self.BARE_AMPERSAND.sub('&', val)
  501.                 
  502.                 attrs.append(fmt % (self.toEncoding(key, encoding), self.toEncoding(val, encoding)))
  503.             
  504.         
  505.         close = ''
  506.         closeTag = ''
  507.         if self.isSelfClosing:
  508.             close = ' /'
  509.         else:
  510.             closeTag = '</%s>' % encodedName
  511.         (indentTag, indentContents) = (0, 0)
  512.         if prettyPrint:
  513.             indentTag = indentLevel
  514.             space = ' ' * (indentTag - 1)
  515.             indentContents = indentTag + 1
  516.         
  517.         contents = self.renderContents(encoding, prettyPrint, indentContents)
  518.         if self.hidden:
  519.             s = contents
  520.         else:
  521.             s = []
  522.             attributeString = ''
  523.             if attrs:
  524.                 attributeString = ' ' + ' '.join(attrs)
  525.             
  526.             if prettyPrint:
  527.                 s.append(space)
  528.             
  529.             s.append('<%s%s%s>' % (encodedName, attributeString, close))
  530.             if prettyPrint:
  531.                 s.append('\n')
  532.             
  533.             s.append(contents)
  534.             if prettyPrint and contents and contents[-1] != '\n':
  535.                 s.append('\n')
  536.             
  537.             if prettyPrint and closeTag:
  538.                 s.append(space)
  539.             
  540.             s.append(closeTag)
  541.             if prettyPrint and closeTag and self.nextSibling:
  542.                 s.append('\n')
  543.             
  544.             s = ''.join(s)
  545.         return s
  546.  
  547.     
  548.     def prettify(self, encoding = DEFAULT_OUTPUT_ENCODING):
  549.         return self.__str__(encoding, True)
  550.  
  551.     
  552.     def renderContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  553.         s = []
  554.         for c in self:
  555.             text = None
  556.             if isinstance(c, NavigableString):
  557.                 text = c.__str__(encoding)
  558.             elif isinstance(c, Tag):
  559.                 s.append(c.__str__(encoding, prettyPrint, indentLevel))
  560.             
  561.             if text and prettyPrint:
  562.                 text = text.strip()
  563.             
  564.             if text:
  565.                 if prettyPrint:
  566.                     s.append(' ' * (indentLevel - 1))
  567.                 
  568.                 s.append(text)
  569.                 if prettyPrint:
  570.                     s.append('\n')
  571.                 
  572.             prettyPrint
  573.         
  574.         return ''.join(s)
  575.  
  576.     
  577.     def find(self, name = None, attrs = { }, recursive = True, text = None, **kwargs):
  578.         r = None
  579.         l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
  580.         if l:
  581.             r = l[0]
  582.         
  583.         return r
  584.  
  585.     findChild = find
  586.     
  587.     def findAll(self, name = None, attrs = { }, recursive = True, text = None, limit = None, **kwargs):
  588.         generator = self.recursiveChildGenerator
  589.         if not recursive:
  590.             generator = self.childGenerator
  591.         
  592.         return self._findAll(name, attrs, text, limit, generator, **kwargs)
  593.  
  594.     findChildren = findAll
  595.     first = find
  596.     fetch = findAll
  597.     
  598.     def fetchText(self, text = None, recursive = True, limit = None):
  599.         return self.findAll(text = text, recursive = recursive, limit = limit)
  600.  
  601.     
  602.     def firstText(self, text = None, recursive = True):
  603.         return self.find(text = text, recursive = recursive)
  604.  
  605.     
  606.     def append(self, tag):
  607.         self.contents.append(tag)
  608.  
  609.     
  610.     def _getAttrMap(self):
  611.         if not getattr(self, 'attrMap'):
  612.             self.attrMap = { }
  613.             for key, value in self.attrs:
  614.                 self.attrMap[key] = value
  615.             
  616.         
  617.         return self.attrMap
  618.  
  619.     
  620.     def childGenerator(self):
  621.         for i in range(0, len(self.contents)):
  622.             yield self.contents[i]
  623.         
  624.         raise StopIteration
  625.  
  626.     
  627.     def recursiveChildGenerator(self):
  628.         stack = [
  629.             (self, 0)]
  630.         while stack:
  631.             (tag, start) = stack.pop()
  632.             if isinstance(tag, Tag):
  633.                 for i in range(start, len(tag.contents)):
  634.                     a = tag.contents[i]
  635.                     yield a
  636.                     if isinstance(a, Tag) and tag.contents:
  637.                         if i < len(tag.contents) - 1:
  638.                             stack.append((tag, i + 1))
  639.                         
  640.                         stack.append((a, 0))
  641.                         break
  642.                         continue
  643.                 
  644.         raise StopIteration
  645.  
  646.  
  647.  
  648. class SoupStrainer:
  649.     
  650.     def __init__(self, name = None, attrs = { }, text = None, **kwargs):
  651.         self.name = name
  652.         if isString(attrs):
  653.             kwargs['class'] = attrs
  654.             attrs = None
  655.         
  656.         if kwargs:
  657.             if attrs:
  658.                 attrs = attrs.copy()
  659.                 attrs.update(kwargs)
  660.             else:
  661.                 attrs = kwargs
  662.         
  663.         self.attrs = attrs
  664.         self.text = text
  665.  
  666.     
  667.     def __str__(self):
  668.         if self.text:
  669.             return self.text
  670.         return '%s|%s' % (self.name, self.attrs)
  671.  
  672.     
  673.     def searchTag(self, markupName = None, markupAttrs = { }):
  674.         found = None
  675.         markup = None
  676.         if isinstance(markupName, Tag):
  677.             markup = markupName
  678.             markupAttrs = markup
  679.         
  680.         if callable(self.name):
  681.             pass
  682.         callFunctionWithTagData = not isinstance(markupName, Tag)
  683.         if not not (self.name) and callFunctionWithTagData:
  684.             if (markup or self._matches(markup, self.name) or not markup) and self._matches(markupName, self.name):
  685.                 if callFunctionWithTagData:
  686.                     match = self.name(markupName, markupAttrs)
  687.                 else:
  688.                     match = True
  689.                     markupAttrMap = None
  690.                     for attr, matchAgainst in self.attrs.items():
  691.                         if not markupAttrMap:
  692.                             if hasattr(markupAttrs, 'get'):
  693.                                 markupAttrMap = markupAttrs
  694.                             else:
  695.                                 markupAttrMap = { }
  696.                                 for k, v in markupAttrs:
  697.                                     markupAttrMap[k] = v
  698.                                 
  699.                         
  700.                         attrValue = markupAttrMap.get(attr)
  701.                         if not self._matches(attrValue, matchAgainst):
  702.                             match = False
  703.                             break
  704.                             continue
  705.                     
  706.                 if match:
  707.                     if markup:
  708.                         found = markup
  709.                     else:
  710.                         found = markupName
  711.                 
  712.             
  713.         return found
  714.  
  715.     
  716.     def search(self, markup):
  717.         found = None
  718.         if isList(markup) and not isinstance(markup, Tag):
  719.             for element in markup:
  720.                 if isinstance(element, NavigableString) and self.search(element):
  721.                     found = element
  722.                     break
  723.                     continue
  724.             
  725.         elif isinstance(markup, Tag):
  726.             if not self.text:
  727.                 found = self.searchTag(markup)
  728.             
  729.         elif isinstance(markup, NavigableString) or isString(markup):
  730.             if self._matches(markup, self.text):
  731.                 found = markup
  732.             
  733.         else:
  734.             raise Exception, "I don't know how to match against a %s" % markup.__class__
  735.         return isString(markup)
  736.  
  737.     
  738.     def _matches(self, markup, matchAgainst):
  739.         result = False
  740.         if matchAgainst == True and type(matchAgainst) == types.BooleanType:
  741.             result = markup != None
  742.         elif callable(matchAgainst):
  743.             result = matchAgainst(markup)
  744.         elif isinstance(markup, Tag):
  745.             markup = markup.name
  746.         
  747.         if markup and not isString(markup):
  748.             markup = unicode(markup)
  749.         
  750.         if hasattr(matchAgainst, 'match'):
  751.             if markup:
  752.                 pass
  753.             result = matchAgainst.search(markup)
  754.         elif isList(matchAgainst):
  755.             result = markup in matchAgainst
  756.         elif hasattr(matchAgainst, 'items'):
  757.             result = markup.has_key(matchAgainst)
  758.         elif matchAgainst and isString(markup):
  759.             if isinstance(markup, unicode):
  760.                 matchAgainst = unicode(matchAgainst)
  761.             else:
  762.                 matchAgainst = str(matchAgainst)
  763.         
  764.         if not result:
  765.             result = matchAgainst == markup
  766.         
  767.         return result
  768.  
  769.  
  770.  
  771. class ResultSet(list):
  772.     
  773.     def __init__(self, source):
  774.         list.__init__([])
  775.         self.source = source
  776.  
  777.  
  778.  
  779. def isList(l):
  780.     if not hasattr(l, '__iter__'):
  781.         pass
  782.     return type(l) in (types.ListType, types.TupleType)
  783.  
  784.  
  785. def isString(s):
  786.     
  787.     try:
  788.         if not isinstance(s, unicode):
  789.             pass
  790.         return isinstance(s, basestring)
  791.     except NameError:
  792.         return isinstance(s, str)
  793.  
  794.  
  795.  
  796. def buildTagMap(default, *args):
  797.     built = { }
  798.     for portion in args:
  799.         if hasattr(portion, 'items'):
  800.             for k, v in portion.items():
  801.                 built[k] = v
  802.             
  803.         if isList(portion):
  804.             for k in portion:
  805.                 built[k] = default
  806.             
  807.         built[portion] = default
  808.     
  809.     return built
  810.  
  811.  
  812. class BeautifulStoneSoup(Tag, SGMLParser):
  813.     SELF_CLOSING_TAGS = { }
  814.     NESTABLE_TAGS = { }
  815.     RESET_NESTING_TAGS = { }
  816.     QUOTE_TAGS = { }
  817.     MARKUP_MASSAGE = [
  818.         (re.compile('(<[^<>]*)/>'), (lambda x: x.group(1) + ' />')),
  819.         (re.compile('<!\\s+([^<>]*)>'), (lambda x: '<!' + x.group(1) + '>'))]
  820.     ROOT_TAG_NAME = u'[document]'
  821.     HTML_ENTITIES = 'html'
  822.     XML_ENTITIES = 'xml'
  823.     ALL_ENTITIES = [
  824.         HTML_ENTITIES,
  825.         XML_ENTITIES]
  826.     
  827.     def __init__(self, markup = '', parseOnlyThese = None, fromEncoding = None, markupMassage = True, smartQuotesTo = XML_ENTITIES, convertEntities = None, selfClosingTags = None):
  828.         self.parseOnlyThese = parseOnlyThese
  829.         self.fromEncoding = fromEncoding
  830.         self.smartQuotesTo = smartQuotesTo
  831.         if convertEntities:
  832.             self.smartQuotesTo = None
  833.         
  834.         if isList(convertEntities):
  835.             self.convertHTMLEntities = self.HTML_ENTITIES in convertEntities
  836.             self.convertXMLEntities = self.XML_ENTITIES in convertEntities
  837.         else:
  838.             self.convertHTMLEntities = self.HTML_ENTITIES == convertEntities
  839.             self.convertXMLEntities = self.XML_ENTITIES == convertEntities
  840.         self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
  841.         SGMLParser.__init__(self)
  842.         if hasattr(markup, 'read'):
  843.             markup = markup.read()
  844.         
  845.         self.markup = markup
  846.         self.markupMassage = markupMassage
  847.         
  848.         try:
  849.             self._feed()
  850.         except StopParsing:
  851.             pass
  852.  
  853.         self.markup = None
  854.  
  855.     
  856.     def _feed(self, inDocumentEncoding = None):
  857.         markup = self.markup
  858.         if isinstance(markup, unicode):
  859.             if not hasattr(self, 'originalEncoding'):
  860.                 self.originalEncoding = None
  861.             
  862.         else:
  863.             dammit = UnicodeDammit(markup, [
  864.                 self.fromEncoding,
  865.                 inDocumentEncoding], smartQuotesTo = self.smartQuotesTo)
  866.             markup = dammit.unicode
  867.             self.originalEncoding = dammit.originalEncoding
  868.         if markup:
  869.             if self.markupMassage:
  870.                 if not isList(self.markupMassage):
  871.                     self.markupMassage = self.MARKUP_MASSAGE
  872.                 
  873.                 for fix, m in self.markupMassage:
  874.                     markup = fix.sub(m, markup)
  875.                 
  876.             
  877.         
  878.         self.reset()
  879.         if not markup:
  880.             pass
  881.         SGMLParser.feed(self, '')
  882.         SGMLParser.close(self)
  883.         self.endData()
  884.         while self.currentTag.name != self.ROOT_TAG_NAME:
  885.             self.popTag()
  886.  
  887.     
  888.     def __getattr__(self, methodName):
  889.         if methodName.find('start_') == 0 and methodName.find('end_') == 0 or methodName.find('do_') == 0:
  890.             return SGMLParser.__getattr__(self, methodName)
  891.         if methodName.find('__') != 0:
  892.             return Tag.__getattr__(self, methodName)
  893.         raise AttributeError
  894.  
  895.     
  896.     def isSelfClosingTag(self, name):
  897.         if not self.SELF_CLOSING_TAGS.has_key(name):
  898.             pass
  899.         return self.instanceSelfClosingTags.has_key(name)
  900.  
  901.     
  902.     def reset(self):
  903.         Tag.__init__(self, self, self.ROOT_TAG_NAME)
  904.         self.hidden = 1
  905.         SGMLParser.reset(self)
  906.         self.currentData = []
  907.         self.currentTag = None
  908.         self.tagStack = []
  909.         self.quoteStack = []
  910.         self.pushTag(self)
  911.  
  912.     
  913.     def popTag(self):
  914.         tag = self.tagStack.pop()
  915.         if len(self.currentTag.contents) == 1 and isinstance(self.currentTag.contents[0], NavigableString):
  916.             self.currentTag.string = self.currentTag.contents[0]
  917.         
  918.         if self.tagStack:
  919.             self.currentTag = self.tagStack[-1]
  920.         
  921.         return self.currentTag
  922.  
  923.     
  924.     def pushTag(self, tag):
  925.         if self.currentTag:
  926.             self.currentTag.append(tag)
  927.         
  928.         self.tagStack.append(tag)
  929.         self.currentTag = self.tagStack[-1]
  930.  
  931.     
  932.     def endData(self, containerClass = NavigableString):
  933.         if self.currentData:
  934.             currentData = ''.join(self.currentData)
  935.             if currentData.endswith('<') and self.convertHTMLEntities:
  936.                 currentData = currentData[:-1] + '<'
  937.             
  938.             if not currentData.strip():
  939.                 if '\n' in currentData:
  940.                     currentData = '\n'
  941.                 else:
  942.                     currentData = ' '
  943.             
  944.             self.currentData = []
  945.             if self.parseOnlyThese and len(self.tagStack) <= 1:
  946.                 if not (self.parseOnlyThese.text) or not self.parseOnlyThese.search(currentData):
  947.                     return None
  948.                 o = containerClass(currentData)
  949.                 o.setup(self.currentTag, self.previous)
  950.                 if self.previous:
  951.                     self.previous.next = o
  952.                 
  953.             self.previous = o
  954.             self.currentTag.contents.append(o)
  955.         
  956.  
  957.     
  958.     def _popToTag(self, name, inclusivePop = True):
  959.         if name == self.ROOT_TAG_NAME:
  960.             return None
  961.         numPops = 0
  962.         mostRecentTag = None
  963.         for i in range(len(self.tagStack) - 1, 0, -1):
  964.             if name == self.tagStack[i].name:
  965.                 numPops = len(self.tagStack) - i
  966.                 break
  967.                 continue
  968.             name == self.ROOT_TAG_NAME
  969.         
  970.         if not inclusivePop:
  971.             numPops = numPops - 1
  972.         
  973.         for i in range(0, numPops):
  974.             mostRecentTag = self.popTag()
  975.         
  976.         return mostRecentTag
  977.  
  978.     
  979.     def _smartPop(self, name):
  980.         nestingResetTriggers = self.NESTABLE_TAGS.get(name)
  981.         isNestable = nestingResetTriggers != None
  982.         isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
  983.         popTo = None
  984.         inclusive = True
  985.         for i in range(len(self.tagStack) - 1, 0, -1):
  986.             p = self.tagStack[i]
  987.             if (not p or p.name == name) and not isNestable:
  988.                 popTo = name
  989.                 break
  990.             
  991.             if (nestingResetTriggers != None or p.name in nestingResetTriggers or nestingResetTriggers == None) and isResetNesting and self.RESET_NESTING_TAGS.has_key(p.name):
  992.                 popTo = p.name
  993.                 inclusive = False
  994.                 break
  995.             
  996.             p = p.parent
  997.         
  998.         if popTo:
  999.             self._popToTag(popTo, inclusive)
  1000.         
  1001.  
  1002.     
  1003.     def unknown_starttag(self, name, attrs, selfClosing = 0):
  1004.         if self.quoteStack:
  1005.             attrs = ''.join(map((lambda .0: (x, y) = .0' %s="%s"' % (x, y)), attrs))
  1006.             self.currentData.append('<%s%s>' % (name, attrs))
  1007.             return None
  1008.         self.endData()
  1009.         if not self.isSelfClosingTag(name) and not selfClosing:
  1010.             self._smartPop(name)
  1011.         
  1012.         if self.parseOnlyThese and len(self.tagStack) <= 1:
  1013.             if self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs):
  1014.                 return None
  1015.             tag = Tag(self, name, attrs, self.currentTag, self.previous)
  1016.             if self.previous:
  1017.                 self.previous.next = tag
  1018.             
  1019.         self.previous = tag
  1020.         self.pushTag(tag)
  1021.         if selfClosing or self.isSelfClosingTag(name):
  1022.             self.popTag()
  1023.         
  1024.         if name in self.QUOTE_TAGS:
  1025.             self.quoteStack.append(name)
  1026.             self.literal = 1
  1027.         
  1028.         return tag
  1029.  
  1030.     
  1031.     def unknown_endtag(self, name):
  1032.         if self.quoteStack and self.quoteStack[-1] != name:
  1033.             self.currentData.append('</%s>' % name)
  1034.             return None
  1035.         self.endData()
  1036.         self._popToTag(name)
  1037.         if self.quoteStack and self.quoteStack[-1] == name:
  1038.             self.quoteStack.pop()
  1039.             self.literal = len(self.quoteStack) > 0
  1040.         
  1041.  
  1042.     
  1043.     def handle_data(self, data):
  1044.         if self.convertHTMLEntities and data:
  1045.             if data[0] == '&':
  1046.                 data = self.BARE_AMPERSAND.sub('&', data)
  1047.             else:
  1048.                 data = data.replace('&', '&').replace('<', '<').replace('>', '>')
  1049.         
  1050.         self.currentData.append(data)
  1051.  
  1052.     
  1053.     def _toStringSubclass(self, text, subclass):
  1054.         self.endData()
  1055.         self.handle_data(text)
  1056.         self.endData(subclass)
  1057.  
  1058.     
  1059.     def handle_pi(self, text):
  1060.         if text[:3] == 'xml':
  1061.             text = "xml version='1.0' encoding='%SOUP-ENCODING%'"
  1062.         
  1063.         self._toStringSubclass(text, ProcessingInstruction)
  1064.  
  1065.     
  1066.     def handle_comment(self, text):
  1067.         self._toStringSubclass(text, Comment)
  1068.  
  1069.     
  1070.     def handle_charref(self, ref):
  1071.         if ref[0] == 'x':
  1072.             data = unichr(int(ref[1:], 16))
  1073.         else:
  1074.             
  1075.             try:
  1076.                 data = unichr(int(ref))
  1077.             except ValueError:
  1078.                 data = unichr(int(ref, 16))
  1079.  
  1080.         if data <= data:
  1081.             pass
  1082.         elif data <= u'┬ƒ':
  1083.             data = UnicodeDammit.subMSChar(chr(ord(data)), self.smartQuotesTo)
  1084.         elif not (self.convertHTMLEntities) and not (self.convertXMLEntities):
  1085.             data = '&#%s;' % ref
  1086.         
  1087.         self.handle_data(data)
  1088.  
  1089.     
  1090.     def handle_entityref(self, ref):
  1091.         if self.convertXMLEntities:
  1092.             pass
  1093.         replaceWithXMLEntity = self.XML_ENTITIES_TO_CHARS.has_key(ref)
  1094.         if self.convertHTMLEntities or replaceWithXMLEntity:
  1095.             
  1096.             try:
  1097.                 data = unichr(name2codepoint[ref])
  1098.             except KeyError:
  1099.                 if replaceWithXMLEntity:
  1100.                     data = self.XML_ENTITIES_TO_CHARS.get(ref)
  1101.                 else:
  1102.                     data = '&%s' % ref
  1103.             except:
  1104.                 replaceWithXMLEntity
  1105.             
  1106.  
  1107.         None<EXCEPTION MATCH>KeyError
  1108.         data = '&%s;' % ref
  1109.         self.handle_data(data)
  1110.  
  1111.     
  1112.     def handle_decl(self, data):
  1113.         self._toStringSubclass(data, Declaration)
  1114.  
  1115.     
  1116.     def parse_declaration(self, i):
  1117.         j = None
  1118.         if self.rawdata[i:i + 9] == '<![CDATA[':
  1119.             k = self.rawdata.find(']]>', i)
  1120.             if k == -1:
  1121.                 k = len(self.rawdata)
  1122.             
  1123.             data = self.rawdata[i + 9:k]
  1124.             j = k + 3
  1125.             self._toStringSubclass(data, CData)
  1126.         else:
  1127.             
  1128.             try:
  1129.                 j = SGMLParser.parse_declaration(self, i)
  1130.             except SGMLParseError:
  1131.                 toHandle = self.rawdata[i:]
  1132.                 self.handle_data(toHandle)
  1133.                 j = i + len(toHandle)
  1134.  
  1135.         return j
  1136.  
  1137.  
  1138.  
  1139. class BeautifulSoup(BeautifulStoneSoup):
  1140.     
  1141.     def __init__(self, *args, **kwargs):
  1142.         if not kwargs.has_key('smartQuotesTo'):
  1143.             kwargs['smartQuotesTo'] = self.HTML_ENTITIES
  1144.         
  1145.         BeautifulStoneSoup.__init__(self, *args, **kwargs)
  1146.  
  1147.     SELF_CLOSING_TAGS = buildTagMap(None, [
  1148.         'br',
  1149.         'hr',
  1150.         'input',
  1151.         'img',
  1152.         'meta',
  1153.         'spacer',
  1154.         'link',
  1155.         'frame',
  1156.         'base'])
  1157.     QUOTE_TAGS = {
  1158.         'script': None }
  1159.     NESTABLE_INLINE_TAGS = [
  1160.         'span',
  1161.         'font',
  1162.         'q',
  1163.         'object',
  1164.         'bdo',
  1165.         'sub',
  1166.         'sup',
  1167.         'center']
  1168.     NESTABLE_BLOCK_TAGS = [
  1169.         'blockquote',
  1170.         'div',
  1171.         'fieldset',
  1172.         'ins',
  1173.         'del']
  1174.     NESTABLE_LIST_TAGS = {
  1175.         'ol': [],
  1176.         'ul': [],
  1177.         'li': [
  1178.             'ul',
  1179.             'ol'],
  1180.         'dl': [],
  1181.         'dd': [
  1182.             'dl'],
  1183.         'dt': [
  1184.             'dl'] }
  1185.     NESTABLE_TABLE_TAGS = {
  1186.         'table': [],
  1187.         'tr': [
  1188.             'table',
  1189.             'tbody',
  1190.             'tfoot',
  1191.             'thead'],
  1192.         'td': [
  1193.             'tr'],
  1194.         'th': [
  1195.             'tr'],
  1196.         'thead': [
  1197.             'table'],
  1198.         'tbody': [
  1199.             'table'],
  1200.         'tfoot': [
  1201.             'table'] }
  1202.     NON_NESTABLE_BLOCK_TAGS = [
  1203.         'address',
  1204.         'form',
  1205.         'p',
  1206.         'pre']
  1207.     RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', NON_NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1208.     NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1209.     CHARSET_RE = re.compile('((^|;)\\s*charset=)([^;]*)')
  1210.     
  1211.     def start_meta(self, attrs):
  1212.         httpEquiv = None
  1213.         contentType = None
  1214.         contentTypeIndex = None
  1215.         tagNeedsEncodingSubstitution = False
  1216.         for i in range(0, len(attrs)):
  1217.             (key, value) = attrs[i]
  1218.             key = key.lower()
  1219.             if key == 'http-equiv':
  1220.                 httpEquiv = value
  1221.                 continue
  1222.             if key == 'content':
  1223.                 contentType = value
  1224.                 contentTypeIndex = i
  1225.                 continue
  1226.         
  1227.         if httpEquiv and contentType:
  1228.             match = self.CHARSET_RE.search(contentType)
  1229.             if match:
  1230.                 if getattr(self, 'declaredHTMLEncoding') or self.originalEncoding == self.fromEncoding:
  1231.                     newAttr = self.CHARSET_RE.sub((lambda match: match.group(1) + '%SOUP-ENCODING%'), value)
  1232.                     attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], newAttr)
  1233.                     tagNeedsEncodingSubstitution = True
  1234.                 else:
  1235.                     newCharset = match.group(3)
  1236.                     if newCharset and newCharset != self.originalEncoding:
  1237.                         self.declaredHTMLEncoding = newCharset
  1238.                         self._feed(self.declaredHTMLEncoding)
  1239.                         raise StopParsing
  1240.                     newCharset != self.originalEncoding
  1241.             
  1242.         
  1243.         tag = self.unknown_starttag('meta', attrs)
  1244.         if tag and tagNeedsEncodingSubstitution:
  1245.             tag.containsSubstitutions = True
  1246.         
  1247.  
  1248.  
  1249.  
  1250. class StopParsing(Exception):
  1251.     pass
  1252.  
  1253.  
  1254. class ICantBelieveItsBeautifulSoup(BeautifulSoup):
  1255.     I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = [
  1256.         'em',
  1257.         'big',
  1258.         'i',
  1259.         'small',
  1260.         'tt',
  1261.         'abbr',
  1262.         'acronym',
  1263.         'strong',
  1264.         'cite',
  1265.         'code',
  1266.         'dfn',
  1267.         'kbd',
  1268.         'samp',
  1269.         'strong',
  1270.         'var',
  1271.         'b',
  1272.         'big']
  1273.     I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = [
  1274.         'noscript']
  1275.     NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
  1276.  
  1277.  
  1278. class MinimalSoup(BeautifulSoup):
  1279.     RESET_NESTING_TAGS = buildTagMap('noscript')
  1280.     NESTABLE_TAGS = { }
  1281.  
  1282.  
  1283. class BeautifulSOAP(BeautifulStoneSoup):
  1284.     
  1285.     def popTag(self):
  1286.         if len(self.tagStack) > 1:
  1287.             tag = self.tagStack[-1]
  1288.             parent = self.tagStack[-2]
  1289.             parent._getAttrMap()
  1290.             if isinstance(tag, Tag) and len(tag.contents) == 1 and isinstance(tag.contents[0], NavigableString) and not parent.attrMap.has_key(tag.name):
  1291.                 parent[tag.name] = tag.contents[0]
  1292.             
  1293.         
  1294.         BeautifulStoneSoup.popTag(self)
  1295.  
  1296.  
  1297.  
  1298. class BeautifulClosedSoup(BeautifulSoup):
  1299.     
  1300.     def isSelfClosingTag(self, name):
  1301.         return True
  1302.  
  1303.  
  1304.  
  1305. class RobustXMLParser(BeautifulStoneSoup):
  1306.     pass
  1307.  
  1308.  
  1309. class RobustHTMLParser(BeautifulSoup):
  1310.     pass
  1311.  
  1312.  
  1313. class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
  1314.     pass
  1315.  
  1316.  
  1317. class RobustInsanelyWackAssHTMLParser(MinimalSoup):
  1318.     pass
  1319.  
  1320.  
  1321. class SimplifyingSOAPParser(BeautifulSOAP):
  1322.     pass
  1323.  
  1324.  
  1325. try:
  1326.     import chardet
  1327. except:
  1328.     chardet = None
  1329.  
  1330. chardet = None
  1331.  
  1332. try:
  1333.     import cjkcodecs.aliases as cjkcodecs
  1334. except:
  1335.     pass
  1336.  
  1337.  
  1338. try:
  1339.     import iconv_codec
  1340. except:
  1341.     pass
  1342.  
  1343.  
  1344. class UnicodeDammit:
  1345.     CHARSET_ALIASES = {
  1346.         'macintosh': 'mac-roman',
  1347.         'x-sjis': 'shift-jis' }
  1348.     
  1349.     def __init__(self, markup, overrideEncodings = [], smartQuotesTo = 'xml'):
  1350.         (self.markup, documentEncoding, sniffedEncoding) = self._detectEncoding(markup)
  1351.         self.smartQuotesTo = smartQuotesTo
  1352.         self.triedEncodings = []
  1353.         if isinstance(markup, unicode):
  1354.             return markup
  1355.         u = None
  1356.         for proposedEncoding in overrideEncodings:
  1357.             u = self._convertFrom(proposedEncoding)
  1358.             if u:
  1359.                 break
  1360.                 continue
  1361.             isinstance(markup, unicode)
  1362.         
  1363.         if not u:
  1364.             for proposedEncoding in (documentEncoding, sniffedEncoding):
  1365.                 u = self._convertFrom(proposedEncoding)
  1366.                 if u:
  1367.                     break
  1368.                     continue
  1369.             
  1370.         
  1371.         if not u and chardet and not isinstance(self.markup, unicode):
  1372.             u = self._convertFrom(chardet.detect(self.markup)['encoding'])
  1373.         
  1374.         if not u:
  1375.             for proposed_encoding in ('utf-8', 'windows-1252'):
  1376.                 u = self._convertFrom(proposed_encoding)
  1377.                 if u:
  1378.                     break
  1379.                     continue
  1380.             
  1381.         
  1382.         self.unicode = u
  1383.         if not u:
  1384.             self.originalEncoding = None
  1385.         
  1386.  
  1387.     
  1388.     def subMSChar(orig, smartQuotesTo):
  1389.         sub = UnicodeDammit.MS_CHARS.get(orig)
  1390.         if type(sub) == types.TupleType:
  1391.             if smartQuotesTo == 'xml':
  1392.                 sub = '&#x%s;' % sub[1]
  1393.             elif smartQuotesTo == 'html':
  1394.                 sub = '&%s;' % sub[0]
  1395.             else:
  1396.                 sub = unichr(int(sub[1], 16))
  1397.         
  1398.         return sub
  1399.  
  1400.     subMSChar = staticmethod(subMSChar)
  1401.     
  1402.     def _convertFrom(self, proposed):
  1403.         proposed = self.find_codec(proposed)
  1404.         if not proposed or proposed in self.triedEncodings:
  1405.             return None
  1406.         self.triedEncodings.append(proposed)
  1407.         markup = self.markup
  1408.         if self.smartQuotesTo and proposed in ('windows-1252', 'ISO-8859-1', 'ISO-8859-2'):
  1409.             markup = (re.compile('([\x80-\x9f])').sub,)((lambda x: self.subMSChar(x.group(1), self.smartQuotesTo)), markup)
  1410.         
  1411.         
  1412.         try:
  1413.             u = self._toUnicode(markup, proposed)
  1414.             self.markup = u
  1415.             self.originalEncoding = proposed
  1416.         except Exception:
  1417.             e = None
  1418.             return None
  1419.  
  1420.         return self.markup
  1421.  
  1422.     
  1423.     def _toUnicode(self, data, encoding):
  1424.         if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  1425.             encoding = 'utf-16be'
  1426.             data = data[2:]
  1427.         elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  1428.             encoding = 'utf-16le'
  1429.             data = data[2:]
  1430.         elif data[:3] == '\xef\xbb\xbf':
  1431.             encoding = 'utf-8'
  1432.             data = data[3:]
  1433.         elif data[:4] == '\x00\x00\xfe\xff':
  1434.             encoding = 'utf-32be'
  1435.             data = data[4:]
  1436.         elif data[:4] == '\xff\xfe\x00\x00':
  1437.             encoding = 'utf-32le'
  1438.             data = data[4:]
  1439.         
  1440.         newdata = unicode(data, encoding)
  1441.         return newdata
  1442.  
  1443.     
  1444.     def _detectEncoding(self, xml_data):
  1445.         xml_encoding = None
  1446.         sniffed_xml_encoding = None
  1447.         
  1448.         try:
  1449.             if xml_data[:4] == 'Lo\xa7\x94':
  1450.                 xml_data = self._ebcdic_to_ascii(xml_data)
  1451.             elif xml_data[:4] == '\x00<\x00?':
  1452.                 sniffed_xml_encoding = 'utf-16be'
  1453.                 xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
  1454.             elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  1455.                 sniffed_xml_encoding = 'utf-16be'
  1456.                 xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
  1457.             elif xml_data[:4] == '<\x00?\x00':
  1458.                 sniffed_xml_encoding = 'utf-16le'
  1459.                 xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
  1460.             elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  1461.                 sniffed_xml_encoding = 'utf-16le'
  1462.                 xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
  1463.             elif xml_data[:4] == '\x00\x00\x00<':
  1464.                 sniffed_xml_encoding = 'utf-32be'
  1465.                 xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
  1466.             elif xml_data[:4] == '<\x00\x00\x00':
  1467.                 sniffed_xml_encoding = 'utf-32le'
  1468.                 xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
  1469.             elif xml_data[:4] == '\x00\x00\xfe\xff':
  1470.                 sniffed_xml_encoding = 'utf-32be'
  1471.                 xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
  1472.             elif xml_data[:4] == '\xff\xfe\x00\x00':
  1473.                 sniffed_xml_encoding = 'utf-32le'
  1474.                 xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
  1475.             elif xml_data[:3] == '\xef\xbb\xbf':
  1476.                 sniffed_xml_encoding = 'utf-8'
  1477.                 xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
  1478.             else:
  1479.                 sniffed_xml_encoding = 'ascii'
  1480.             xml_encoding_match = re.compile('^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>').match(xml_data)
  1481.         except:
  1482.             xml_encoding_match = None
  1483.  
  1484.         if xml_encoding_match:
  1485.             xml_encoding = xml_encoding_match.groups()[0].lower()
  1486.             if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  1487.                 xml_encoding = sniffed_xml_encoding
  1488.             
  1489.         
  1490.         return (xml_data, xml_encoding, sniffed_xml_encoding)
  1491.  
  1492.     
  1493.     def find_codec(self, charset):
  1494.         if not self._codec(self.CHARSET_ALIASES.get(charset, charset)):
  1495.             if not charset or self._codec(charset.replace('-', '')):
  1496.                 if not charset or self._codec(charset.replace('-', '_')):
  1497.                     pass
  1498.         return charset
  1499.  
  1500.     
  1501.     def _codec(self, charset):
  1502.         if not charset:
  1503.             return charset
  1504.         codec = None
  1505.         
  1506.         try:
  1507.             codecs.lookup(charset)
  1508.             codec = charset
  1509.         except LookupError:
  1510.             charset
  1511.             charset
  1512.         except:
  1513.             charset
  1514.  
  1515.         return codec
  1516.  
  1517.     EBCDIC_TO_ASCII_MAP = None
  1518.     
  1519.     def _ebcdic_to_ascii(self, s):
  1520.         c = self.__class__
  1521.         if not c.EBCDIC_TO_ASCII_MAP:
  1522.             emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  1523.             import string
  1524.             c.EBCDIC_TO_ASCII_MAP = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  1525.         
  1526.         return s.translate(c.EBCDIC_TO_ASCII_MAP)
  1527.  
  1528.     MS_CHARS = {
  1529.         '\x80': ('euro', '20AC'),
  1530.         '\x81': ' ',
  1531.         '\x82': ('sbquo', '201A'),
  1532.         '\x83': ('fnof', '192'),
  1533.         '\x84': ('bdquo', '201E'),
  1534.         '\x85': ('hellip', '2026'),
  1535.         '\x86': ('dagger', '2020'),
  1536.         '\x87': ('Dagger', '2021'),
  1537.         '\x88': ('circ', '2C6'),
  1538.         '\x89': ('permil', '2030'),
  1539.         '\x8a': ('Scaron', '160'),
  1540.         '\x8b': ('lsaquo', '2039'),
  1541.         '\x8c': ('OElig', '152'),
  1542.         '\x8d': '?',
  1543.         '\x8e': ('#x17D', '17D'),
  1544.         '\x8f': '?',
  1545.         '\x90': '?',
  1546.         '\x91': ('lsquo', '2018'),
  1547.         '\x92': ('rsquo', '2019'),
  1548.         '\x93': ('ldquo', '201C'),
  1549.         '\x94': ('rdquo', '201D'),
  1550.         '\x95': ('bull', '2022'),
  1551.         '\x96': ('ndash', '2013'),
  1552.         '\x97': ('mdash', '2014'),
  1553.         '\x98': ('tilde', '2DC'),
  1554.         '\x99': ('trade', '2122'),
  1555.         '\x9a': ('scaron', '161'),
  1556.         '\x9b': ('rsaquo', '203A'),
  1557.         '\x9c': ('oelig', '153'),
  1558.         '\x9d': '?',
  1559.         '\x9e': ('#x17E', '17E'),
  1560.         '\x9f': ('Yuml', '178') }
  1561.  
  1562.  
  1563. try:
  1564.     import psyco
  1565. except ImportError:
  1566.     pass
  1567.  
  1568. for obj in [
  1569.     PageElement,
  1570.     NavigableString,
  1571.     CData,
  1572.     ProcessingInstruction,
  1573.     Comment,
  1574.     Declaration,
  1575.     Tag,
  1576.     SoupStrainer,
  1577.     ResultSet,
  1578.     BeautifulStoneSoup,
  1579.     BeautifulSoup,
  1580.     BeautifulSOAP,
  1581.     UnicodeDammit]:
  1582.     psyco.bind(obj)
  1583.  
  1584. if __name__ == '__main__':
  1585.     import sys
  1586.     soup = BeautifulSoup(sys.stdin.read())
  1587.     print soup.prettify()
  1588.  
  1589.