4

Using XLRD in Python to read from Excel.

Simple scenario. I have a cell with a value and this is associated with a named range.

NamedRange "Foo" = Sheet1!$A$1 The value in A1 is "Bar"

book =xlrd.open_workbook("")

rng =  book.name_map['foo'][0]  # lower case for some reason.

print rng.???   # how to print the cell value bar??

I just want to reference the Named range "Foo" in python code and print out the value "Bar" of the cell.

EDIT: Here is another more complete example:

import xlrd

workbook = xlrd.open_workbook('/path/to/metester.xls')
cell_obj = workbook.name_and_scope_map.get(('sales', -1))
# this does print Sheet1!$A$1
print cell_obj.formula_text
# this raises the NoneTypeError
print cell_obj.cell()

formula_text is there to ensure excel can read the file. In my case the named cell is "sales" in Sheet1, cell A1.

Returns:

Sheet1!$A$1
Traceback (most recent call last):
  File "tester.py", line 7, in <module>
    print cell_obj.cell()
  File "/usr/local/lib/python2.7/dist-packages/xlrd/book.py", line 253, in cell
    self.dump(self.book.logfile,
AttributeError: 'NoneType' object has no attribute 'logfile'
dnozay
  • 23,846
  • 6
  • 82
  • 104
Dickster
  • 2,969
  • 3
  • 23
  • 29
  • any solution to this? I've tried rng.cell() and that gives me an error: AttributeError: 'NoneType' object has no attribute 'logfile'. I am using version 0.9.3 – user_78361084 Jan 26 '15 at 00:31
  • 2
    @llamawithabowlcut Hmm - that Attribute error doesn't tally with ['the source code'](https://github.com/python-excel/xlrd/blob/master/xlrd/book.py#L240) - Name.cell() seems to rail only with an `XLRDError`. Are you sure it's that line that's giving you the error? as it refers to `logfile` - could it be that it's your logging setup or something that's actually failing? Could you give us a little more complete use case? Maybe your question isn't the same as the original from @Dickster and you need a new question? – J Richard Snape Jan 26 '15 at 12:15
  • @JRichardSnape: Please see my edits. I have no other imports and that is the full & complete code. – user_78361084 Jan 27 '15 at 01:20
  • @llamawithabowlcut See my answer - your question is a different one to the original. Would appreciate you confirming you're using .xlsx rather than .xls. Let me know if it works - took a while to figure out. – J Richard Snape Jan 27 '15 at 13:00

2 Answers2

5

Firstly, it is lower case as explained in the xlrd module information (https://secure.simplistix.co.uk/svn/xlrd/trunk/xlrd/doc/xlrd.html?p=4966):

name_map [#]

A mapping from lower_case_name to a list of Name objects. The list is sorted in scope order. Typically there will be one item (of global scope) in the list.

You have two options. If you a truely only setting a name for a single cell, then you use the 'cell' method of the Name class (see the docs):

import xlrd
book = xlrd.open_workbook("")
Name = book.name_map['foo'][0]
print(Name.cell())

Console:

text:'Bar'

If you have, however, named an entire range of values, then you need to use the area2d method of the Name class:

import xlrd
book = xlrd.open_workbook("q1.xls")
Name = book.name_map['foo'][0]
Sheet, rowxlo, rowxhi, colxlo, colxhi = Name.area2d()
for i in range(rowxhi):
    print(Sheet.cell(i,0))

Console:

text:'Bar'
jonnybazookatone
  • 2,188
  • 15
  • 21
  • 2
    As I mentioned above cell() gives me an error: AttributeError: 'NoneType' object has no attribute 'logfile' (Sheet.cell(i,0)) also gives me that same error) – user_78361084 Jan 26 '15 at 02:14
  • 1
    You need to give more details, example code, as noted by @J Richard Snape – jonnybazookatone Jan 26 '15 at 17:14
  • Thank you for your explanation, Jonny. I am trying to use the name range for a table within Excel but I can not find the corresponding function from xlrd. Do you happen to know how to read a table assigned a name range? – Zong Jan 04 '17 at 19:00
  • @Zong I'd suggest you open a new question and link to it here, it'll get answered much quickly than if you wait for me. – jonnybazookatone Jan 05 '17 at 02:24
3

The initial question and example is answered exactly by @jonnybazookatone answer below. The expanded example is a different error. It seems to be an xlrd deficiency to me, but I've hacked a solution for what you want. First section is explanation, last section the hack.

Explanation

Looking at the more expanded example, the Error message is produced from the lines of code that handle the error condition on a Name object where the result of evaluating the formulae in a Name range has failed for some reason.

The actual reported error

AttributeError: 'NoneType' object has no attribute 'logfile'

is a secondary error - indicating that self.book evaluates to None for this Name object.

Secondly - you are missing an important detail, I think, that your Excel file is in .xlsx format. Note the trailing x. In the normal xls parser, the Name object doesn't have an attribute formula_text, so your code fails with the following error.

Traceback (most recent call last):
  File "D:\q1.py", line 16, in <module>
    print cell_obj.formula_text
AttributeError: 'Name' object has no attribute 'formula_text'

Please be careful with your code examples - it took me a while to track that difference - xlsx files are parsed by entirely different code within xlrd. With the xlsx fixed, I can reproduce your error.

The formula_text attribute in this case is just giving the R-C notation reference to the cell that is your named range. It should be noted that the function that sets formula_text is caveated "#### UNDER CONSTRUCTION ####" and has been since the initial commit of the xlsx module in 2012.

The problem is - as far as I can see - formulae are never evaluated in the opening of an xlsx - so you hit the error that Name.res is None and therefore you see your error. It seems to be an xlrd bug / feature.

Hack

I have come up with a nasty hack that will do what you want in the case where your range is a single cell by expanding the cell reference. Note it will fail for named ranges referring to more than one cell as it is now, but could be easily modified to cope with that scenario:

hack = cell_obj.formula_text
(sheetName,ref) = hack.split('!')
(discard,colStr,rowStr) = ref.split('$')
col = 0
for i in range(len(colStr)):
    colAdd = string.ascii_uppercase.index(colStr)
    col += colAdd * 10**i
row = int(rowStr)-1
print("Trying to evaluate cell",row,col,"in sheet",sheetName)
print workbook.sheet_by_name(sheetName).cell(row,col)
Community
  • 1
  • 1
J Richard Snape
  • 20,116
  • 5
  • 51
  • 79
  • sorry about that .xls vs .xlsx error...stupid, stupid me. I like the name of var "hack". – user_78361084 Jan 29 '15 at 02:13
  • No problem, easy mistake to make. I never suspected the processing for xls vs. xlsx was so different until I started digging. Glad I could resolve it. Couldn't resist the var name, glad you like it :) – J Richard Snape Jan 29 '15 at 09:23
  • 1
    I've raised https://github.com/python-excel/xlrd/pull/326 to fix the secondary error – Alice Purcell Mar 11 '19 at 14:00