简体   繁体   中英

python library or code to read already open Excel file

Suppose I have an Excel 2013 file already open (say XYZ.xlsx) - this file gets some data via a DDE link. Suppose I want to read certain cells (say A1:B3) from a worksheet (say Sheet1). How can I do it in Python 3 (I am using Python 3.4.2.4 / Winpython installation)?

I found the openpyxl package, but I couldn't understand how to make it read an active open workbook?

I am not 100% sure if this solution works with DDE (is the file being written to disk somewhere?)

I recommend using xlrd ( https://github.com/python-excel/xlrd ). I personally installed it via pip. Then to simply read A1:B3 do as such (ripped off the docs):

from xlrd import open_workbook

wb = open_workbook('spreadsheet.xlsx')
for s in wb.sheets():
     print('Sheet:',s.name) # you can check for the sheet name here if you want
     for row in range(2): #the first 2 rows (A and B) (use s.nrows for all)
         values = []
         for col in range(3): #the first 3 columns (use s.ncols for all)
            values.append(str(s.cell(row,col).value))
         print(','.join(values))

I needed exactly the same solution and here is what I did (it's already working):

  1. I created a local http server and an local websocket server on different ports (8080 and 3000) with Node.js (using Node Webkit, but you can use pure Node.js).

  2. On http server I set http://localhost:8080/write/ to receive a GET request with the params I wanted and to broadcast a message to the websocket server with those params.

  3. I wrote a VBA code to push data to the local http server, using Worksheet_Calculate sub (it's an event fired every time the worksheet refresh its content).

  4. Finnaly I made Python to listen to the websocket server (ws://localhost:3000/), waiting for the server signal.


Diving into coding details:

To create local server I used Express and ws modules. The first one to create http server and the second to create websocket server:

Express details:

var express = require('express');
var app = express();
app.get('/', function (req, res) { res.send('Hello World!') }) // to see if server is on

// broadcast data on q GET variable: http://localhost:8080/write/?q=32,34,23
app.get('/write/', function (req, res) {   
    wss.broadcast(req.query.q); // this line uses the variable created below
    res.send();
}); 

var server = app.listen(8080, function () {});

ws details:

var WebSocketServer = require('ws').Server;

wss = new WebSocketServer({port: 3000});
wss.on('connection', function(ws) {
    // you can use this event to perform some action when python get connected
    ws.send("you're connected");        

    // use this event if you need websocket to receive message
    ws.on('message', function(message) { 
        ws.send(message);
    });
});

wss.broadcast = function broadcast(data) {
    wss.clients.forEach(function each(client) {
        client.send(data);
    });
 };

To send data from Excel to the http server (I tried to use websocket, but I couldn't figure out how Excel connects to a websocket):

Function postServer(url, data)
    Set objHTTP = CreateObject("MSXML2.ServerXMLHTTP")
    objHTTP.Open "GET", url & data, False ' true para asynchronous calls
    objHTTP.send '(dados)

    postServer = 1 'It's a good idea to get the responseHTML from server and verifies if the everything is all right
End Function

Use this function inside Worksheet_Calculate() to send data:

Private Sub Worksheet_Calculate()
    server = "http://localhost:8080/write/?q="
    'data = <something> 'catch your data with vba code
    postServer(server,data)
End sub

The trick detail here is how to know when some data is already sent so you can avoid send it twice or more. You'll have to create your method based on how your data is organized on the sheet. For me, a have a number column, ordered and a cell registering the last number I sent.

Ok! Now you have to prepare Python to receive this data:

I downloaded the websocket-client 0.25.0 module. And here is the python code to listen to the websocket server:

import sys
import websocket
import thread
import time

wsAddress = "ws://localhost:3000/"     

def on_message(ws, message):
    print message # you got it!

def on_error(ws, error):
    print error

def on_close(ws):
    print "### closed ###"

def on_open(ws):
    print "conectado"

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp(wsAddress,
                          on_message = on_message,
                          on_error = on_error,
                          on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

If you're on a local network, you can use IP (or machine names) to post and listen to the server (it's awesome). Here is the code to discover the id with node.js:

var os = require('os');
var ifaces=os.networkInterfaces();
for (var dev in ifaces) {
    var alias=0;
    ifaces[dev].forEach(function(details){
        if ((details.family=='IPv4')&&(details.address!="127.0.0.1")&&(details.internal === false)) {
            console.log(details.address);
            ++alias;
        }
    });
}  

I hope I've helped. If you let some questions, just let me know.

I wanted to share a solution largely stolen from this StackOverflow solution since I've found xlrd to have inconsistent outcomes when opening Excel files that may or may not be opened by another user on the network. Using win32com has (so far) worked better for me.

#using path to avoid escape character errors
from pathlib import Path
path = Path(r'/full/filepath')

from win32com import client

xlApp = client.GetObject(None, "Excel.Application")
books = xlApp.Workbooks.Open(path)
ws = books.Worksheets[0]
ws.Visible = 1
#returns tuple
shift_1 = ws.Range("A1:B2").Value

import numpy as np
#selection of 0 element of tuple would depend on use case.
shift_1 = np.asarray(shift_1[0])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM