Здравствуйте.
После посещения одной конференции у меня появилась идея, воплощение которой я и представляю.
Данный пост предоставляет пример работы с библиотеками grab и rdflib, а также готовый класс для выполнения SPARQL запросов к содержимому web-страниц.
С целью выполнения SPARQL запросов к html содержимому необходимо создать локальное rdf хранилище, и наполнить его информацией, полученной из html страницы посредством grab.
Для начала импортируем все необходимые библиотеки, а также зарегистрируем использование SPARQL плагина.
# -*- coding: utf-8 -*-
import grab
import rdflib
from rdflib import *
from rdflib import plugin
plugin.register(
'sparql', rdflib.query.Processor,
'rdfextras.sparql.processor', 'Processor')
plugin.register(
'sparql', rdflib.query.Result,
'rdfextras.sparql.query', 'SPARQLQueryResult')
Определим наш класс, и построим конструктор для него.
Конструктор может принимать url страницы, которую необходимо раcпарсить и поместить в хранилище, а также может определять нестандартное расположение определения пространства имён.
class SQtHD():
'''
sparql query to html documents
'''
def __init__(self,url=None,htmlNamespace='http://localhost/rdf/html#'):
'''
Constructor
'''
self.__grab__=grab.Grab()#Наш парсер
self.__storage__=Graph()#Наше хранилище
self.__namespace__=Namespace(htmlNamespace)#Создаем пространство имен
self.__storage__.bind('html', URIRef(htmlNamespace))#Задаем пространству имен короткое имя
self.__initnamespace__=dict(self.__storage__.namespace_manager.namespaces())
if url:#Если необходимо, то загружаем содержимое указанной страницы в хранилище.
self.__store__(url)
Далее необходимо определить служебную функцию для наполнения хранилища содержимым страницы.
def __store__(self,url):
self.__storage__.remove((None,None,None))#Очищаем хранилище
self.__grab__.go(url)#Выполняем переход по указанному адресу средствами grab
root=self.__grab__.tree.getroottree().getroot()
self.__parse__(root)#Парсим содрежимое страницы.
В локальное хранилище помещается следующая информация о элементах:
- Информация о типе элемента (какой html тэг)
- Информация о родительском элементе
- Информация о позиции элемента по отношению к братьям
- Информация о уровне вложенности элемента
- Тест содержащийся в элементе
- Информация о количестве дочерних элементов
- Ссылки на дочерние элементы
- Значение атрибутов для элемента
Следующая служебная функция рекурсивно проходит по дереву элементов собирая необходимую информацию и добавляя её в хранилище.
def __parse__(self,element,parent=None,children_position=None,children_level=0):
current_element=BNode()
children_elements=element.getchildren()
if str(element.tag)=='<built-in function Comment>':
self.__storage__.add((current_element, RDF.type,
self.__namespace__['comment']))
else:
self.__storage__.add((current_element, RDF.type,
self.__namespace__[element.tag]))
if not parent==None:
self.__storage__.add((current_element,self.__namespace__['parent'],parent))
self.__storage__.add((parent,self.__namespace__['children'],
current_element))
self.__storage__.add((current_element,self.__namespace__['children_position'],
Literal(children_position)))
self.__storage__.add((current_element,self.__namespace__['children_level'],
Literal(children_level)))
if element.text and len(element.text.strip())>0:
self.__storage__.add((current_element,self.__namespace__['text'],
Literal(element.text.strip())))
if element.text_content() and len(element.text_content().strip())>0:
self.__storage__.add((current_element,self.__namespace__['text_content'],
Literal(element.text_content().strip())))
self.__storage__.add((current_element,self.__namespace__['children_count'],
Literal(len(children_elements))))
for i in element.attrib:
self.__storage__.add((current_element,self.__namespace__[i],
Literal(element.attrib[i])))
for i in range(len(children_elements)):
self.__parse__(children_elements[i],current_element,i,children_level+1)
Данная функция выполняет SPARQL запрос к локальному хранилищу.
def executeQuery(self,query,url=None):
'''
execute query on storadge
'''
if url:#Если необходимо, то загружаем содержимое указанной страницы в хранилище.
self.__store__(url)
return self.__storage__.query(query,
initNs=self.__initnamespace__)#Возвращаем результат выполнения запроса.
Данная функция наполняет хранилище содержимым указанной страницы.
def loadStoradge(self,url):
'''
load and parse html page to local rdf storadge
'''
self.__store__(url)
И напоследок, несколько простых примеров запросов.
if __name__ == "__main__":
endPoint = SQtHD()#Создаем экземпляр класса SQtHD
endPoint.loadStoradge('http://habrahabr.ru')#Загружаем страницу в хранилище
print "All sources for images given by tag <img>:"#Вывести все уникальные адреса картинок
q=endPoint.executeQuery('SELECT DISTINCT ?src { ?a rdf:type html:img. ?a html:src ?src. }')
for row in q.result:
print row
print
print "All link urls:"#Вывести все уникальные адреса ссылок
q=endPoint.executeQuery('SELECT DISTINCT ?href { ?a rdf:type html:a. ?a html:href ?href. }')
for row in q.result:
print row
print
print "All class names for elements:"#Вывести все уникальные имена классов
q=endPoint.executeQuery('SELECT DISTINCT ?class { ?a html:class ?class. }')
for row in q.result:
print row
print
'''
print "All scripts (without loaded by src):"#Тест всех внутристраничных скриптов.
q=endPoint.executeQuery('SELECT ?text { ?a rdf:type html:script. ?a html:text ?text. }')
for row in q.result:
print row
print'''
print "All script srcs:"#Все ссылки на скрипты.
q=endPoint.executeQuery('SELECT ?src { ?a rdf:type html:script. ?a html:src ?src. }')
for row in q.result:
print row
print
Результат исполнения запроса на вывод всех ссылок на скрипты:
All script srcs:
/javascripts/1341931979/all.js
/javascripts/1341931979/_parts/posts.js
/javascripts/1341931979/_parts/to_top.js
/javascripts/1341931979/_parts/shortcuts.js
/javascripts/1341931979/libs/jquery.form.js
/javascripts/1341931979/facebook_reader.js
/js/1341931979/adriver.core.2.js
/javascripts/1341931979/libs/highlight.js
/javascripts/1341931979/hubs/all.js
/javascripts/1341931979/posts/all.js
Таким образом, существует 3 способа наполнить хранилище:
- При инициализации класса
- Посредством функции loadStoradge
- При каждом запросе к хранилищу
Использовать данный инструмент предполагается для превращения информации с сайтов, которые не предоставляют её в структурированном виде (rdf-тройки, xml, json), в понятный «машинам» вид.
GIST проекта, содержит также определение пространства имен посредством xml. Пространство имен определяет, что является тегом, закладывает необходимые свойства и отношения а также определяет тэги html 4.
Рекомендуемая литература:
«Programming the Semantic Web» By Toby Segaran, Colin Evans, Jamie Taylor
Автор: rndr