Здравствуйте, дорогие друзья! Хочу поделиться наработкой (время от времени на форуме всплывают темы по парсированию, - может кому будет полезно)
Предыстория: Увлекаюсь в последнее время python, и недавно поставили передо мной задачу "генерации" списка номеров операторов мобильной связи, которые относятся к определенной области.
Описание: В данном посте предлагаю соответствующее решение, которое представляет собой скрипт на python, выполняющий последовательный парсинг информации с ресурса codificator.ru. Программа работает в два этапа: сперва по заданному пользователем ключевому слову выполняется поиск по странице, на которой выведен перечень префиксов, зарегистрированных для разных операторов, а затем выполняется последовательный перебор найденных префиксов и выписывание соответствующих данных.
Сам скрипт:
#!/usr/bin/env python3
# coding=utf8
# подключаемые библиотеки:
from urllib2 import urlopen #работа с сетью
from bs4 import BeautifulSoup #работа с DOM
import argparse #парсирование аргументов из командной строки и работа с системой
import sys #для адекватной работы с кодировками в данном случае
import codecs #работа с файлами
import os #работа с операционной системой и выполнение команд bash
from itertools import groupby #для удаления дубликатов
# Простейший целевой парсер web-ресурса на языке Python
# Функционал: выполняет сперва парсирование перечня кодов (902, 921 и т.п.), которые зарегистрированы
# для определенной области, а затем выписывает по данным кодам информацию о зарегистрированных телефонных
# номерах конкретных операторов в рассматриваемой области. Результаты оформляет таблицей в формате html,
# которая сохранятеся в домашней папке текущего пользователя.
# Дополнительно: понимает один URL в виде аргумента командной строки
# Зависимости: пакет python-beautifulsoup
# Работа проверялась под KUbuntu 18.04, Python 2.7.15rc1
#Процедура парсирования перечня префиксов мобильных операторов с поиском ключевого слова в каждом из них:
def pars_by_keyword(mykeyword,PrefList):
finList=[] #список, куда будем сохранять данные
finList.append(['<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><table border="1">','','','',''])
finList.append(['<tr><td>Оператор</td><td>Регион</td><td>Диапазон</td><td>Вид номеров</td><td>Количество номеров</td></tr>','','','',''])
for prefix in PrefList:
url = "https://codificator.ru/code/mobile/"+str(prefix) #вычисляем адрес
response = urlopen(url) #выполняем запрос на сервер
html = response.read() #читаем полученный ответ
soup = BeautifulSoup(html,"lxml") #по сути, интерпретируем ответ, формируя коллекцию DOM-элементов
OurTable = soup.find_all('table')[0] #берем первую таблицу в коллекции
print "Парсирование префикса "+str(prefix)+" ["+url+"]"
#поиск индекса столбика с регионом:
i_m=1000
for i in range(0,4):
if 'Регион' in OurTable.find_all('tr')[0].find_all('th')[i].string:
i_m=i
if (i_m != 1000):
for curString in OurTable.find_all('tr'):
stro1=mykeyword.upper().lower()
try:
stro2=curString.find_all('td')[i_m].string.upper().lower()
except:
stro2=""
if stro1 in stro2:
aL=[]
if len(curString.find_all('td')) == 4:
aL.append("<td>---</td>")
for zCell in curString.find_all('td'):
aL.append(zCell)
finList.append(['<tr>','','','',''])
finList.append(aL)
finList.append(['</tr>','','','',''])
else:
print "Не найдено поле [Регион] в таблице"
finList.append(['</table></body></html>','','','',''])
print "Информация спарсирована. Выполняется сохранение файла..."
#сохранение результата в файл:
fname=os.getenv('HOME')+'/'+mykeyword+".html" #вычисляем имя файла
fp = codecs.open(fname, 'w', encoding='utf-8') #открываем файл для записи
for curString in finList: #построчное заполнение файла
fp.write(unicode(curString[0]) + "" + unicode(curString[1]) + "" + unicode(curString[2])+ "" + unicode(curString[3]) + "" + unicode(curString[4]) + '\n')
fp.close() #закрытие файла
print 'Результат сохранен в файл: ['+fname+']'
bashCommand = 'firefox "'+fname+'"'
os.system(bashCommand) #открытие файла в firefox
def pars_codes(mykeyword):
url='https://codificator.ru/code/mobile/regions.html'
response = urlopen(url) #выполняем запрос на сервер
html = response.read() #читаем полученный ответ
soup = BeautifulSoup(html,"lxml") #по сути, интерпретируем ответ, формируя коллекцию DOM-элементов
OurTable = soup.find_all('table')[0] #берем первую таблицу в коллекции
finList=[]
found=0
for curString in OurTable.find_all('tr'):
if ( len(curString.find_all('td')) == 4 ) and ( found == 1):
break
if ( len(curString.find_all('td')) == 4 ):
if mykeyword.lower() in curString.find_all('td')[0].string.lower():
found=1 #нашли упоминание!
if ( found == 1 ):
if ( len(curString.find_all('td')) == 3 ):
finList.append(curString.find_all('td')[0].string)
if ( len(curString.find_all('td')) == 4 ):
finList.append(curString.find_all('td')[1].string)
lastList = [el for el, _ in groupby(finList)] #удаление дубликатов
print 'Найдено префиксов для области: '+unicode(len(finList))
return lastList
#Сама программа (разбираем аргументы, и вызываем парсер):
def main():
reload(sys)
sys.setdefaultencoding('utf8')
callWith=argparse.ArgumentParser() #контейнер для приема аргументов из командной строки
callWith.add_argument('--obl', help='Область (ключевое слово для поиска)') #единственный принимаемый аргумент
args = callWith.parse_args() #вытаскиваем аргумент из командной строки
if (args.obl == None): #если аргуент не задан. В примере - навигация на строку по-умолчанию
PrefList=pars_codes("алтай")
if ( len(PrefList) > 0 ):
pars_by_keyword("алтай",PrefList)
else:
print "!!! Не найдено ни одного префикса по ключевому слову. Список не будет сгенерирован."
else: #если аргумент задан. Воспринимаем его за ключевое слово для поиска
print 'Введено ключевое слово: '+args.obl
PrefList=pars_codes(args.obl)
if ( len(PrefList) > 0 ):
pars_by_keyword(args.obl,PrefList)
else:
print "!!! Не найдено ни одного префикса по ключевому слову. Список не будет сгенерирован."
#вызов главной процедуры
if __name__ == '__main__':
main()
Принцип работы со скриптом: Для совсем не знакомых с процессом, поясняю: выделяем приведенный исходный код, категорически копируем и вставляем в открытое в фоне окно любимого текстового редактора. Далее сохраняем полученный текст в файл, скажем, /home/имя_пользователя/программа.py. В приведенном имени под имя_пользователя понимается имя текущего пользователя, который авторизован в системе в данный момент времени. После выполненных манипуляций окно текстового редактора закрываем, открываем терминал (ctrl+alt+t), в терминале выполняем:
python $HOME/программа.py
В процессе выполнения в окне терминала будем видеть лог выполнения. Пример такого лога по-дефолту приведен под спойлером.
Найдено префиксов для области: 30
Парсирование префикса 901 [https://codificator.ru/code/mobile/901]
Парсирование префикса 902 [https://codificator.ru/code/mobile/902]
Парсирование префикса 903 [https://codificator.ru/code/mobile/903]
Парсирование префикса 905 [https://codificator.ru/code/mobile/905]
Парсирование префикса 906 [https://codificator.ru/code/mobile/906]
Парсирование префикса 909 [https://codificator.ru/code/mobile/909]
Парсирование префикса 913 [https://codificator.ru/code/mobile/913]
Парсирование префикса 923 [https://codificator.ru/code/mobile/923]
Парсирование префикса 929 [https://codificator.ru/code/mobile/929]
Парсирование префикса 933 [https://codificator.ru/code/mobile/933]
Парсирование префикса 952 [https://codificator.ru/code/mobile/952]
Парсирование префикса 953 [https://codificator.ru/code/mobile/953]
Парсирование префикса 958 [https://codificator.ru/code/mobile/958]
Парсирование префикса 960 [https://codificator.ru/code/mobile/960]
Парсирование префикса 961 [https://codificator.ru/code/mobile/961]
Парсирование префикса 962 [https://codificator.ru/code/mobile/962]
Парсирование префикса 963 [https://codificator.ru/code/mobile/963]
Парсирование префикса 964 [https://codificator.ru/code/mobile/964]
Парсирование префикса 967 [https://codificator.ru/code/mobile/967]
Парсирование префикса 983 [https://codificator.ru/code/mobile/983]
Парсирование префикса 991 [https://codificator.ru/code/mobile/991]
Парсирование префикса 995 [https://codificator.ru/code/mobile/995]
Парсирование префикса 996 [https://codificator.ru/code/mobile/996]
Парсирование префикса 999 [https://codificator.ru/code/mobile/999]
Информация спарсирована. Выполняется сохранение файла...
Результат сохранен в файл: [/home/zg_nico/алтай.html]
После окончания выполнения скрипта будет открыто окно браузера firefox (входит в стандартную поставку ubuntu, поэтому в скрипте "прибит гвоздями"), в котором будет загружена сгенерированная скриптом таблица. Для выполнения поиска по ключевому слову следует запустить скрипт с указанием параметра --obl, например таким образом:
python $HOME/программа.py --obl карелия
Если указанное ключевое слово не было найдено при парсировании, будет сгенерирован соответствующий лог, и выходной файл получен не будет:
>>> python Парсинг_диапазонов_сотовых_операторов.py --obl ввожу_фигню
Найдено префиксов для области: 0
!!! Не найдено ни одного префикса по ключевому слову. Список не будет сгенерирован.
Вниманию пользователей: В скрипте на текущий момент времени не предусмотрено никакой проверки вводимого ключевого слова, - оно напрямую передается внутренним процедурам скрипта, что не правильно, но вместе с тем скрипт в рабочем состоянии, и со своей задачей справляется. Экранирование пробелов тоже не предусмотрено. Чтобы выполнить поиск по запросу "Мурманская обл", следует в качестве параметра obj указать либо "Мурманская обл" (в кавычках) либо Мурманская\ обл (с заэкранированным обратным слэшем пробелом):
python $HOME/программа.py --obl "Мурманская обл"
python $HOME/программа.py --obl мурманская\ обл