{"id":232,"date":"2016-03-15T16:43:43","date_gmt":"2016-03-15T16:43:43","guid":{"rendered":"https:\/\/www.pyxll.com\/blog\/?p=232"},"modified":"2021-04-12T16:23:10","modified_gmt":"2021-04-12T16:23:10","slug":"a-real-time-twitter-feed-in-excel","status":"publish","type":"post","link":"https:\/\/www.pyxll.com\/blog\/a-real-time-twitter-feed-in-excel\/","title":{"rendered":"A Real-Time Python Twitter Feed in Excel"},"content":{"rendered":"\n<p>PyXLL 3.0 introduced a new, simpler, way of streaming real time data to Excel from Python.<\/p>\n\n\n\n<p>Excel has had support for real time data (RTD) for a long time, but it requires a certain knowledge of COM to get it to work. With the new RTD features in PyXLL 3.0 it is now a lot simpler to get streaming data into Excel without having to write any COM code.<\/p>\n\n\n\n<p>This blog will show how to build a simple real time data feed from Twitter in Python using the <a href=\"http:\/\/www.tweepy.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">tweepy<\/a> package, and then show how to stream that data into Excel using PyXLL.<\/p>\n\n\n\n<p>As we are interested in real time data we will use tweepy&#8217;s streaming API. Details on this are available in the tweepy documentation here <a href=\"http:\/\/tweepy.readthedocs.org\/en\/v3.5.0\/streaming_how_to.html\">http:\/\/tweepy.readthedocs.org\/en\/v3.5.0\/streaming_how_to.html<\/a>.<\/p>\n\n\n\n<p>If you don&#8217;t already have tweepy installed, you can install it using pip<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\npip install tweepy\n<\/pre><\/pre>\n\n\n\n<p>The code from this blog is available on github <a href=\"https:\/\/github.com\/pyxll\/pyxll-examples\/tree\/master\/twitter\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/pyxll\/pyxll-examples\/tree\/master\/twitter<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-getting-twitter-api-keys\">Getting Twitter API keys<\/h2>\n\n\n\n<p>In order to access Twitter Streaming API you will need a Twitter API key, API secret, Access token and Access token secret. Follow the steps below to get your own access tokens.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Create a twitter account if you do not already have one.<\/li><li>Go to https:\/\/apps.twitter.com\/ and log in with your twitter credentials.<\/li><li>Click &#8220;Create New App&#8221;.<\/li><li>Fill out the form, agree to the terms, and click &#8220;Create your Twitter application&#8221;<\/li><li>In the next page, click on &#8220;API keys&#8221; tab, and copy your &#8220;API key&#8221; and &#8220;API secret&#8221;.<\/li><li>Scroll down and click &#8220;Create my access token&#8221;, and copy your &#8220;Access token&#8221; and &#8220;Access token secret&#8221;.<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-streaming-tweets-in-python\">Streaming Tweets in Python<\/h2>\n\n\n\n<p>To start with we can create a simple listener class that simply prints tweets as they arrive<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom tweepy.streaming import StreamListener\nfrom tweepy import OAuthHandler\nfrom tweepy import Stream\nimport logging\n\n_log = logging.getLogger(__name__)\n\n# User credentials to access Twitter API\naccess_token = &quot;YOUR ACCESS TOKEN&quot;\naccess_token_secret = &quot;YOUR ACCESS TOKEN SECRET&quot;\nconsumer_key = &quot;YOUR CONSUMER KEY&quot;\nconsumer_secret = &quot;YOUR CONSUMER KEY SECRET&quot;\n\nclass TwitterListener(StreamListener):\n\n    def __init__(self, phrases):\n        auth = OAuthHandler(consumer_key, consumer_secret)\n        auth.set_access_token(access_token, access_token_secret)\n        self.__stream = Stream(auth, listener=self)\n        self.__stream.filter(track=phrases, async=True)\n\n    def disconnect(self):\n        self.__stream.disconnect()\n\n    def on_data(self, data):\n        print(data)\n\n    def on_error(self, status):\n        print(status)\n\nif __name__ == '__main__':\n    import time\n    logging.basicConfig(level=logging.INFO)\n\n    phrases = &#x5B;&quot;python&quot;, &quot;excel&quot;, &quot;pyxll&quot;]\n    listener = TwitterListener(phrases)\n\n    # listen for 60 seconds then stop\n    time.sleep(60)\n    listener.disconnect()\n<\/pre><\/pre>\n\n\n\n<p>If we run this code any tweets mentioning Python, Excel or PyXLL get printed:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\npython twitterxl.py\n\nINFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): stream.twitter.com\n{&quot;text&quot;: &quot;Excel keyboard shortcut - CTRL+1 to bring up Cell Formatting https:\/\/t.co\/wvx634EpUy&quot;, &quot;is...\n{&quot;text&quot;: &quot;Excel Tips - What If Analysis #DMZWorld #Feature #Bond #UMI https:\/\/t.co\/lxzgZnIItu #UMI&quot;,...\n{&quot;text&quot;: &quot;How good are you at using #Excel? We're looking for South Africa's #ExcelChamp Ts &amp;amp;amp;amp;amp;amp;amp; Cs...\n{&quot;text&quot;: &quot;The Best Data Scientists Run R and Python - insideBIGDATA https:\/\/t.co\/rwty058dL2 #python ...\n{&quot;text&quot;: &quot;How to Create a Pivot Table in Excel: A Step-by-Step Tutorial (With Video) \\u2013 https:\/\/...\n{&quot;text&quot;: &quot;Python eats Alligator 02, Time Lapse Speed x6 https:\/\/t.co\/3km8I92zJo&quot;, &quot;is_quote_status&quot;:...\n...\n\nProcess finished with exit code 0\n<\/pre><\/pre>\n\n\n\n<p>In order to make this more suitable for getting these tweets into Excel we will now extend this TwitterListener class in the following ways:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Broadcast updates to other <em>subscribers<\/em> instead of just printing tweets.<\/li><li>Keep a buffer of the last few received tweets.<\/li><li>Only ever create one listener for each unique set of phrases.<\/li><li>Automatically disconnect listeners with no subscribers.<\/li><\/ul>\n\n\n\n<p>The updated TwitterListener class is as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nclass TwitterListener(StreamListener):\n    &quot;&quot;&quot;tweepy.StreamListener that notifies multiple subscribers when\n    new tweets are received and keeps a buffer of the last 100 tweets\n    received.\n    &quot;&quot;&quot;\n    __listeners = {}  # class level cache of listeners, keyed by phrases\n    __lock = threading.RLock()\n    __max_size = 100\n\n    @classmethod\n    def get_listener(cls, phrases, subscriber):\n        &quot;&quot;&quot;Fetch an TwitterListener listening to a set of phrases and subscribe to it&quot;&quot;&quot;\n        with cls.__lock:\n        # get the listener from the cache or create a new one\n        phrases = frozenset(map(str, phrases))\n        listener = cls.__listeners.get(phrases, None)\n        if listener is None:\n        listener = cls(phrases)\n        cls.__listeners&#x5B;phrases] = listener\n\n        # add the subscription and return\n        listener.subscribe(subscriber)\n        return listener\n\n    def __init__(self, phrases):\n        &quot;&quot;&quot;Use static method 'get_listener' instead of constructing directly&quot;&quot;&quot;\n        _log.info(&quot;Creating listener for &#x5B;%s]&quot; % &quot;, &quot;.join(phrases))\n        self.__phrases = phrases\n        self.__subscriptions = set()\n        self.__tweets = &#x5B;None] * self.__max_size\n\n        # listen for tweets in a background thread using the 'async' keyword\n        auth = OAuthHandler(consumer_key, consumer_secret)\n        auth.set_access_token(access_token, access_token_secret)\n        self.__stream = Stream(auth, listener=self)\n        self.__stream.filter(track=phrases, is_async=True)\n        self.__connected = True\n\n    @property\n    def tweets(self):\n        return list(self.__tweets)\n\n    def subscribe(self, subscriber):\n        &quot;&quot;&quot;Add a subscriber that will be notified when new tweets are received&quot;&quot;&quot;\n        with self.__lock:\n            self.__subscriptions.add(subscriber)\n\n    def unsubscribe(self, subscriber):\n        &quot;&quot;&quot;Remove subscriber added previously.\n        When there are no more subscribers the listener is stopped.\n        &quot;&quot;&quot;\n        with self.__lock:\n            self.__subscriptions.remove(subscriber)\n            if not self.__subscriptions:\n                self.disconnect()\n\n    def disconnect(self):\n        &quot;&quot;&quot;Disconnect from the twitter stream and remove from the cache of listeners.&quot;&quot;&quot;\n        with self.__lock:\n        if self.__connected:\n            _log.info(&quot;Disconnecting twitter stream for &#x5B;%s]&quot; % &quot;, &quot;.join(self.__phrases))\n            self.__listeners.pop(self.__phrases)\n            self.__stream.disconnect()\n            self.__connected = False\n\n    @classmethod\n    def disconnect_all(cls):\n        &quot;&quot;&quot;Disconnect all listeners.&quot;&quot;&quot;\n        with cls.__lock:\n            for listener in list(cls.__listeners.values()):\n                listener.disconnect()\n\n    def on_data(self, data):\n        data = json.loads(data)\n        with self.__lock:\n            self.__tweets.insert(0, data)\n            self.__tweets = self.__tweets&#x5B;:self.__max_size]\n            for subscriber in self.__subscriptions:\n                try:\n                    subscriber.on_data(data)\n                except:\n                    _log.error(&quot;Error calling subscriber&quot;, exc_info=True)\n                   return True\n\n    def on_error(self, status):\n        with self.__lock:\n            for subscriber in self.__subscriptions:\n                try:\n                    subscriber.on_error(status)\n                except:\n                    _log.error(&quot;Error calling subscriber&quot;, exc_info=True)\n\nif __name__ == '__main__':\n    import time\n    logging.basicConfig(level=logging.INFO)\n\n    class TestSubscriber(object):\n        &quot;&quot;&quot;simple subscriber that just prints tweets as they arrive&quot;&quot;&quot;\n\n        def on_error(self, status):\n            print(&quot;Error: %s&quot; % status)\n\n        def on_data(self, data):\n            print(data.get(&quot;text&quot;))\n\n    subscriber = TestSubscriber()\n    listener = TwitterListener.get_listener(&#x5B;&quot;python&quot;, &quot;excel&quot;, &quot;pyxll&quot;], subscriber)\n\n    # listen for 60 seconds then stop\n    time.sleep(60)\n    listener.unsubscribe(subscriber)\n<\/pre><\/pre>\n\n\n\n<p>When this is run it&#8217;s very similar to the last case, except that now only the text part of the tweets are printed. Also note that the listener is not explicitly disconnected, that happens automatically when the last subscriber unsubscribes.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\npython twitterxl.py\n\nINFO:__main__:Creating listener for python, excel, pyxll\nINFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): stream.twitter.com\nLinuxtoday Make a visual novel with Python: Linux User &amp;amp;amp;amp;amp;amp;amp; Developer: Bridge the gap between books...\nHow to create drop down list in excel https:\/\/t.co\/Ii2hKRlRBe...\nRT @papisdotio: Flying dron with Python @theglamp #PAPIsConnect https:\/\/t.co\/zzPNSFb66e...\nRT @saaid230: The reason I work hard and try to excel at everything I do so one day I can take care ...\nRT @javacodegeeks: I'm reading 10 Awesome #Python Tutorials to Kick-Start my Web #Programming https:...\nINFO:__main__:Disconnecting twitter stream for\n\n&#x5B;python 1=&quot;excel,&quot; 2=&quot;pyxll&quot; language=&quot;,&quot;]&#x5B;\/python]\n\nProcess finished with exit code 0\n<\/pre><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-getting-the-data-into-excel\">Getting the Data into Excel<\/h2>\n\n\n\n<p>Now the hard part of getting the streaming twitter data into Python is taken care of, creating a real time data source in Excel using PyXLL is pretty straightforward.<\/p>\n\n\n\n<p>PyXLL 3.0 has a new class, <code>RTD<\/code>. When a function decorated with the <code>xl_func<\/code> decorator returns an RTD instance, the value of the calling cell will be the <code>value<\/code> property of the RTD instance. If the value property of the returned RTD instance later changes, the cell value changes.<\/p>\n\n\n\n<p>We will write a new class inheriting from RTD that acts as a subscriber to our twitter stream (in the same way as TestSubscriber in the code above). Whenever a new tweet is received it will update its value, and so the cell in Excel will update.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom pyxll import RTD\n\nclass TwitterRTD(RTD):\n    &quot;&quot;&quot;Twitter RTD class that notifies Excel whenever a new tweet is received.&quot;&quot;&quot;\n\n    def __init__(self, phrases):\n        # call super class __init__ with an initial value\n        super(TwitterRTD, self).__init__(value=&quot;Waiting for tweets...&quot;)\n\n        # get the TwitterListener and subscribe to it\n        self.__listener = TwitterListener.get_listener(phrases, self)\n\n    def disconnect(self):\n        # overridden from RTD base class. Called when Excel no longer\n        # needs the RTD object (for example, when the cell formula\n        # is changed.\n        self.__listener.unsubscribe(self)\n\n    def on_error(self, status):\n        self.value = &quot;#ERROR %s&quot; % status\n\n    def on_data(self, data):\n        self.value = data.get(&quot;text&quot;)\n<\/pre><\/pre>\n\n\n\n<p>To expose that to Excel all that&#8217;s needed is a function that returns an instance of our new TwitterRTD class<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom pyxll import xl_func\n\n@xl_func(&quot;string&#x5B;] phrases: rtd&quot;)\ndef twitter_listen(phrases):\n    &quot;&quot;&quot;Listen for tweets containing certain phrases&quot;&quot;&quot;\n    # flatten the 2d list of lists into a single list of phrases\n    phrases = &#x5B;str(x) for x in itertools.chain(*phrases) if x]\n    assert len(phrases) &amp;amp;amp;amp;amp;amp;gt; 0, &quot;At least one phrase is required&quot;\n\n    # return our TwitterRTD object that will update when a tweet is received\n    return TwitterRTD(phrases)\n<\/pre><\/pre>\n\n\n\n<p>All that&#8217;s required now is to add the module to the pyxll.cfg file, and then the new function &#8216;twitter_listen&#8217; will appear in Excel, and calling it will return a live stream of tweets.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab1.gif\" rel=\"attachment wp-att-260\"><img decoding=\"async\" width=\"540\" height=\"422\" data-attachment-id=\"260\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/a-real-time-twitter-feed-in-excel\/screengrab1\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab1.gif\" data-orig-size=\"540,422\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"screengrab1\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab1.gif\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab1.gif\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab1.gif\" alt=\"screengrab1\" class=\"wp-image-260\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-a-more-complete-twitter-feed\">A More Complete Twitter Feed<\/h2>\n\n\n\n<p>So far we&#8217;ve got live tweets streaming into Excel, which is pretty cool, but only one tweet is visible at a time and we can only see the tweet text. It would be even better to see a grid of data showing the most recent tweets with some metadata as well as the tweet itself.<\/p>\n\n\n\n<p>RTD functions always return just a single cell of data, so what we need to do is write a slightly different function that takes a couple more arguments: A key for the part of the tweet we want (e.g. &#8216;text&#8217; or &#8216;created_at&#8217;) and an index (e.g. 0 as the latest tweet, 1 the second most recent tweet etc).<\/p>\n\n\n\n<p>As some interesting bits of metadata are in nested dictionaries in the twitter data, the &#8216;key&#8217; used to select the item from the data dictionary is a &#8216;\/&#8217; delimited list of keys used to drill into tweet data (for example, the name of the user is in the sub-dictionary &#8216;user&#8217;, so to retrieve it the key &#8216;user\/name&#8217; would be used).<\/p>\n\n\n\n<p>The TwitterListener class we&#8217;ve written already keeps a limited history of the tweets it&#8217;s received so this isn&#8217;t too much more than we&#8217;ve already done.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nclass TwitterRTD(RTD):\n    &quot;&quot;&quot;Twitter RTD class that notifies Excel whenever a new tweet is received.&quot;&quot;&quot;\n\n    def __init__(self, phrases, row=0, key=&quot;text&quot;):\n        super(TwitterRTD, self).__init__(value=&quot;Waiting for tweets...&quot;)\n        self.__listener = TwitterListener.get_listener(phrases, self)\n        self.__row = row\n        self.__key = key\n\n    def disconnect(self):\n        self.__listener.unsubscribe(self)\n\n    def on_error(self, status):\n        self.value = &quot;#ERROR %s&quot; % status\n\n    def on_data(self, data):\n        # if there are no tweets for this row return an empty string\n        tweets = self.__listener.tweets\n        if len(tweets) &amp;amp;amp;amp;amp;amp;lt; self.__row or not tweets&#x5B;self.__row]:\n            self.value = &quot;&quot;\n            return\n\n        # get the value from the tweets\n        value = tweets&#x5B;self.__row]\n        for key in self.__key.split(&quot;\/&quot;):\n            if not isinstance(value, dict):\n                value = &quot;&quot;\n                break\n            value = value.get(key, {})\n\n         # set the value back in Excel\n         self.value = str(value)\n<\/pre><\/pre>\n\n\n\n<p>The worksheet function also has to be updated to take these extra arguments<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n@xl_func(&quot;string&#x5B;] phrases, int row, string key: rtd&quot;)\ndef twitter_listen(phrases, row=0, key=&quot;text&quot;):\n    &quot;&quot;&quot;Listen for tweets containing certain phrases&quot;&quot;&quot;\n    # flatten the 2d list of lists into a single list of phrases\n    phrases = &#x5B;str(x) for x in itertools.chain(*phrases) if x]\n    assert len(phrases) &amp;amp;amp;amp;amp;amp;gt; 0, &quot;At least one phrase is required&quot;\n\n    # return our TwitterRTD object that will update when a tweet is received\n    return TwitterRTD(phrases, row, key)\n<\/pre><\/pre>\n\n\n\n<p>After reloading the PyXLL addin, or restarting Excel, we can now call this modified function with different values for row and key to build an updating grid of live tweets.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab3.gif\" rel=\"attachment wp-att-266\"><img decoding=\"async\" data-attachment-id=\"266\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/a-real-time-twitter-feed-in-excel\/screengrab2\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab2.gif\" data-orig-size=\"1012,500\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"real-time-twitter-feed-in-excel\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab2.gif\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab2.gif\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab3.gif\" alt=\"screengrab3\" class=\"wp-image-266\"\/><\/a><\/figure>\n\n\n\n<p>One final step is to make sure that any active streams are disconnected when Excel closes. This will prevent the tweepy background thread from preventing Excel from exiting cleanly.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom pyxll import xl_on_close\n\n@xl_on_close\ndef disconnect_all_listeners():\n    TwitterListener.disconnect_all()\n<\/pre><\/pre>\n\n\n\n<p>The code from this blog is available on github <a href=\"https:\/\/github.com\/pyxll\/pyxll-examples\/tree\/master\/twitter\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/pyxll\/pyxll-examples\/tree\/master\/twitter<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-other-use-cases\">Other Use Cases<\/h2>\n\n\n\n<p>Of course getting real time tweets into Excel is interesting, but it may not really be that useful! Real time data in Excel has many other real-world applications however, for example:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Streaming market data such as stock prices from an external data provider<\/li><li>Retrieving data from a <a href=\"https:\/\/www.tivix.com\/blog\/django-vs-rails-picking-right-web-framework\">web application<\/a><\/li><li>Returning results from a long running function on a background thread<\/li><li>Live web traffic monitoring<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>PyXLL 3.0 introduced a new, simpler, way of streaming real time data to Excel from Python. Excel has had support for real time data (RTD) for a long time, but it requires a certain knowledge of COM to get it to work. With the new RTD features in PyXLL 3.0 it is now a lot<\/p>\n","protected":false},"author":1,"featured_media":268,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,9],"tags":[],"class_list":["post-232","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pyxll","category-real-time-data"],"acf":[],"jetpack_featured_media_url":"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2016\/03\/screengrab3.gif","jetpack_shortlink":"https:\/\/wp.me\/p7l3LP-3K","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/232","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/comments?post=232"}],"version-history":[{"count":1,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/232\/revisions"}],"predecessor-version":[{"id":1528,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/232\/revisions\/1528"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/media\/268"}],"wp:attachment":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/media?parent=232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/categories?post=232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/tags?post=232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}