У меня есть несколько XML-файлов с такими именами, как:
LLL_ABC0D012_title.xml
LLL_ABC0D013_title.xml
LLL_ABT0G012_title.xml
LLL_ABR0N012_title.xml
После «LLL_» всегда есть 8 символов.
У меня есть файл Excel с более чем 900 строками, который выглядит следующим образом:
В моих XML-файлах есть теги <dtp1>text</dtp1>, <dtp2>text</dtp2>, <dtp3>text</dtp3>. Я хотел бы изменить текст этих тегов с текстом в ячейках соответствующих столбцов в таблице выше.
Для этого я бы хотел, чтобы мой сценарий выполнял цикл, который считывает символы ABC0D012 (а затем другие) в заголовке моего XML-файла, находит совпадение в столбце «Ссылка» моего файла Excel, а затем ищет соответствующее значение. в «dtp1», «dtp2» и «dtp3». Затем я хотел бы сохранить эти значения в переменной, чтобы заменить текст в моих тегах.
Я новичок в Python. Я попробовал что-то вроде:
import numpy as np
import openpyxl
import pandas as pd
import xml.etree.ElementTree as ET
import os
table1 = pd.read_excel('C:/Users/Documents/datatypes.xlsx', na_values=['NA'])
table2 = table1.replace('\xa0', ' ',regex=True)
for root, dirs, files in os.walk("."):
for file in files :
if file[-4:] == '.xml':
#print(file)
xml = ET.parse('LLL_ABC0D012_title.xml')
root = xml.getroot()
dtp1_xml = root[8]
dtp2_xml = root[9]
dtp3_xml = root[10]
num = file[4:12]
#print(num)
dtp1_excel = table2['dtp1'].where(table2['reference'] == num)
dtp1_xml.text = dtp1_excel
#print(dtp1_xml.text)
ET.indent(root)
xml.write("LLL_ABC0D012_title.xml", encoding='utf-8', xml_declaration=True, method='xml')
Но это не работает, «dtp1_excel» не возвращает значение dtp1 из таблицы в функции печати, а что-то вроде:
0 NaN
1 NaN
2 NaN
3 NaN
4 NaN
...
931 NaN
932 NaN
933 NaN
934 NaN
935 NaN
Name: dtp1, Length: 936, dtype: object
У меня нет значения NaN в столбцах reference и dtp1, и оно должно возвращать только одно значение ячейки.
Что не так с моим подходом? Не могли бы вы мне помочь, пожалуйста ?
сначала проверьте, что у вас всего table1
, затем, что у вас есть table2
и т. д. Возможно, вы сделали что-то для удаления значений. ИЛИ, может быть, проблема в том, когда вы это читаете. ИЛИ, может быть, ваш num
неправильный - например. в нем могут быть дополнительные пробелы, которые он не отображает, ИЛИ, возможно, ваши данные имеют строки в нижнем регистре, но имя файла имеет верхний регистр. и т. д.
возможно, проблема приводит к where()
- и, возможно, вам следует использовать table2['dtp1', table2['reference'] == num]
, а затем получить первый элемент из результата - потому что он всегда будет давать DataFrame
или Series
(даже если у него один результат или ноль результатов)
Почему вы импортируете openpyxl, если вместо этого вы используете pandas для чтения строки Excel для задания? См. также здесь или вы можете получить значения ячеек напрямую по ключу, doc
Основная проблема заключается в том, что .where()
возвращает все строки
и он помещает NaN
в строки, которые не совпали, и сохраняет значения в строках, которые совпали.
Например
import pandas as pd
data = {
'X': ['A','B','C'],
'Y': ['D','E','F'],
'Z': ['G','H','I']
}
df = pd.DataFrame(data)
df['X'].where(df['Y'] == 'E')
дает
0 NaN
1 B
2 NaN
Name: X, dtype: object
но df['X'][df['Y'] == 'E']
дает
1 B
Name: X, dtype: object
Но все же есть другая проблема: оба дают Series
, и все равно нужно использовать [index]
или .iloc[row_number]
, чтобы получить единственное значение.
results = df['X'][df['Y'] == 'E']
print(results[1]) # row's index is `1`
print(results.iloc[0]) # row's numer is `0`
Полный рабочий пример:
import pandas as pd
data = {
'X': ['A','B','C'],
'Y': ['D','E','F'],
'Z': ['G','H','I']
}
df = pd.DataFrame(data)
print('---')
print(df)
print("--- [df['Y'] == 'E'] ---")
results = df['X'][df['Y'] == 'E']
print(results)
print('type:', type(results))
print("--- .where(df['Y'] == 'E') ---")
results = df['X'].where(df['Y'] == 'E')
print(results)
print('type:', type(results))
results = df['X'][df['Y'] == 'E']
print("---")
print(' [1]:', results[1])
print('.iloc[0]:', results.iloc[0])
И у вас та же проблема — вам может понадобиться код без .where()
и с .iloc[0]
dtp1_excel = table2['dtp1'][table2['reference'] == num].iloc[0]
Если вам нужно сравнить с большим количеством значений, вы можете использовать
( ... == ... ) & (... == ...)
df['X'][ (df['Y'] == 'E') & (df['Y'] == 'F']) ]
или .isin()
df['X'][ df['Y'].isin(['E', 'F']) ]
dtp1_excel = table2['dtp1'][ table2['reference'] == num]
работает, но в конце num не всегда имеет одинаковое количество символов. Знаете ли вы, как я могу добавить функцию «или», например dtp1_excel = table2['dtp1'][ table2['reference'] == (num1 or num2 or num3)]
? «num2» и «num3» — другие варианты длины «num». Я пробовал это, но это не работает. Спасибо !
возможно, вам следует использовать .isin(dataset)
или .str.contains(text)
, чтобы проверить, есть ли в столбце какая-то часть строки. Pandas имеет множество функций.
Код item == (num1 or num2 or num3)
никогда не работает. Всегда нужно item == num1 or item == num2 or item == num3
в конце концов item in (num1, num2, num3)
. Но у Панды для этого есть item.isin([num1, num2, num3])
Я добавил примеры в ответ (в конце)
.isin([num1, num2, num3])
кажется работает! Еще раз, спасибо.
Вот решение без панд. Я использую openpyxl то, что вы тоже импортируете, но не используете. Может быть имеет смысл измененные XML-файлы записать в другую папку? Я только переименовал его здесь:
from openpyxl import load_workbook
import xml.etree.ElementTree as ET
import os
def row_values(row_id, col_w=4):
val_list = []
for col in range(col_w):
val_list.append(ws[row_id][col].value)
return val_list
def change_xml(filename, data_list):
root= ET.parse(filename).getroot()
root[8].text = data_list[1]
root[9].text = data_list[2]
root[10].text = data_list[3]
tree = ET.ElementTree(root)
ET.indent(root, space=' ')
tree.write(f"Changed_{data_list[0]}.xml", encoding='utf-8', xml_declaration=True, method='xml')
print(f"Changed_{data_list[0]}.xml finished!")
wb = load_workbook("datatypes.xlsx")
ws = wb.active # if you have only one sheet take the active
print("Work sheet title:", ws.title)
#ws = wb['Tabelle1']
files = os.listdir(path='./')
for f in files:
if "LLL" in f: # Because proceeded XML will written in same directory
for cell in ws['A']: # iterate about the column 'A' of the Excel
if f.endswith(".xml") and cell.value in f:
print("Parsed file: ", f)
change_xml(f, row_values(cell.row))
Поскольку я не знаю вашей структуры XML, я использовал этот фиктивный файл:
<?xml version = "1.0" encoding = "utf-8"?>
<root>
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dtp1></dtp1>
<dtp2></dtp2>
<dtp3></dtp3>
</root>
Выход:
<?xml version='1.0' encoding='utf-8'?>
<root>
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dummy />
<dtp1>1_blabla</dtp1>
<dtp2>1_1_blablabla</dtp2>
<dtp3>1_1_1_blablablabla</dtp3>
</root>
Спасибо за вашу помощь. Я новичок и не привык def
, я собираюсь использовать решение Furas, чтобы быстро выполнить свою работу, но я попробую ваше предложение на практике и скажу вам, удалось ли мне это сделать.
Возможно, сначала используйте
print()
(иprint(type(...))
,print(len(...))
и т. д.), чтобы увидеть, какая часть кода выполняется и что на самом деле у вас есть в переменных. Он называется"print debugging"
и помогает увидеть, что на самом деле делает код.