Форум сайта python.su
Привет!
Подскажите, как можно получить авторов книг 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 (Май 6, 2022 23:25:10)
Офлайн
Что интересно, функция на 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
Отредактировано Novator (Май 9, 2022 09:09:37)
Офлайн
Novatorскиньте две книги которые нормально обрабатываются и которые нет
натыкаюсь на проблему, когда автор не один, а тег middle-name (отчество) указан не для всех, тогда список неверного размера и теряется порядок
Офлайн
Скинь примеры книг. Там, где несколько авторов. Там, где есть полные данные и есть пропуски.
Просто надо выбрать все узлы author, которые находятся в узле description и в его дочернем узле title-info. Дальше эти выбранные узлы (несколько авторов) по очереди подаются в функцию разобрать_автора(). Функция разобрать_автора() сначала устанавливает для всех нужных тегов значения None, затем выбирает все нужные теги. Для тех нужных тегов, который найдены, значение None перезаписывает текстами, которые содержатся в тегах. Для тех нужных тегов, который не найдены, оставляет None.
Пример куска книжки
Отредактировано py.user.next (Май 9, 2022 12:37:18)
Офлайн
py.user.nextДа, получается, что для авторов нужна отдельная функция, которая будет работать после функции get_text_node(self, nodes, node)
Дальше эти выбранные узлы (несколько авторов) по очереди подаются в функцию разобрать_автора()
Отредактировано Novator (Май 9, 2022 12:59:41)
Офлайн
Здесь пример присоединил.
Отыскивает в файле fb2 имена отчества и фамилии авторов и выводит их, если они есть.
Вывод
[guest@localhost getfbauthors]$ ./getfbauthors.py
Роберт Артур
Роальд Даль
Гарри Гаррисон
Генри Каттнер
Тэлмидж Пауэлл
Деннис Уитли
Роберт Альберт Блох
Чарльз Бернард Гилфорд
Роберт Шпехт
Уильям Бриттен
Мартин С. Уоддел
Рональд Четвинд-Хейс
Нигель Нил
Ричард Мэтисон
[guest@localhost getfbauthors]$
Отредактировано py.user.next (Май 9, 2022 16:12:51)
Прикреплённый файлы:
getfbauthors.tar.gz (384,1 KБ)
Офлайн
py.user.next, работает, правда пока не разбирался как. Буду смотреть, как вписать в существующие реалии под себя
Офлайн
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
Отредактировано xam1816 (Май 9, 2022 23:22:29)
Офлайн
В моем виде сработало так:
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()
Офлайн
Тоже вариант подсказали… По моему, самый короткий. Ну правда, если добавить групповую обработку, то коротким уже не будет:
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)
Офлайн