Advertisement
Korotkodul

LORD_4

Oct 31st, 2021
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.77 KB | None | 0 0
  1. from urllib.request import urlopen
  2. from bs4 import BeautifulSoup
  3. import json
  4. import requests
  5.  
  6. url = "https://ae-lib.org.ua/texts-c/tolkien__the_lord_of_the_rings_1__en.htm"
  7. html = urlopen(url).read()
  8. soup = BeautifulSoup(html, features="html.parser")
  9.  
  10. # kill all script and style elements
  11. for script in soup(["script", "style"]):
  12.     script.extract()  # rip it out
  13.  
  14. # get text
  15. text = soup.get_text()
  16. txt_list  = text.split()
  17. # break into lines and remove leading and trailing space on each
  18. lines = (line.strip() for line in text.splitlines())
  19. # break multi-headlines into a line each
  20. chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
  21. # drop blank lines
  22. text = '\n'.join(chunk for chunk in chunks if chunk)
  23.  
  24. text = text[1100::]  # Сразу избавляемся от оглавления
  25. titles = [
  26.     "Chapter 1\nA Long-expected Party",
  27.  
  28.     "Chapter 2\nThe Shadow of the Past",
  29.  
  30.     "Chapter 3\nThree is Company",
  31.  
  32.     "Chapter 4\nA Short Cut to Mushrooms",
  33.  
  34.     "Chapter 5\nA Conspiracy Unmasked",
  35.  
  36.     "Chapter 6\nThe Old Forest",
  37.  
  38.     "Chapter 7\nIn the House of Tom Bombadil",
  39.  
  40.     "Chapter 8\nFog on the Barrow-Downs",
  41.  
  42.     "Chapter 9\nAt the Sign of The Prancing Pony",
  43.  
  44.     "Chapter 10\nStrider",
  45.  
  46.     "Chapter 11\nA Knife in the Dark",
  47.  
  48.     "Chapter 12\nFlight to the Ford",
  49.  
  50.     "Chapter 1\nMany Meetings",
  51.  
  52.     "Chapter 2\nThe Council of Elrond",
  53.  
  54.     "Chapter 3\nThe Ring Goes South",
  55.  
  56.     "Chapter 4\nA Journey in the Dark",
  57.  
  58.     "Chapter 5\nThe Bridge of Khazad-dûm",
  59.  
  60.     "Chapter 6\nLothlórien",
  61.  
  62.     "Chapter 7\nThe Mirror of Galadriel",
  63.  
  64.     "Chapter 8\nFarewell to Lórien",
  65.  
  66.     "Chapter 9\nThe Great River",
  67.  
  68.     "Chapter 10\nThe Breaking of the Fellowship",
  69.  
  70. ]
  71. """
  72. В цикле НИЖЕ программа находит индексы начала и конца каждой главы в тексте книги.
  73. Это необходимо для дальнейшей обработки текста.
  74. Ниже этого цикла есть готовый список indexes с индексами начала  каждой главы."""
  75. indexes = []
  76. for title in titles:
  77.     idx = text.find(title)
  78.     indexes.append(idx)
  79. # print(indexes)
  80.  
  81. chapters_idx_in_text = {}
  82. for i in range(0, len(titles)):
  83.     title = titles[i]
  84.     if i == len(titles) - 1:
  85.         chapters_idx_in_text[title] = (indexes[i], len(text))
  86.     else:
  87.         chapters_idx_in_text[title] = (indexes[i], indexes[i + 1])
  88.  
  89. #print(chapters_idx_in_text)
  90. url = 'https://the-one-api.dev/v2/character'
  91. from requests.structures import CaseInsensitiveDict
  92. headers = CaseInsensitiveDict()
  93. headers["Accept"] = "application/json"
  94. headers["Authorization"] = "Bearer 7mbNq6GVWo0LiEQS7GCc"
  95. get_characters = requests.get(url, headers=headers)
  96. if get_characters.status_code == requests.codes.ok:
  97.     api_characters = get_characters.json()
  98.  
  99. #Делаем API запрос, чтобы узнать все имена персонажей из "Вдастелина колец"
  100. all_characters = []
  101. for one in api_characters["docs"]:
  102.     all_characters.append(one["name"])
  103. #Узнаем, какие имена встречаются в тексте
  104. heroes_in_text = []
  105. for hero in all_characters:
  106.     if hero in txt_list:
  107.         heroes_in_text.append(hero)
  108.  
  109.  
  110.  
  111. #Будем называть ИНДЕКСОМ ГЕРОЯ каждый индекс вхождения слова в список txt_list -- список всех слов, разделённых пробелом
  112. #Например:
  113. # имя героя -- 'a'
  114. #txt_list = ['a','b','c','a']
  115. #Тогда 0 и 3 -- являются ИНДЕКСАМИ героя
  116.  
  117.  
  118. #Функции
  119. #Посчитать расстояние между 2-мя индексами
  120. def dst(a, b):
  121.     return abs(a - b)
  122.  
  123.  
  124.  
  125. #Посчитать расстояние между одним индексом героя A и всеми индексами героя B
  126. def binP_dst(x, array):
  127.     L = 0
  128.     R = len(array) - 1
  129.     while R - L > 1:
  130.         idx = (L + R) // 2
  131.         y = array[idx]
  132.         if y >= x:
  133.             R = idx
  134.             L = (L + R) // 2
  135.         else:
  136.             L = idx
  137.             R = (R + len(array)) // 2
  138.     if dst(x, array[L]) <=  dst(x, array[R]):
  139.         return dst(x, array[L])
  140.     else:
  141.         return dst(x, array[R])
  142.  
  143.  
  144.  
  145. heroes_idx = {}
  146.  
  147.  
  148. #Посчитать минимальное расстояние между именами 2-х героев в тексте
  149. def min_dist(hero_A, hero_B):
  150.     min_dst = pow(10, 20)
  151.     idx_A = heroes_idx[hero_A]
  152.     idx_B = heroes_idx[hero_B]
  153.     for i in range(len(idx_A)):
  154.         new_dst = binP_dst(idx_A[i], idx_B)
  155.         if new_dst < min_dst:
  156.             min_dst = new_dst
  157.     return min_dst
  158.  
  159.  
  160. #Получить список всех индексов одного персонажа в списке txt_list
  161. def get_idx(hero_name):
  162.     hero = hero_name
  163.     from_ = 0
  164.     all_idx = []
  165.     while hero in txt_list[from_::]:
  166.         idx = txt_list.index(hero, from_)
  167.         all_idx.append(idx)
  168.         from_ = idx+1
  169.     return all_idx
  170.  
  171.  
  172. #Теперь посчитаем все индексы для каждого героя
  173. for hero in heroes_in_text:
  174.     hero_idx = get_idx(hero)
  175.     heroes_idx[hero] = hero_idx
  176.  
  177.  
  178.  
  179.  
  180.  
  181. heroes_dist = {}
  182. for hero in heroes_in_text:
  183.     heroes_dist[hero] = {}
  184.  
  185. for hero_A in heroes_idx:
  186.     for hero_B in heroes_idx:
  187.         if (hero_A != hero_B) and (hero_B not in heroes_idx[hero_A]) and (hero_A not in heroes_idx[hero_B]):
  188.             minimum_dist = min_dist(hero_A, hero_B)
  189.             heroes_dist[hero_A][hero_B] = minimum_dist
  190.             heroes_dist[hero_B][hero_A] = minimum_dist
  191.  
  192.  
  193. with open("heroes_dist.json", 'w', encoding='utf-8') as fh:
  194.     fh.write(json.dumps(heroes_dist, ensure_ascii=False, indent = 4))
  195.  
  196. #Персонажи не связаны, если количество слов между ними больше N.
  197. #Пусть N = среднеарифметиечское кол-во слов в одной главе, делённое на 80
  198. N = len(txt_list) / len(titles) / 80
  199. print("N = ",N)
  200. """Обоснование выбора числа N  в задании 4.
  201. Назовём героев "связными", если они вместе принимают участие хотя бы в одном эпизоде.
  202. Глава -- это смысловая часть, в которой происходит какое-то некотрое количество эпизодов.
  203. Значит, если два каких-то персонажа фигурируют в одной и той же главе, то есть вероятность,
  204. что они вместе принимают,участие в каком-то эпизоде.
  205.  
  206. Чтобы как можно более точно отразить связь персонажей между собой, разделим
  207. средний размер главы на 80, предпологая, что в одной главе может быть до 80 эпизодов.
  208. Теперь после деления на 80 вероятность того, что персонажи принимают участие в одном эпиходе, значительно возросла.
  209.  
  210. В итоге вышло, что N = 105 (примерно).
  211. Больше этого значения N лучше не брать, поскольку, чем больше N, тем более запутанный и визуально непонятный граф.
  212. """
  213.  
  214. #Строим граф
  215. import networkx as nx
  216. import matplotlib.pyplot as plt
  217. from random import randint
  218. G = nx.Graph()
  219. marked_edges = []
  220. colors = ['red', 'green', 'pink',  'brown', 'yellow', 'purple', 'blue']
  221.  
  222. for hero_A in heroes_in_text:
  223.     for hero_B in heroes_dist[hero_A]:
  224.         if heroes_dist[hero_A][hero_B] <= N:
  225.             if min(hero_A, hero_B) + max(hero_A, hero_B) not in marked_edges:
  226.                 marked_edges.append(min(hero_A, hero_B) + max(hero_A, hero_B))
  227.                 G.add_edge(hero_A, hero_B, weight = heroes_dist[hero_A][hero_B])
  228. color_map = []
  229. cnt = -1 #сделаем рандомные цвета для всех вершин, чтобы было легче различать разных героев
  230. for edge in G:
  231.     cnt += 1
  232.     cnt %= len(colors)
  233.     color_map.append(colors[cnt])
  234. color_map_2 = []
  235. cnt = -1
  236. for node in G:
  237.     cnt += 1
  238.     cnt %= len(colors)
  239.     color_map_2.append(colors[cnt])
  240. #Разноцветные рёбра в графе могут помочь лучше проследить связть между 2-мя персонажами
  241. nx.draw(G, with_labels=True,node_color= color_map_2,edge_color = color_map)#,  node_size = 10
  242. #plt.savefig("N4_22.png")
  243. plt.show()
  244. print("FINISHED")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement