User Tools

Site Tools


python:twistedlxmlandre

Twisted, lxml and re package

Twisted Package

Twisted is an event-driven networking engine written in Python and licensed under the open source:

  • As a platform, Twisted should be focused on integration. Ideally, all functionality will be accessible through all protocols. Failing that, all functionality should be configurable through at least one protocol, with a seamless and consistent user-interface.
  • The next phase of development will be focusing strongly on a configuration system which will unify many disparate pieces of the current infrastructure, and allow them to be tacked together by a non-programmer.

Refer:

reactor: core of event loop

The event loop at the core of your program.

Som basic functions in core reactor twisted/internet/base.py:

  • class reactor init
    class ReactorBase(object):
        def __init__(self):
            self.threadCallQueue = []
            self._eventTriggers = {}
            self._pendingTimedCalls = []
            self._newTimedCalls = []
            self._cancellations = 0
            self.running = False
            self._started = False
            self._justStopped = False
            self._startedBefore = False
            # reactor internal readers, e.g. the waker.
            self._internalReaders = set()
            self.waker = None
     
            # Arrange for the running attribute to change to True at the right time
            # and let a subclass possibly do other things at that time (eg install
            # signal handlers).
            self.addSystemEventTrigger(
                'during', 'startup', self._reallyStartRunning)
            self.addSystemEventTrigger('during', 'shutdown', self.crash)
            self.addSystemEventTrigger('during', 'shutdown', self.disconnectAll)
     
            if platform.supportsThreads():
                self._initThreads()
            self.installWaker()
  • class reactor run: Fire 'startup' System Events, move the reactor to the 'running' state, then run the main loop until it is stopped with stop() or crash()
    @implementer(IReactorCore, IReactorTime, IReactorPluggableResolver)
    class ReactorBase(object):
        def fireSystemEvent(self, eventType):
            """See twisted.internet.interfaces.IReactorCore.fireSystemEvent.
            """
            event = self._eventTriggers.get(eventType)
            if event is not None:
                event.fireEvent()                
        def startRunning(self):
            if self._started:
                raise error.ReactorAlreadyRunning()
            if self._startedBefore:
                raise error.ReactorNotRestartable()
            self._started = True
            self._stopped = False
            if self._registerAsIOThread:
                threadable.registerAsIOThread()
            self.fireSystemEvent('startup')
     
    class _SignalReactorMixin(object):        
        def run(self, installSignalHandlers=True):
            self.startRunning(installSignalHandlers=installSignalHandlers)
            self.mainLoop()
     
        def mainLoop(self):
            while self._started:
                try:
                    while self._started:
                        # Advance simulation time in delayed event
                        # processors.
                        self.runUntilCurrent()
                        t2 = self.timeout()
                        t = self.running and t2
                        self.doIteration(t)
                except:
                    log.msg("Unexpected error in main loop.")
                    log.err()
                else:
                    log.msg('Main loop terminated.')    
        def runUntilCurrent(self):
            """Run all pending timed calls.
            """
            if self.threadCallQueue:
                # Keep track of how many calls we actually make, as we're
                # making them, in case another call is added to the queue
                # while we're in this loop.
                count = 0
                total = len(self.threadCallQueue)
                for (f, a, kw) in self.threadCallQueue:
                    try:
                        f(*a, **kw)
                    except:
                        log.err()
                    count += 1
                    if count == total:
                        break
                del self.threadCallQueue[:count]
                if self.threadCallQueue:
                    self.wakeUp()
     
            # insert new delayed calls now
            self._insertNewDelayedCalls()
     
            now = self.seconds()
            while self._pendingTimedCalls and (self._pendingTimedCalls[0].time <= now):
                call = heappop(self._pendingTimedCalls)
                if call.cancelled:
                    self._cancellations-=1
                    continue
     
                if call.delayed_time > 0:
                    call.activate_delay()
                    heappush(self._pendingTimedCalls, call)
                    continue
     
                try:
                    call.called = 1
                    call.func(*call.args, **call.kw)
                except:
                    log.deferr()
                    if hasattr(call, "creator"):
                        e = "\n"
                        e += " C: previous exception occurred in " + \
                             "a DelayedCall created here:\n"
                        e += " C:"
                        e += "".join(call.creator).rstrip().replace("\n","\n C:")
                        e += "\n"
                        log.msg(e)
     
     
            if (self._cancellations > 50 and
                 self._cancellations > len(self._pendingTimedCalls) >> 1):
                self._cancellations = 0
                self._pendingTimedCalls = [x for x in self._pendingTimedCalls
                                           if not x.cancelled]
                heapify(self._pendingTimedCalls)
     
            if self._justStopped:
                self._justStopped = False
                self.fireSystemEvent("shutdown")                        
  • class reactor callLater
        def callLater(self, _seconds, _f, *args, **kw):
            """See twisted.internet.interfaces.IReactorTime.callLater.
            """
            assert callable(_f), "%s is not callable" % _f
            assert _seconds >= 0, \
                   "%s is not greater than or equal to 0 seconds" % (_seconds,)
            tple = DelayedCall(self.seconds() + _seconds, _f, args, kw,
                               self._cancelCallLater,
                               self._moveCallLaterSooner,
                               seconds=self.seconds)
            self._newTimedCalls.append(tple)
            return tple

event dispatching: Scheduling and Deferreds

Scheduling

Deferreds

Like callback functions, only a lot better.Twisted’s preferred mechanism for controlling the flow of asynchronous code. We would still need a way of saying “do this only when that has finished”.

Below are some basic function of defer API:

  • defer.py: maybeDeferred
    def succeed(result):
        d = Deferred()
        d.callback(result)
        return d
    def fail(result=None):
        d = Deferred()
        d.errback(result)
        return d
    def maybeDeferred(f, *args, **kw):
        try:
            result = f(*args, **kw)
        except:
            return fail(failure.Failure(captureVars=Deferred.debug))
     
        if isinstance(result, Deferred):
            return result
        elif isinstance(result, failure.Failure):
            return fail(result)
        else:
            return succeed(result)
  • class Defered:addCallback,callback
        def __init__(self, canceller=None):
            self.callbacks = []
            self._canceller = canceller
            if self.debug:
                self._debugInfo = DebugInfo()
                self._debugInfo.creator = traceback.format_stack()[:-1]
     
        def addCallbacks(self, callback, errback=None,
                         callbackArgs=None, callbackKeywords=None,
                         errbackArgs=None, errbackKeywords=None):
            assert callable(callback)
            assert errback == None or callable(errback)
            cbs = ((callback, callbackArgs, callbackKeywords),
                   (errback or (passthru), errbackArgs, errbackKeywords))
            self.callbacks.append(cbs)
     
            if self.called:
                self._runCallbacks()
            return self
        def addCallback(self, callback, *args, **kw):
            return self.addCallbacks(callback, callbackArgs=args,
                                     callbackKeywords=kw)
        def addErrback(self, errback, *args, **kw):
            return self.addCallbacks(passthru, errback,
                                     errbackArgs=args,
                                     errbackKeywords=kw)
        def callback(self, result):
            assert not isinstance(result, Deferred)
            self._startRunCallbacks(result)                                 
  • class Defered: inlineCallbacks(f): helps you write Deferred-using code that looks like a regular sequential function.

communication protocol

TCP servers , TCP clients , UDP networking and Using processes

using Threads

lxml package

The lxml XML toolkit is a Pythonic binding for the C libraries libxml2 and libxslt:

two the most basic classes in lxml packages for parsing xml and html:

Parsing xml and html to Etree Object

refer: http://lxml.de/parsing.html
etree.parse return lxml.etree._ElementTree object

  • Parsing XML
    • XML String
      import StringIO
      from lxml import etree
      f = StringIO.StringIO('<foo><bar></bar></foo>')
      tree = etree.parse(f)
    • XML file
      from lxml import etree
       
      tree = etree.parse("doc/test.xml")
    • Iterate Parsing xml
      import StringIO
      from lxml import etree
      f = StringIO.StringIO('<foo><bar></bar></foo>')    
      context = etree.iterparse(f)
      for action, elem in context:
          print("%s: %s" % (action, elem.tag))
      end: bar
      end: foo
  • Parsing HTML
    • HTML String
      import StringIO
      from lxml import etree
      from lxml.html import HTMLParser
       
      broken_html = "<html><head><title>test<body><h1>page title</h3>"
      parser = HTMLParser()
      tree   = etree.parse(StringIO.StringIO(broken_html), parser)
      result = etree.tostring(tree.getroot(), pretty_print=True, method="html")
      print(result)
    • HTML file
      from lxml import etree
      from lxml.html import HTMLParser
       
      parser = HTMLParser()
      tree   = etree.parse('index.html', parser)
    • Iterate Parsing HTML
      import StringIO
      from lxml import etree
       
      with open('index.html', 'r') as f:
          htmlcontent = f.read()
          context = etree.iterparse(StringIO.StringIO(htmlcontent), html = True)
          for action, elem in context:
              print("%s: %s" % (action, elem.tag))

XPath for xml and html

XPath Syntax

refer: http://www.w3schools.com/XPath/xpath_syntax.asp

XPath Expressions:

  • Selecting Nodes
  • Predicates: Predicates are used to find a specific node or a node that contains a specific value
  • Selecting Unknown Nodes
  • Selecting Several Paths

XPath function return:

  • XPath() function return value are array of HTMLElement objects
  • If you add string option in XPath function //text(), XPath will return value are array of String Objects

Examples

  • Simple Example for xml
    import StringIO
    from lxml import etree
    f = StringIO.StringIO('<foo><bar></bar></foo>')
    tree = etree.parse(f)
    r = tree.xpath('/foo/bar')
    print r[0].tag
    len(r)
    r = tree.xpath('bar')
    r[0].tag
    print r[0].tag

    ⇒output:

    bar
    bar
  • xpath with html
    • Information of tag which you want parser:
      <div title="buyer-name">Carson Busses</div>
      <span class="item-price">$29.95</span>
    • code parser
      from lxml import html
      import requests
       
      page = requests.get('http://econpy.pythonanywhere.com/ex/001.html')
      tree = html.fromstring(page.text)
      #This will create a list of buyers:
      buyers = tree.xpath('//div[@title="buyer-name"]/text()')
      #This will create a list of prices
      prices = tree.xpath('//span[@class="item-price"]/text()')
       
      print 'Buyers: ', buyers
      print 'Prices: ', prices

      ⇒output:

      Buyers:  ['Carson Busses', 'Earl E. Byrd', 'Patty Cakes', 'Derri Anne Connecticut', 'Moe Dess', 'Leda Doggslife', 'Dan Druff', 'Al Fresco', 'Ido Hoe', 'Howie Kisses', 'nt', 'Ben D. Rules', 'Ave Sectomy', 'Gary Shattire', 'Bobbi Soks', 'Sheila Takya', 'Rose Tattoo', 'Moe Tell']
      Prices:  ['$29.95', '$8.37', '$15.26', '$19.25', '$19.25', '$13.99', '$31.57', '$8.49', '$14.47', '$15.86', '$11.11', '$15.98', '$16.27', '$7.50', '$50.85', '$14.26', '
      0.09']

lxml.html

HTMLParser

  • parser element with basic HTMLParser
    from HTMLParser import HTMLParser
     
    # create a subclass and override the handler methods
    class MyHTMLParser(HTMLParser):
        def handle_starttag(self, tag, attrs):
            print "Encountered a start tag:", tag
        def handle_endtag(self, tag):
            print "Encountered an end tag :", tag
        def handle_data(self, data):
            print "Encountered some data  :", data
     
    # instantiate the parser and fed it some HTML
    parser = MyHTMLParser()
    parser.feed('''<html><head><title>Test</title></head>
                <body><h1>Parse me!</h1></body></html>''')

    output:

    Encountered a start tag: html
    Encountered a start tag: head
    Encountered a start tag: title
    Encountered some data  : Test
    Encountered an end tag : title
    Encountered an end tag : head
    Encountered some data  :
    
    Encountered a start tag: body
    Encountered a start tag: h1
    Encountered some data  : Parse me!
    Encountered an end tag : h1
    Encountered an end tag : body
    Encountered an end tag : html

Create tree or root Element with lxml.html

refer: http://lxml.de/lxmlhtml.html

  • parse html from request:
    from lxml import html, etree
    from lxml.html import HTMLParser
    import StringIO
    import requests
     
    tree = html.parse('http://www.google.com')
    tree.write('index.html')

    Or

    from lxml import html
    import requests
     
    page = requests.get('http://www.google.com')
    tree = html.fromstring(page.text)
    r = tree.xpath('//title');
    print r[0].text
  • parse html from local file
    from lxml import html as HTML
     
    tree = HTML.parse('index.html')
    r = tree.xpath('//title');
    print r[0].tag
    print r[0].text
  • parser html with HTMLParser:
    import StringIO
    from lxml import etree
    from lxml.html import HTMLParser
     
    broken_html = "<html><head><title>test<body><h1>page title</h3>"
    parser = HTMLParser()
    tree   = etree.parse(StringIO.StringIO(broken_html), parser)
    result = etree.tostring(tree.getroot(), pretty_print=True, method="html")
    print(result)

Build xml using Etree

  • Build xml using xml.etree.ElementTree:
    from xml.etree import ElementTree as ET
    '''
    <?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank>1</rank>
            <year>2008</year>
        </country>
        <country name="Singapore">
            <rank>4</rank>
            <year>2011</year>
        </country>
    </data>
    '''
    data = ET.Element('data')
     
    country1 = ET.SubElement(data, 'country', {'name':'Liechtenstein'})
    rank1 = ET.SubElement(country1, 'rank')
    rank1.text = '1'
    year1 = ET.SubElement(country1, 'year')
    year1.text = '2008'
     
    country2 = ET.SubElement(data, 'country', {'name':'Singapore'})
    rank2 = ET.SubElement(country2, 'rank')
    rank2.text = '4'
    year2 = ET.SubElement(country2, 'year')
    year2.text = '2011'
    print ET.tostring(data)

    output:

    <data><country name="Liechtenstein"><rank>1</rank><year>2008</year></country><country name="Singapore"><rank>4</rank><year>2011</year></country></data>
  • Build xml using lxml.etree:
    from lxml import etree as ET
    '''
    <?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank>1</rank>
            <year>2008</year>
        </country>
        <country name="Singapore">
            <rank>4</rank>
            <year>2011</year>
        </country>
    </data>
    '''
    data = ET.Element('data')
     
    country1 = ET.SubElement(data, 'country', {'name':'Liechtenstein'})
    rank1 = ET.SubElement(country1, 'rank')
    rank1.text = '1'
    year1 = ET.SubElement(country1, 'year')
    year1.text = '2008'
     
    country2 = ET.SubElement(data, 'country', {'name':'Singapore'})
    rank2 = ET.SubElement(country2, 'rank')
    rank2.text = '4'
    year2 = ET.SubElement(country2, 'year')
    year2.text = '2011'
    print ET.tostring(data)

    output:

    <data><country name="Liechtenstein"><rank>1</rank><year>2008</year></country><country name="Singapore"><rank>4</rank><year>2011</year></country></data>

Custom Functions

  • Load html from local file
    from lxml.html import HtmlElement
    from lxml import etree
    from lxml import html as HTML
     
    tree = HTML.parse('index.html')
    r = tree.xpath('//div[@id="content"]');
    print(etree.tostring(r[0], pretty_print=True, encoding='utf-8'))
  • Print HTMLElement
    from lxml.html import HtmlElement
    from lxml import etree
    from lxml import html as HTML
     
    tree = HTML.parse('index.html')
    r = tree.xpath('//div[@id="content"]');
    print(etree.tostring(r[0], pretty_print=True, method="html"))
  • Write html from tree
    from lxml import html, etree
    from lxml.html import HTMLParser
    import StringIO
    import requests
     
    page = requests.get('http://shop.babies.vn')
    parser = HTMLParser()
    tree   = etree.parse(StringIO.StringIO(page.text), parser)
    tree.write('index.html', method = 'html')

    Or

    from lxml import html, etree
    from lxml.html import HTMLParser
    import StringIO
    import requests
     
    tree = html.parse('http://shop.babies.vn')
    tree.write('index.html', method = 'html')

re Package(Regular Expression)

To use re package, we need to import it:

import re

Regular Expression Language

A regular expression (abbreviated regex or regexp) is a sequence of characters that forms a search pattern
refer:

Match Character

  • Character Escapes: The backslash character (\) in a regular expression indicates that the character that follows it either is a special character (as shown in the following table), or should be interpreted literally
    • \t Match a tab: pattern (\w+)\t match “item1\t”, “item2\t” in “item1\titem2\t”
    • \n Match a new line
    • \d Matches any decimal digit
    • \D Matches any character other than a decimal digit.
  • Character Classes: A character class matches any one of a set of characters.
    • [character_group] Matches any single character in character_group:pattern [ae] match “a” in “gray” and “a”, “e” in “lane”
    • [^character_group] Negation: Matches any single character that is not in character_group
    • [first-last] Character range: Matches any single character in the range from first to last.
    • \w Matches any word character
    • \W Matches any non-word character.
    • \s Matches any white-space character: Pattern \w\s match “D “ in “ID A1.3”

Anchors: cause a match to succeed or fail depending on the current position in the string

  • ^ The match must start at the beginning of the string or line: pattern ^\d{3} match “901” in “901-333-“
  • $ The match must occur at the end of the string or before \n at the end of the line or string: pattern -\d{3}$ match ”-333” in ”-901-333”
  • \A The match must occur at the start of the string: pattern \A\d{3} match “901” in “901-333-“
  • \Z The match must occur at the end of the string or before \n at the end of the string: pattern -\d{3}\Z match ”-333” in “-901-333”

Grouping constructs: Grouping constructs delineate subexpressions of a regular expression and typically capture substrings of an input string

  • (subexpression) Captures the matched subexpression and assigns it a one-based ordinal number: pattern (\w)\1 match “ee” in “deep”

Quantifier: A quantifier specifies how many instances of the previous element (which can be a character, a group, or a character class) must be present in the input string for a match to occur

  • * Matches the previous element zero or more times: pattern \d*\.\d match “.0”, “19.9”, “219.9”
  • + Matches the previous element one or more times: pattern “be+” match “bee” in “been”, “be” in “bent”
  • ? Matches the previous element zero or one time: pattern “rai?n” match “ran”, “rain”
  • {n} Matches the previous element exactly n times:pattern “,\d{3}“ match ”,043” in “1,043.6”, “,876”, “,543”, and “,210” in “9,876,543,210”
  • {n ,} Matches the previous element at least n times: pattern “\d{2,}“ match “166”, “29”, “1930”
  • {n,m} Matches the previous element at least n times, but no more than m times: pattern “\d{3,5}“ match “166”, “17668” “19302” in “193024”

re Package

refer: http://www.pythonforbeginners.com/regex/regular-expressions-in-python

re Flags:

# flags
I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case
L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale
U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode locale
M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline
S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline
X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments

re.findall

findall: The findall() is probably the single most powerful function in the re module

  1. Example 1:
    str = 'purple [email protected], blah monkey [email protected] blah dishwasher'
     
    ## Here re.findall() returns a list of all the found email strings
    emails = re.findall(r'[\w\.-][email protected][\w\.-]+', str) ## ['[email protected]', '[email protected]']
     
    for email in emails:
        # do something with each found email string
        print email

    Understand pattern syntax above:

  • [\w\.-]+ ⇒ Begin with one or multiple(sign: +) in group(sign: []): word(sign: \w) or character .(sign: \.) or character -
  • @[\w\.-]+ ⇒ next of it is character @ and one or multiple characters in group: [word, . , -]
  1. Example 2:
    # Open file
    f = open('test.txt', 'r')
     
    # Feed the file text into findall(); it returns a list of all the found strings
    strings = re.findall(r'some pattern', f.read())

re.search, re.match

  • re.search: The re.search() method takes a regular expression pattern and a string and searches for that pattern within the string
    str = 'an example word:cat!!'
    match = re.search(r'word:www', str)
    # If-statement after search() tests if it succeeded
      if match:                      
        print 'found', match.group() ## 'found word:cat'
      else:
        print 'did not find'

    As you can see in the example below, I have used the | operator, which search for either pattern I specify.

    import re
    programming = ["Python", "Perl", "PHP", "C++"]
    pat = "^B|^P|i$|H$"
    for lang in programming:
        if re.search(pat,lang,re.IGNORECASE):
            print lang , "FOUND"
        else:
            print lang, "NOT FOUND"

    The output of above script will be:

    Python FOUND
    Perl FOUND
    PHP FOUND
    C++ NOT FOUND
  • re.search and re.match
    import re
     
    text = "The Attila the Hun Show"
     
    # a single character
    m = re.match(".", text)
    if m: print repr("."), "=>", repr(m.group(0))
     
    # any string of characters
    m = re.match(".*", text)
    if m: print repr(".*"), "=>", repr(m.group(0))
     
    # a string of letters (at least one)
    m = re.match("\w+", text)
    if m: print repr("\w+"), "=>", repr(m.group(0))
     
    # a string of digits
    m = re.match("\d+", text)
    if m: print repr("\d+"), "=>", repr(m.group(0))

    output:

    '.' => 'T'
    '.*' => 'The Attila the Hun Show'
    '\\w+' => 'The'
  • re.search and re.match:
    import re
     
    print '**********************************'
    text ="10/15/99"
     
    print "match1:"
    m = re.match("(\d{2})/(\d{2})/(\d{2,4})", text)
    if m:
        print m.group(1, 2, 3)
     
    print "search1:"
    s = re.search("(\d{2})/(\d{2})/(\d{2,4})", text)
    if s:
        print s.group(1, 2, 3)    
     
    print '**********************************'
    text ="hello 10/15/99"
    print "match2:"
    m = re.match("(\d{2})/(\d{2})/(\d{2,4})", text)
    if m:
        print m.group(1, 2, 3)
     
    print "search2:"
    s = re.search("(\d{2})/(\d{2})/(\d{2,4})", text)
    if s:
        print s.group(1, 2, 3)

    output:

    **********************************
    match1:
    ('10', '15', '99')
    search1:
    ('10', '15', '99')
    **********************************
    match2:
    search2:
    ('10', '15', '99')

re.sub and re.compile

  • re.sub: The re.sub() function in the re module can be used to replace substrings
    • first example
      import re
      text = "Python for beginner is a very cool website"
      text2 = re.sub("cool", "good", text)
      print text2

      output

      Python for beginner is a very good website
    • Here is another example (taken from Googles Python class ) which searches for all the email addresses, and changes them to keep the user (1) but have yo-yo-dyne.com as the host.
      str = 'purple [email protected], blah monkey [email protected] blah dishwasher' 
       
      ## re.sub(pat, replacement, str) -- returns new string with all replacements,
       
      ## 1 is group(1), 2 group(2) in the replacement
       
      print re.sub(r'([\w.-]+)@([\w.-]+)', r'[email protected]', str)
      ## purple [email protected], blah monkey [email protected] blah dishwasher

      output:

      purple [email protected], blah monkey [email protected] blah dishwasher
  • re.compile: With the re.compile() function we can compile pattern into pattern objects, which have methods for various operations such as searching for pattern matches or performing string substitutions.
    • The first example checks if the input from the user contains only letters, spaces or . (no digits) Any other character is not allowed.
      import re
       
      name_check = re.compile(r"[^A-Za-zs.]")
      name = raw_input ("Please, enter your name: ")
      while name_check.search(name):
          print "Please enter your name correctly!"
          name = raw_input ("Please, enter your name: ")
    • The second example checks if the input from the user contains only numbers, parentheses, spaces or hyphen (no letters) Any other character is not allowed
      import re
       
      phone_check = re.compile(r"[^0-9s-()]")
      phone = raw_input ("Please, enter your phone: ")
      while phone_check.search(phone):
          print "Please enter your phone correctly!"
          phone = raw_input ("Please, enter your phone: ")

      The output of above script will be:

      Please, enter your phone: s
      Please enter your phone correctly!

      It will continue to ask until you put in numbers only.

re.split

python/twistedlxmlandre.txt · Last modified: 2017/01/12 09:12 (external edit)