from bs4 import BeautifulSoup import re import os # Вспомогательная функция, её наличие не обязательно и не будет проверяться def build_tree(start, end, path): link_re = re.compile(r"(?<=/wiki/)[\w()]+") # Искать ссылки можно как угодно, не обязательно через re files = dict.fromkeys(os.listdir(path)) # Словарь вида {"filename1": None, "filename2": None, ...} # TODO Проставить всем ключам в files правильного родителя в значение, начиная от start link_list = [start] while link_list: for id, link in enumerate(link_list): with open("{}{}".format(path, link), encoding='utf-8') as data: file_links = link_re.findall(data.read()) for lnk in [i for i in file_links if i in files.keys()]: if files.get(lnk) is None: files[lnk] = link if lnk == end: return files link_list.append(lnk) link_list.pop(id) return files # Вспомогательная функция, её наличие не обязательно и не будет проверяться def build_bridge(start, end, path): files = build_tree(start, end, path) # TODO Добавить нужные страницы в bridge parent = end bridge = [parent] while parent != start: parent = files[parent] if parent is not None: bridge.append(parent) else: bridge.append(start) parent = start return bridge[::-1] def parse(start, end, path): """ Если не получается найти список страниц bridge, через ссылки на которых можно добраться от start до end, то, по крайней мере, известны сами start и end, и можно распарсить хотя бы их: bridge = [end, start]. Оценка за тест, в этом случае, будет сильно снижена, но на минимальный проходной балл наберется, и тест будет пройден. Чтобы получить максимальный балл, придется искать все страницы. Удачи! """ bridge = build_bridge(start, end, path) # Искать список страниц можно как угодно, даже так: bridge = [end, start] # Когда есть список страниц, из них нужно вытащить данные и вернуть их out = {} for file in bridge: with open("{}{}".format(path, file), encoding='utf-8') as data: soup = BeautifulSoup(data, "lxml") body = soup.find(id="bodyContent") # TODO посчитать реальные значения # imgs = 5 # Количество картинок (img) с шириной (width) не меньше 200 imgs = len((body.find_all('img', width=lambda x: int(x or 0) > 199))) # headers = 10 # Количество заголовков, первая буква текста внутри которого: E, T или C headers = len([i.text for i in body.find_all(name=re.compile(r'[hH1-6]{2}')) if i.text[0] in 'ETC']) # linkslen = 15 # Длина максимальной последовательности ссылок, между которыми нет других тегов linkslen = 0 link_found = body.find_next('a') while link_found: local_linklen = 1 for i in link_found.find_next_siblings(): if i.name == 'a': local_linklen += 1 else: break linkslen = max(linkslen, local_linklen) link_found = link_found.find_next('a') # lists = 20 # Количество списков, не вложенных в другие списки lists = 0 html_lists = body.find_all(['ul', 'ol']) for html_list in html_lists: if not html_list.find_parents(['ul', 'ol']): lists += 1 out[file] = [imgs, headers, linkslen, lists] return out if __name__ == '__main__': correct = { 'Stone_Age': [13, 10, 12, 40], 'Brain': [19, 5, 25, 11], 'Artificial_intelligence': [8, 19, 13, 198], 'Python_(programming_language)': [2, 5, 17, 41], } start = 'Stone_Age' end = 'Python_(programming_language)' path = './wiki/' print('parse result:', parse(start, end, path)) print('correct result', correct)