Сам_себе_программист._Как_научиться_программировать_и_устроиться. Guide to Programming Professionally
Скачать 3.94 Mb.
|
, ! X B определяет сам контент страни- цы. Вместе теги создают ссылку. HTML-элементы могут содержать дан- ные. К примеру, помещение строки href="https://w ww.google.com" внутрь HTML-элемента a дает браузеру понять, на какой сайт нужно добавить ссылку. В языке HTML есть еще много чего, но для создания своего первого парсера кон- тента этих знаний вам будет достаточно. 173 Введение в инструменты программирования Парсинг контента с сайта Google Новости В данном разделе вы построите парсер контента , который собирает все исто- рии с сервиса Google Новости, извлекая все теги из HTML-кода этого сайта. На сайте Google Новости эти теги используются для создания ссылок на различные сайты, так что помимо некоторых дополнительных данных вы собе- рете все URL-адреса новостей, отображаемых на сайте. Вы воспользуетесь мо- дулем BeautifulSoup, чтобы выполнить парсинг HTML-кода сайта Google Новости. Парсинг означает принятие формата вроде HTML и придание ему структуры при помощи языка программирования. К примеру, превращение дан- ных в объект. Для начала используйте следующую команду для установки модуля BeautifulSoup на Ubuntu и Unix. bash_ex58.sh $ sudo pip install beautifulsoup4==4.4.1 >> Successfully insta lled beautifulsoup4-4.4.1 И на Windows (запустите командную строку от имени администратора). bash_ex59.sh C:\Python36\Scripts\pip3.exe install beautifulsoup4==4.4.1 >> Successfully installed beautifulsoup4-4.4.1 Для работы с URL-адресами у Python есть встроенный модуль urllib. Введи- те следующий код в новый файл Python: Python_ex284.py 1 import urllib.request 2 from bs4 import BeautifulSoup 3 class Scraper: 4 def __init__( self , 5 site): 6 self .site = site 7 def scrape( self ): 8 pass Метод __init__ принимает в качестве параметра сайт, с которого нужно со- брать данные. Позднее вы передадите в качестве параметра значение "https:// news.google.ru/" . В классе Scraper есть метод scrape, который вы будете вызывать всякий раз, когда захотите собрать данные с переданного сайта. Добавьте следующий код в ваш метод scrape: 174 Часть III Python_ex285.py 1 def scrape( self ): 2 r = urllib.request.urlopen( self .site) 3 html = r.read() Функция urlopen() отправляет запрос на веб-сайт и возвращает объект Response , в котором сохранен HTML-код этого сайта вместе с дополнитель- ными данными. Функция response.read() возвращает HTML-код из объекта Response . Весь HTML-код с сайта содержится в переменной html. Теперь вы готовы выполнить парсинг HTML-кода. Добавьте в функцию scrape новую строку кода, которая создаст объект BeautifulSoup, и передай- те в функцию в качестве параметра переменную html и строку "html.parser" (поскольку вы выполняете парсинг HTML-контента). Python_ex286.py 1 def scrape( self ): 2 r = urllib.request.urlopen( self .site) 3 html = r.read() 4 parser = "html.parser" 5 sp = BeautifulSoup(html, parser) Объект BeautifulSoup делает всю трудную работу и выполняет парсинг HTML-кода. Теперь вы можете добавить в функцию scrape код, который вы- зывает метод nd_all в объекте BeautifulSoup. Передайте в качестве пара- метра значение "a" (это сообщит функции, что нужно искать теги ) и метод вернет все URL-адреса, на которые ссылается сайт, в загруженном вами HTML-коде. Python_ex287.py 1 def scrape( self ): 2 r = urllib.request.urlopen( self .site) 3 html = r.read() 4 parser = "html.parser" 5 sp = BeautifulSoup(html, parser) 6 for tag in sp. nd_all("a"): 7 url = tag.get("href") 8 if url is None : 9 continue 10 if "html" in url: 11 print("\n" + url) Метод nd_all возвращает итерируемый объект, который содержит най- денные методом объекты tag. При каждом прохождении цикла for перемен- ной tag присваивается значение нового объекта Tag. У каждого объекта Tag 175 Введение в инструменты программирования есть много переменных экземпляра, но вам нужно значение лишь переменной href , содержащий все URL-адреса. Его можно получить путем вызова метода get и передачи в него значения "href" в качестве параметра. Наконец, вы про- веряете, содержит ли переменная url данные; содержит ли она строку "html" (вам не нужно выводить внутренние ссылки), если содержит, вы выводите ее. Ниже показан полный код парсера контента. Python_ex288.py 1 import urllib.request 2 from bs4 import BeautifulSoup 3 class Scraper: 4 def __init__( self , site): 5 self .site = site 6 def scrape( self ): 7 r = urllib.request.urlopen( self .site) 8 html = r.read() 9 parser = "html.parser" 10 sp = BeautifulSoup(html, parser) 11 for tag in sp. nd_all("a"): 12 url = tag.get("href") 13 if url is None : 14 continue 15 if "html" in url: 16 print("\n" + url) 17 news = "https://news.google.ru/" 18 Scraper(news).scrape() Когда вы запустите программу, вывод будет выглядеть примерно так: >> https://regnum.ru/russian/fd-south/crimea/alushta.html >> https://www.gazeta.ru/business/2017/11/01/10965968.shtml >> https://www.segodnya.ua/regions/krym/v-okkupirovannom-kry- mu-zayavili-o-diversii-vzorvan-gazoprovod-1068509.html >> http://www.vesti.ru/doc.html?id=2949606&cid=9 Теперь, когда вы умеете собирать ссылки с сайта Google Новости, ваши возмож- ности безграничны. Вы можете написать программу, которая будет анализировать наиболее часто употребляемые в заголовках слова. Можете разработать программу, которая будет оценивать «настроения» заголовков, чтобы узнать, существует ли кор- реляция с фондовым рынком. Благодаря парсингу контента вам доступна информа- ция со всего мира, и, я надеюсь, что вас это поражает так же сильно, как и меня. Словарь терминов HTML: язык, который организовывает структуру сайта. HTML-элемент: аналогичен ключевому слову в программировании — инструк- тирует браузер сделать что-либо. Парсер веб-контента: программа, извлекающая данные с веб-сайта. Парсинг: парсинг означает принятие формата вроде HTML и придание ему структуры при помощи языка программирования. К примеру, превращение дан- ных в объект. Практикум Модифицируйте ваш парсер контента так, чтобы он сохранял найденные ссыл- ки в файл. Решение: challenge_1.py. 177 Глава 21. Структуры данных Вообще-то я утверждаю, что разница между плохим программистом и хорошим закл ючается в том, что именно он считает более важным — свой код или свои структуры данных. Плохие программисты беспокоятся о коде. Хорошие программисты беспокоятся о структурах данных и их отношениях. Линус Торвальдс Структуры данных Структура данных — это формат, используемый для хранения и организации информации. Структуры данных — фундаментальное понятие в программирова- нии, они встроены в большинство языков. Вы уже знаете, как пользоваться не- которыми встроенными в Python структурами данных — списками, кортежами и словарями . В этой главе вы научитесь создавать еще две структуры данных — сте- ки и очереди. Стеки Стек — это структура данных. Как и в список, в стек можно добавлять элементы и удалять их из него, но в отличие от списка, вы можете добавлять и удалять толь- ко последний элемент. Если у вас есть список [1, 2, 3], вы можете удалить из него любой элемент. Если у вас есть такой же стек, вы можете удалить лишь по- следний элемент, 3, и тогда стек будет иметь вид [1, 2]. Теперь можно удалить 2 , а после этого — 1, и стек станет пустым. Удаление элемента из стека обознача- ется термином pop. Если вы поместите 1 обратно в стек, он будет иметь вид [1], а если затем поместите двойку, то [1, 2]. Добавление элемента в стек обозна- чается словом push. Такой вид структуры данных, при котором последний поме- ЧАСТЬ IV Введение в информатику 178 Часть IV щенный элемент является первым извлекаемым, называется структурой данных «последним вошел — первым вышел» (LIFO, last-in-first-out). LIFO можно представить как стопку тарелок. Если вы сложите пять тарелок одну на другую и захотите вытащить ту, что в самом низу, вам сначала придется убрать все верхние. Каждый фрагмент данных в стеке сродни тарелке, чтобы по- лучить к нему доступ, нужно избавиться от данных сверху. В этом разделе вы построите стек. В Python есть библиотека с обеими струк- турами данных, описываемыми в этой главе, но чтобы понять, как они работают, нужно создать их самостоятельно. У стека будет пять методов: is_empty, push, pop , peek и size. Метод is_empty возвращает значение True если ваш стек пуст, и False в противном случае. Метод push добавляет элемент на «вершину» стека, pop удаляет и возвращает элемент с «вершины» стека, peek возвращает этот эле- мент, но не удаляет его. Метод size возвращает целое число, представляющее ко- личество элементов в стеке. Вот как выглядит реализация стека в Python: Python_ex289.py 1 class Stack: 2 def __init__( self ): 3 self .items = [] 4 def is_empty( self ): 5 return self .items == [] 6 def push( self , item): 7 self .items.append(item) 8 def pop( self ): 9 return self .items.pop() 10 def peek( self ): 11 last = len( self .items) -1 12 return self .items[last] 13 def size( self ): 14 return len( self .items) Если вы создадите новый стек, он будет пуст, и метод is_empty вернет зна- чение True. Python_ex290.py 1 stack = Stack() 2 print(stack.is_empty()) >> True 179 Введение в информатику Если вы добавите новый элемент в стек, метод is_empty вернет значение False Python_ex291.py 1 stack = Stack() 2 stack.push(1) 3 print(stack.is_empty()) >> False Вызовите метод pop, чтобы удалить элемент из стека, тогда метод is_empty снова вернет значение True. Python_ex292.py 1 stack = Stack() 2 stack.push(1) 3 item = stack.pop() 4 print(item) 5 print(stack.is_empty()) >> 1 >> True Наконец, вы можете заглянуть в содержимое стека и узнать его размер. Python_ex293.py 1 stack = Stack() 2 for i in range(0, 6): 3 stack.push(i) 4 print(stack.peek()) 5 print(stack.size()) >> 5 >> 6 Изменение порядка символов строки при помощи стека Стек может изменять направление итерируемого элемента на обратное, по- скольку все, что вы поместите в стек, отделяется в обратном порядке. В этом разделе вы решите часто задаваемую на собеседованиях задачу — изменение по- рядка следования символов в строке с помощью стека путем помещения строки в стек и дальнейшего ее извлечения. 180 Часть IV Python_ex294.py 1 class Stack: 2 def __init__( self ): 3 self .items = [] 4 def is_empty( self ): 5 return self .items == [] 6 def push( self , item): 7 self .items.append(item) 8 def pop( self ): 9 return self .items.pop() 10 def peek( self ): 11 last = len( self .items) -1 12 return self .items[last] 13 def size( self ): 14 return len( self .items) 15 stack = Stack() 16 for c in "": 17 stack.push(c) 18 reverse = "" 19 for i in range(len(stack.items)): 20 reverse += stack.pop() 21 print(reverse) >> Сначала вы прошли через каждый символ строки "" и поместили его в стек. Затем вы перебрали стек, взяли каждый элемент из стека и поместили в пере- менную reverse. Как только итерирование завершилось, буквы в исходном слове расположились в обратном порядке, и программа вывела строку . Очереди Очередь — еще одна структура данных. Она тоже похожа на список; вы може- те добавлять в нее элементы и удалять их. Очередь также напоминает стек, по- скольку добавлять и удалять элементы можно лишь в определенном порядке. В отличие от стека, где первый помещенный элемент является последним извле- каемым, очередь представляет собой структуру данных вида «первым вошел — 181 Введение в информатику первым вышел» (FIFO , first-in-first-out): первый помещенный элемент является первым извлекаемым. Представьте FIFO как очередь людей, желающих купить билеты в кино. Пер- вый человек в очереди — это первый человек, который купит билеты, второй че- ловек в очереди — второй, кто купит билеты, и так далее. В этом разделе вы построите очередь, используя четыре метода: enqueue, dequeue , is_empty и size. enqueue добавляет новый элемент в очередь; dequeue удаляет элемент из очереди; is_empty проверяет, пуста ли очередь, и возвращает со- ответственно True либо False; size возвращает количество элементов в очереди. Python_ex295.py 1 class Queue: 2 def __init__( self ): 3 self .items = [] 4 def is_empty( self ): 5 return self .items == [] 6 def enqueue(self, item): 7 self .items.insert(0, item) 8 def dequeue( self ): 9 return self .items.pop() 10 def size( self ): 11 return len( self .items) Если вы создадите новую пустую очередь, метод is_empty вернет значение True Python_ex296.py 1 a_queue = Queue() 2 print(a_queue.is_empty()) >> True Добавьте в очередь элементы и узнайте ее размер. Python_ex297.py 1 a_queue = Queue() 2 for i in range(5): 3 a_queue.enqueue(i) 4 print(a_queue.size()) >> 5 182 Часть IV Удалите каждый элемент из очереди. Python_ex298.py 1 a_queue = Queue() 2 for i in range(5): 3 a_queue.enqueue(i) 4 for i in range(5): 5 print(a_queue.dequeue()) 6 print() 7 print(a_queue.size()) >> 0 >> 1 >> 2 >> 3 >> 4 >> >> 0 Очередь за билетами При помощи очереди можно смоделировать ситуацию, когда люди выстроились в ряд и ждут, чтобы купить билеты на фильм. Python_ex299.py 1 import time 2 import random 3 class Queue: 4 def __init__( self ): 5 self .items = [] 6 def is_empty( self ): 7 return self .items == [] 8 def enqueue( self , item): 9 self .items.insert(0, item) 10 def dequeue( self ): 11 return self .items.pop() 183 Введение в информатику 12 def size( self ): 13 return len( self .items) 14 def simulate_line( self , till_show, max_time): 15 pq = Queue() 16 tix_sold = [] 17 for i in range(10): 18 pq.enqueue(" " + str(i)) 19 t_end = time.time() + till_show 20 now = time.time() 21 while now < t_end and not pq.is_empty(): 22 now = time.time() 23 r = random.randint(0, max_time) 24 time.sleep(r) 25 person = pq.dequeue() 26 print(person) 27 tix_sold.append(person) 28 return tix_sold 29 queue = Queue() 30 sold = queue.simulate_line(5, 1) 31 print(sold) >> 0 >> [' 0', ' 1', ' 2', ... Вначале вы создали функцию simulate_line, которая моделирует прода- жу билетов людям в очереди. Функция принимает два параметра: till_show и max_time . Первый параметр является целым числом, представляющим число секунд, оставшихся до того момента, когда начнется фильм и уже не останется времени на покупку билетов. Второй параметр также является целым числом, представляющим максимальное количество времени в секундах, которое уходит на покупку билета одним человеком. Внутри функции вы создаете новую пустую очередь и пустой список. Список будет отслеживать людей, купивших билет. Затем вы заполняете очередь одной сотней строк, начиная с " 0" и заканчивая " 99". Каж- дая строка в очереди представляет человека в очереди, желающего купить билет. Во встроенном модуле time есть функция time. Она возвращает число с пла- вающей точкой, представляющее число секунд, прошедших с начала эпохи, мо- мента во времени (1 января 1970 года), который используется в качестве ссылки. 184 Часть IV Если я вызову функцию time прямо сейчас, она вернет 1481849664.256039 — число секунд, прошедших с начала эпохи. Если спустя одну секунду я снова вызову эту функцию, число с плавающей точкой, возвращаемое ей, будет увеличено на 1. Переменная t_end находит результат сложения значения функции time и числа секунд, переданного в переменной till_show. Этот результат обозначает момент в будущем. Цикл while будет продолжать итерирование до тех пор, пока либо функция time не вернет значение, большее, чем значение t_end, либо пока очередь не станет пустой. После этого вы используете функцию sleep из встроенного модуля time, чтобы случайное количество секунд между 0 и max_time интерпретатор Python ничего не делал. Вы указываете Python остановить выполнение кода, чтобы сы- митировать время, требующееся на продажу билета, а случайное количество времени используется для имитации того, что на каждую продажу билета уходит разное время. После паузы, вызванной функцией sleep, вы извлекаете из очереди строку, представляющую человека, и помещаете ее в список tix_sold, который пред- ставляет человека, уже купившего билет. Результатом этого кода будет функция, которая умеет продавать билеты лю- дям в очереди, продавая их больше или меньше в зависимости от переданных параметров и случайного значения. Словарь терминов FIFO: сокращение от англ. first-in-first-out — «первым вошел — первым вышел». LIFO: сокращение от англ. last-in-first-out — «последним вошел — первым вышел». Pop: удаление элемента из стека. Push: добавление элемента в стек. Очередь: структура данных вида «первым вошел — первым вышел». Стек: структура данных вида «последним вошел — первым вышел». Структура данных «первым вошел — первым вышел»: структура данных, в ко- торой первый помещенный элемент является первым извлекаемым. Структура данных «последним вошел — первым вышел»: вид структуры дан- ных, при котором последний помещенный элемент является первым извлекае- мым. Структура данных: формат, используемый для хранения и организации инфор- мации. Эпох а: момент во времени, используемый в роли ссылки. Практикум 1. Измените порядок символов строки " B " при помощи стека. 2. Используйте стек, чтобы создать новый список с элементами списка [1, 2, 3, 4, 5] в обратном порядке. Решения: challenge1.py и challenge2.py. |