Найти - Пользователи
Полная версия: Как получить теги авторов книг fb2
Начало » Python для новичков » Как получить теги авторов книг fb2
1
Novator
Привет!
Подскажите, как можно получить авторов книг fb2? Голову сломал, вроде и получаю списки, но натыкаюсь на проблему, когда автор не один, а тег middle-name (отчество) указан не для всех, тогда список неверного размера и теряется порядок

У меня выходит примерно так:
 import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element
import os
PATH = 'j:\\My_book'
class FB2Parser:
	def __init__(self, folder):
		self.folder = folder
		self.filelist = self.ret_filelist_in_folder()
	def get_title_publisher_info(self, nodes):
		info = []
		for file in self.filelist:
			file = PATH + '\\'+ file
			self.root = ET.parse(file)
			self.cleanup()
			info.append(self.root.find(nodes))
		return info
	def get_text_node(self, nodes, node):
		info = self.get_title_publisher_info(nodes)
		job = []
		for element in info:
			results = []
			results.append(element.findall(node))
			if len(results[0]) == 1:
				job.append(results[0][0].text)
			elif len(results[0]) > 1:
				result = []
				for res in results[0]:
					result.append (res.text)
				job.append(result)
			else:
				job.append('')
		return job
	def cleanup(self):
		for element in self.root.iter():
			element.tag = element.tag.partition('}')[-1]
	def ret_filelist_in_folder(self):
		filelist = []
		for file in os.listdir(self.folder):
			filelist.append(file)
		return filelist
def run():
	converter = FB2Parser(PATH)
	converter.get_text_node('./description/title-info', 'author')
if __name__ == '__main__':
	run()
Novator
Что интересно, функция на autoit получает авторов адекватно, то есть last-name, middle-name, first-name через запятую. И потом уже можно сплит делать и назначать отчество тем авторам, где оно указано
Func _ReadXMLNodeText($oXMLobj, $sNode)
Local $results = $oXMLobj.SelectNodes($sNode)
Local $result
If $results.length = 1 Then
Return $results.item(0).text
ElseIf $results.length > 1 Then
For $res In $results
$result &= $res.text & '/'
Next
$result = StringTrimRight($result, 1)
Return $result
Else
Return ""
EndIf
EndFunc ;==>_ReadXMLNodeText
xam1816
Novator
натыкаюсь на проблему, когда автор не один, а тег middle-name (отчество) указан не для всех, тогда список неверного размера и теряется порядок
скиньте две книги которые нормально обрабатываются и которые нет
py.user.next
Скинь примеры книг. Там, где несколько авторов. Там, где есть полные данные и есть пропуски.

Просто надо выбрать все узлы author, которые находятся в узле description и в его дочернем узле title-info. Дальше эти выбранные узлы (несколько авторов) по очереди подаются в функцию разобрать_автора(). Функция разобрать_автора() сначала устанавливает для всех нужных тегов значения None, затем выбирает все нужные теги. Для тех нужных тегов, который найдены, значение None перезаписывает текстами, которые содержатся в тегах. Для тех нужных тегов, который не найдены, оставляет None.

Пример куска книжки
Novator
py.user.next
Дальше эти выбранные узлы (несколько авторов) по очереди подаются в функцию разобрать_автора()
Да, получается, что для авторов нужна отдельная функция, которая будет работать после функции get_text_node(self, nodes, node)
То есть написать converter.get_text_node('./description/title-info', ‘author’) не выйдет. Что ж, допишем…

А пример книги вот
py.user.next
Здесь пример присоединил.

Отыскивает в файле fb2 имена отчества и фамилии авторов и выводит их, если они есть.

Вывод
[guest@localhost getfbauthors]$ ./getfbauthors.py 
Роберт Артур
Роальд Даль
Гарри Гаррисон
Генри Каттнер
Тэлмидж Пауэлл
Деннис Уитли
Роберт Альберт Блох
Чарльз Бернард Гилфорд
Роберт Шпехт
Уильям Бриттен
Мартин С. Уоддел
Рональд Четвинд-Хейс
Нигель Нил
Ричард Мэтисон
[guest@localhost getfbauthors]$
Novator
py.user.next, работает, правда пока не разбирался как. Буду смотреть, как вписать в существующие реалии под себя
xam1816
 from bs4 import BeautifulSoup
#
class Fb2Parser:
    def __init__(self, fb2_file):
        self.soup = self._get_soup(fb2_file)
        self.authors = self._get_authors()
        self.title = self._get_title()
        self.genre = self._get_genre()
#
    def _get_soup(self, file):
        with open(file, 'r', encoding='utf8') as f:
            soup = BeautifulSoup(f.read(), 'xml')
        return soup
#
    def _get_authors(self):
        out = []
        authors = self.soup.find_all('author')
        if authors:
            for author in authors:
                tags = (author.find(i) for i in ('first-name', 'middle-name', 'last-name'))
                author_data = tuple(i.text if i else None for i in tags)
                if any(author_data):
                    out.append(author_data)
            return out
#
    def _get_title(self):
        title = self.soup.find('title')
        if title:
            return title.text
#
    def _get_genre(self):
        genre = self.soup.find('genre')
        if genre:
            return genre.text
#
if __name__ == '__main__':
    book = Fb2Parser('book.fb2')
    print(book.authors)
    s = '\n'.join(f'{f} {m} {l}' if m else f'{f} {l}' for f, m, l in book.authors)
    print(s)
#
    print(book.title)
    print(book.genre)

[('Роберт', None, 'Артур'), ('Роальд', None, 'Даль'), ('Гарри', None, 'Гаррисон'), ('Генри', None, 'Каттнер'), ('Тэлмидж', None, 'Пауэлл'), ('Деннис', None, 'Уитли'), ('Роберт', 'Альберт', 'Блох'), ('Чарльз', 'Бернард', 'Гилфорд'), ('Роберт', None, 'Шпехт'), ('Уильям', None, 'Бриттен'), ('Мартин', 'С.', 'Уоддел'), ('Рональд', None, 'Четвинд-Хейс'), ('Нигель', None, 'Нил'), ('Ричард', None, 'Мэтисон')]
Роберт Артур
Роальд Даль
Гарри Гаррисон
Генри Каттнер
Тэлмидж Пауэлл
Деннис Уитли
Роберт Альберт Блох
Чарльз Бернард Гилфорд
Роберт Шпехт
Уильям Бриттен
Мартин С. Уоддел
Рональд Четвинд-Хейс
Нигель Нил
Ричард Мэтисон

Короткий триллер

thriller

Process finished with exit code 0
Novator
В моем виде сработало так:
 from xml.etree.ElementTree import Element
import os
PATH = 'j:\\My_project_programming\\Converter\\test\\FB2'
class FB2Parser:
	def __init__(self, folder):
		self.folder = folder
		self.filelist = self.ret_filelist_in_folder()
	def get_title_publisher_info(self, nodes):
		info = []
		for file in self.filelist:
			file = PATH + '\\'+ file
			self.root = ET.parse(file)
			self.cleanup()
			info.append(self.root.find(nodes))
		return info
	def get_text_node(self, nodes, node):
		info = self.get_title_publisher_info(nodes)
		job = []
		for element in info:
			results = []
			results.append(element.findall(node))
			if len(results[0]) == 1:
				if node == 'author':
					job.append(self.get_author_data(results))
				else:
					job.append(results[0][0].text)
			elif len(results[0]) > 1:
				result = []
				if node == 'author':
					job.append(self.get_author_data(results))
				else:
					for res in results[0]:
						result.append (res.text)
					job.append(result)
			else:
				job.append('')
		return job
	def get_author_data(self, seachin):
		auth = []
		firstname = middlename = lastname = None
		for elem in seachin[0]:
			firstname_node = elem.find('first-name')
			middlename_node = elem.find('middle-name')
			lastname_node = elem.find('last-name')
			if firstname_node is not None:
				firstname = firstname_node.text
			if middlename_node is not None:
				middlename = middlename_node.text
			if lastname_node is not None:
				lastname = lastname_node.text
			if middlename is None:
				out = '{} {}'.format(firstname, lastname)
			else:
				out = '{} {} {}'.format(firstname, middlename, lastname)
			auth.append(out)
		return auth
	def cleanup(self):
		for element in self.root.iter():
			element.tag = element.tag.partition('}')[-1]
	def ret_filelist_in_folder(self):
		filelist = []
		for file in os.listdir(self.folder):
			filelist.append(file)
		return filelist
def run():
	converter = FB2Parser(PATH)
	aq = converter.get_text_node('./description/title-info', 'author')
	print (aq)
if __name__ == '__main__':
	run()

Получаю теги со всех файлов в папке
Novator
Тоже вариант подсказали… По моему, самый короткий. Ну правда, если добавить групповую обработку, то коротким уже не будет:
 import xml.etree.ElementTree as ET
import re
root = ET.parse('book.fb2')
rootTag = root.getroot().tag
namespaces = {
    '': re.match(r'^\{([^\}]+)\}', rootTag).group(1)
authorEls = root.findall('description/title-info/author', namespaces)
res = []
for authorEl in authorEls:
    firstName = authorEl.find('first-name', namespaces).text
    middleName = authorEl.find('middle-name', namespaces)
    if middleName is not None:
        middleName = authorEl.find('middle-name', namespaces).text
    lastName = authorEl.find('last-name', namespaces).text
    if middleName is None:
        out = '{} {}'.format(firstName, lastName)
    else:
        out = '{} {} {}'.format(firstName, middleName, lastName)
    res.append(out)
print(res)
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