Найти - Пользователи
Полная версия: Python3 FB2, метаданные
Начало » Python для новичков » Python3 FB2, метаданные
1 2 3
antib
Спасибо, это похоже то, что нужно, особенно fb2desc.
antib
Вроде получилось.
Надо будет проверить на fb2 из разных источников - если есть файлы с пустым description и по факту выполняющим его роль annotation, значит возможны и другие закидоны, но в целом заработало. Спасибо за советы.
import xml.etree.ElementTree as et
def check_elem (elem, target, field = None):
	"""Return True if target string found in element children's text attr"""
	if hasattr(elem, "text"):
		if target in str(elem.text).lower():
			if not field:
				return True
			else:
				if field in elem.tag.lower():
					return True
				else:
					print ("close, but no")
		else:
			if len (elem)>0:
				for child in elem:
					x = check_elem(child, target, field=field)
					if x:
						return True
	
def check_descr (fname, target, field = None):
	"""Returns True if target string found in description texts of fb2 file\
	else returns False
	fname - file name
	target - string to find
	field - field name (element.tag), default None (all tags)"""
	tree = et.parse(fname)
	root = tree.getroot()
	#root[0] - description , root[1] - body
	target = target.lower()
	if field:
		field = field.lower()
	if len(root[0])>0:
		print ("Checking description for \'{}\'".format(target))
		for thing in root[0]:
			x=check_elem(thing, target=target, field=field)
			if x:
				return True
	elif len(root[1])>0:
		for thing in root[1]:
			if "annotation" in thing.tag or "title" in thing.tag:
				print ("Checking annotation for \'{}\'".format(target))
				for thing1 in thing:
					x=check_elem(elem=thing1, target=target, field=field)
					if x:
						return True
	else:
		return False
py.user.next
antib
if hasattr(elem, "text"):

>>> import xml.etree.ElementTree as et
>>> 
>>> doc = et.fromstring('<x/>')
>>> hasattr(doc, 'text')
True
>>>

antib
if target in str(elem.text).lower():

>>> import xml.etree.ElementTree as et
>>> 
>>> doc = et.fromstring('<x>abc</x>')
>>> doc.text
'abc'
>>> type(doc.text)
<class 'str'>
>>>


>>> import xml.etree.ElementTree as et
>>> 
>>> doc = et.fromstring('<x>abc<y>def1</y><y>def2</y></x>')
>>> 
>>> lst = doc.findall('y')
>>> lst
[<Element 'y' at 0xb73f33c4>, <Element 'y' at 0xb73f3414>]
>>> [i.text for i in lst]
['def1', 'def2']
>>>
antib
Ок, проверка на наличие text лишняя, но уменьшение регистра нужна.

py.user.next
antib

if target in str(elem.text).lower():


>>> import xml.etree.ElementTree as et
>>>
>>> doc = et.fromstring('<x>abc</x>')
>>> doc.text
'abc'
>>> type(doc.text)
<class ‘str’>
>>>

In [1]: import xml.etree.ElementTree as et
In [2]: doc1 = et.fromstring("<x>abc</x>")
In [3]: doc1.text
Out[3]: 'abc'
In [4]: doc2 = et.fromstring("<z>AbC</z>")
In [5]: doc2.text
Out[5]: 'AbC'
In [6]: doc2.text == doc1.text
Out[6]: False
In [7]: doc2.text.lower() == doc1.text
Out[7]: True
py.user.next
antib
но уменьшение регистра нужна

Это к тому, что text - это уже объект типа str.
antib
Ну да. Я хочу выбирать те fb2, в которых в строках метаданных находится ключевое слово. Я что-то сделал неэффективно?
py.user.next
antib
Я что-то сделал неэффективно?

antib
if target in str(elem.text).lower():

В этой строке объект elem.text приводишь к str, когда он и так уже str. Если же он не str, то он типа NoneType - тоже нет смысла приводить к str.

antib
Я хочу выбирать те fb2, в которых в строках метаданных находится ключевое слово.

Нужно выделить сначала данные, а потом проверять их. Так у тебя источник данных отвяжется от fb2. То есть ты сможешь потом вытаскивать данные не только из fb2, но и ещё откуда-нибудь.

Отвязав данные от fb2, ты поймёшь, что не ограничиваешься средствами обработки xml. В данном случае получается, что данные из fb2 ты вытаскиваешь с помощью xml, а проверку данных ты делаешь с помощью re.
antib
py.user.next
В этой строке объект elem.text приводишь к str, когда он и так уже str. Если же он не str, то он типа NoneType - тоже нет смысла приводить к str.

Ну, искать строку в none вызывает ошибку (да, похоже я её так замалчиваю). Может стоит добавить проверку типа данных и при nonetype сравнение не проводить, или делать try/except на предмет TypeError.

py.user.next
Отвязав данные от fb2, ты поймёшь, что не ограничиваешься средствами обработки xml. В данном случае получается, что данные из fb2 ты вытаскиваешь с помощью xml, а проверку данных ты делаешь с помощью re.

Т.е. сначала вытащить все данные из файла, а потом уже их сравнивать. Так оно кажется стройнее, но что по факту мешает проверять их при помощи re сразу же?
py.user.next
antib
Может стоит добавить проверку типа данных и при nonetype сравнение не проводить

if elem.text is not None:

antib
но что по факту мешает проверять их при помощи re сразу же?

То, что ты ограничиваешься re. Программа должна как можно меньше ограничиваться.

А у тебя появляется два ограничения:
1)
Во-первых, ты считаешь, что при нахождении тега обязательно надо сравнивать его текст с чем-то. Это что значит? Что если тебе надо будет просто найти теги, ни с чем ничего не сравнивая, ты не сможешь использовать эту часть кода.

2)
Во-вторых, ты считаешь, что текст нужно сравнивать через re. Это что значит? Что если ты придумаешь способ сравнения, более подходящий, чем re, ты должен будешь редактировать эту часть кода.

В первом случае ты должен будешь писать всё заново.
Во втором случае ты должен будешь проверять поиск заново.
antib
Это всё в общем случае правильно и интересно к обдумыванию, но реализовывать я это буду уже после того, как доделаю оставшееся. А лучше применю к своей основной поделке, от которой я отвлёкся на fb2. В противном случае решение практического частного случая будет отодвинуто в будущее ради более универсального решения. А то глядишь к поиску просто тегов поттянется создание sqlite базы на все fb2 в папке т.к. надо же их куда-то девать, что бы не искать по всем файлам заново и так далее.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB