[Data Mining, Big Data, Natural Language Processing] Парсим Википедию, фильтруя, для задач NLP в 44 строки кода
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В этой заметке я хотел бы дополнить эту статью и рассказать, как можно гибче использовать экстрактор Википедии WikiExtractor, фильтруя статьи по категориям.
Началось все с того, что мне нужны были определения для различных терминов. Термины и их определения, как правило, являются первым предложением на каждой странице Википедии. Пойдя по самому простому пути, я извлек все статьи и регулярками быстро выцепил все, что было нужно. Проблема в том, что объем определений перевалил за 500 Мб, причем, было слишком много лишнего, например, именованные сущности, города, годы и т.д. которые мне не нужны.
Я верно предположил, что у инструмента WikiExtractor (я буду использовать другую версию, ссылка будет ниже) есть какой-то фильтр и это оказался фильтр по категориям. Категории являются тегами для статей, которые имеют иерархическую структуру для организации страниц. Я на радостях выставил категорию "Точные науки", очень наивно полагая, что все статьи, которые относятся к точным наукам будут включены в список, но чуда не случилось — у каждой страницы свой, крошечный, набор категорий и на отдельно взятой странице нет никакой информации о том, как эти категории соотносятся. Значит, если мне нужны страницы по точным наукам, я должен указать все категории, которые являются потомками для "Точных наук".
Ну не беда, сейчас найду сервис, подумал я, который запросто мне отгрузит все категории от заданного начала. К сожалению, я нашел только это, где можно просто посмотреть, как эти категории взаимосвязаны. Попытка в ручную перебрать категории тоже не увенчалась успехом, зато я "обрадовался" тому, что эти категории имеют структуру не дерева, как я думал все это время, а просто направленного графа, с циклами. Причем, сама иерархия очень сильно плывет — скажу наперед, что задав начальную точку "Математика", легко можно дойти до Александра I. В итоге, мне оставалось только восстановить этот граф локально и как-то получить список интересующих меня категорий.
Итак, задача ставится следующим образом: начиная с какой-то вершины, получить список всех категорий, которые связаны с это вершиной, имея возможность как-то ограничивать их.
Работа проводилась на машине с Ubuntu 16.04, но, полагаю, что для 18.04 следующие инструкции не вызовут проблем.
Скачиваем и развертываем данные
Первым делом, нам необходимо скачать все необходимые данные вот отсюда, а именно
- ruwiki-latest-pages-articles.xml.bz2
- ruwiki-latest-categorylinks.sql.gz
- ruwiki-latest-category.sql.gz
- ruwiki-latest-page.sql.gz
Таблица categorylinks содержит связи между страницей, в смысле Википедии, и ссылкой на категорию вида [[Category:Title]] в любом месте этой страницы, информация. Нас интересуют столбцы cl_from, которая содержит id страницы, и cl_to, которая содержит название категории. Для того, чтобы связать id страницы, нам нужна таблица page (информация) со столбцами page_id и page_title. Но нам не нужно знать взаимосвязь всех страниц, мы хотим только категории. Все категории, или их большинство, как я понял, имеют свою страницу, значит нам нужен перечень всех категорий, чтобы фильтровать названия страниц. Эта информацию содержится в таблице category([информация](category table)) в столбце cat_title. Файл pages-articles.xml содержит текст самих статей.
Для работы с базами данных нам необходим mysql. Установить его можно, выполнив команду
sudo apt-get install mysql-server mysql-client
После этого, необходимо зайти в mysql и создать там базы данных, для того чтобы импортировать базы данных Википедии.
$ mysql -u username -p
mysql> create database category;
mysql> create database categorylinks;
mysql> create database page;
Создав базы данных, приступим к импорту. Он может занять весьма продолжительное время.
$ mysql -u username -p category < ruwiki-latest-category.sql
$ mysql -u username -p categorylinks < ruwiki-latest-categorylinks.sql
$ mysql -u username -p page < ruwiki-latest-page.sql
Формируем таблицу взаимосвязи категорий и восстанавливаем граф
Теперь нам нужно получить таблицу, в которой будет отражено как между собой связаны категории и для дальнейшей работы выгрузить таблицу в csv. Сделать это можно следующим запросом
mysql> select page_title, cl_to from categorylinks.categorylinks join page.page
on cl_from = page_id where page_title in (select cat_title from category) INTO outfile '/var/lib/mysql-files/category.csv' FIELDS terminated by ';' enclosed by '"' lines terminated by '\n';
Результат будет выглядеть следующим образом. Не забудьте вручную добавить название столбцов.
Стоит заметить, что слева у нас потомок, а справа — его предки, поэтому восстанавливать граф будем от потомков к предкам. Кроме того, есть еще очень много разных служебных категорий, которые мне лично не нужны, поэтому я их отфилтровал, сократив количество строк с примерно 1,6 миллионов до 1,1. Сделать все это можно при помощи следующего кода.
import pandas as pd
import networkx as nx
from tqdm.auto import tqdm, trange
#Filtering
df = pd.read_csv("category.csv", sep=";", error_bad_lines=False)
df = df.dropna()
df_filtered = df[df.parant.str.contains("[А-Яа-я]+:") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Страницы,_") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Статьи_проекта_") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Хорошие_статьи") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Перенаправления,_") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Избранные_списки_") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Избранные_статьи_") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Списки_проекта") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Добротные_статьи_") != True]
df_filtered = df_filtered[df_filtered.parant.str.contains("Статьи") != True]
# Graph recovering
G = nx.DiGraph()
c = 0
for i, gr in tqdm(df_filtered.groupby('child')):
vertex = set()
edges = []
for i, r in gr.iterrows():
G.add_node(r.parant, color="white")
G.add_node(r.child, color="white")
G.add_edge(r.parant, r.child)
Работаем с графом и извлекаем фильтрованные статьи
Для того, чтобы пользоваться этим графом и решить поставленную в начале задачу, воспользуемся алгоритмом поиска в глубину с модификациями для обнаружения циклов, для чего мы пометили каждый узел белым цветом, и ограничения глубины поиска.
counter = 0
nodes = []
def dfs(G, node, max_depth):
global nodes, counter
G.nodes[node]['color'] = 'gray'
nodes.append(node)
counter += 1
if counter == max_depth:
counter -= 1
return
for v in G.successors(node):
if G.nodes[v]['color'] == 'white':
dfs(G, v, max_depth)
elif G.nodes[v]['color'] == 'gray':
continue
counter -= 1
В результате, в листе nodes у нас содержатся все категории начиная от указаной и до желаемой глубины от начала. Ниже представлен пример для начальной точки "Точные науки" с ограничением на глубину в 5 вершин. Всего их получилось около 2500 тысяч. Конечно, там содержатся категории, которые не относятся к точным наукам и, возможно, каких-то категорий, которые должны быть, там не окажутся, но с этим способом лучше не выйдет — либо больше покрытие и больше ненужных категорий, либо наоборот. Однако, это гораздо лучше, чем вручную отбирать эти категории.
Результат нужно сохранить построчно в файл, он нам понадобится для фильтарции.
Подкатегории с вершины Точные науки
SPL
Точные_науки
Информатика
CAM
Авторы_учебников_информатики
Архивное_дело
Археографические_комиссии
Археографические_комиссии_Украины
Виленская_археографическая_комиссия
Архивисты
Архивариусы
Архивисты_по_алфавиту
Архивисты_по_векам
Архивисты_по_странам
Архивное_дело_на_Украине
Архивисты_Украины
...
Терминология_телевидения
Терминология_японских_боевых_искусств
Термины_для_знаменитостей
Термины_и_понятия_аниме_и_манги
Технические_термины
Транспортная_терминология
Фантастические_термины_по_их_изобретателям
Филателистические_термины
Философские_термины
Цирковые_термины
Экономические_термины
Японские_исторические_термины
Экономика_знаний
Инкапсуляция_(программирование)
...
Бесконечность
Бесконечные_графы
Единое
Философы_математики
Прокл_Диадох
Функции
Арифметические_функции
Мультипликативные_функции
Большие_числа
Кусочно-линейные_функции
Преобразования
Дискретные_преобразования
Интегральные_преобразования
Преобразования_пространства
Теория_потенциала
Типы_функций
Числа
Для того, чтобы применить эти категории для фильтрации для русского языка, однако, нужно кое-что подправить в исходниках. Я использовал эту версию. Сейчас там что-то новое, возможно, исправления ниже уже не актуальны. В файле WikiExtractor.py нужно заменить "Category" на "Категория" в двух местах. Области с уже исправленным вариантом представлены ниже:
tagRE = re.compile(r'(.*?)<(/?\w+)[^>]*?>(?:([^<]*)(<.*?>)?)?')
# 1 2 3 4
keyRE = re.compile(r'key="(\d*)"')
catRE = re.compile(r'\[\[Категория:([^\|]+).*\]\].*') # capture the category name [[Category:Category name|Sortkey]]"
def load_templates(file, output_file=None):
...
if inText:
page.append(line)
# extract categories
if line.lstrip().startswith('[[Категория:'):
mCat = catRE.search(line)
if mCat:
catSet.add(mCat.group(1))
После этого нужно запустить команду
python WikiExtractor.py --filter_category categories --output wiki_filtered ruwiki-latest-pages-articles.xml
где categories — это файл с категорями. Отфильтрованные статьи будут лежать в wiki_filtered.
На этом все. Спасибо за внимание.
===========
Источник:
habr.com
===========
Похожие новости:
- [Big Data, Информационная безопасность, Исследования и прогнозы в IT, Хранение данных] Новый tech – новая этика. Исследование отношения людей к технологиям и приватности
- [Data Mining, Анализ и проектирование систем, Машинное обучение] О котах и Process Mining
- [Big Data, Анализ и проектирование систем, Визуализация данных, Математика] Большие ошибки в больших данных: проблемы анализа на практике
- [Big Data, Машинное обучение] Минимизируем наложение лейблов в интерактивных визуализациях (перевод)
- [Big Data, Аналитика мобильных приложений] Как сервис BigQuery помогает интернет-маркетологу: несколько приёмов с SQL и визуализация отчётов в Google Data Studio
- [Python, Big Data, Хранение данных, Data Engineering] Apache Airflow: делаем ETL проще
- [Big Data] Цифровые двойники: почему все о них говорят и всем ли они нужны?
- [Big Data, SQL, Высокая производительность, Системное администрирование, Хранилища данных] Теория и практика использования ClickHouse в реальных приложениях. Александр Зайцев
- [Big Data, Data Mining, Визуализация данных, Открытые данные] Аномалии общероссийского голосования по поправкам к Конституции России. Часть 1
- [Машинное обучение, Программирование] Нейронки «с нуля», или Как мы делали помощника для наших диспетчеров техподдержки
Теги для поиска: #_data_mining, #_big_data, #_natural_language_processing, #_wikipedia, #_parsing, #_data_mining, #_data_mining, #_big_data, #_natural_language_processing
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 06:22
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В этой заметке я хотел бы дополнить эту статью и рассказать, как можно гибче использовать экстрактор Википедии WikiExtractor, фильтруя статьи по категориям. Началось все с того, что мне нужны были определения для различных терминов. Термины и их определения, как правило, являются первым предложением на каждой странице Википедии. Пойдя по самому простому пути, я извлек все статьи и регулярками быстро выцепил все, что было нужно. Проблема в том, что объем определений перевалил за 500 Мб, причем, было слишком много лишнего, например, именованные сущности, города, годы и т.д. которые мне не нужны. Я верно предположил, что у инструмента WikiExtractor (я буду использовать другую версию, ссылка будет ниже) есть какой-то фильтр и это оказался фильтр по категориям. Категории являются тегами для статей, которые имеют иерархическую структуру для организации страниц. Я на радостях выставил категорию "Точные науки", очень наивно полагая, что все статьи, которые относятся к точным наукам будут включены в список, но чуда не случилось — у каждой страницы свой, крошечный, набор категорий и на отдельно взятой странице нет никакой информации о том, как эти категории соотносятся. Значит, если мне нужны страницы по точным наукам, я должен указать все категории, которые являются потомками для "Точных наук". Ну не беда, сейчас найду сервис, подумал я, который запросто мне отгрузит все категории от заданного начала. К сожалению, я нашел только это, где можно просто посмотреть, как эти категории взаимосвязаны. Попытка в ручную перебрать категории тоже не увенчалась успехом, зато я "обрадовался" тому, что эти категории имеют структуру не дерева, как я думал все это время, а просто направленного графа, с циклами. Причем, сама иерархия очень сильно плывет — скажу наперед, что задав начальную точку "Математика", легко можно дойти до Александра I. В итоге, мне оставалось только восстановить этот граф локально и как-то получить список интересующих меня категорий. Итак, задача ставится следующим образом: начиная с какой-то вершины, получить список всех категорий, которые связаны с это вершиной, имея возможность как-то ограничивать их. Работа проводилась на машине с Ubuntu 16.04, но, полагаю, что для 18.04 следующие инструкции не вызовут проблем. Скачиваем и развертываем данные Первым делом, нам необходимо скачать все необходимые данные вот отсюда, а именно
Таблица categorylinks содержит связи между страницей, в смысле Википедии, и ссылкой на категорию вида [[Category:Title]] в любом месте этой страницы, информация. Нас интересуют столбцы cl_from, которая содержит id страницы, и cl_to, которая содержит название категории. Для того, чтобы связать id страницы, нам нужна таблица page (информация) со столбцами page_id и page_title. Но нам не нужно знать взаимосвязь всех страниц, мы хотим только категории. Все категории, или их большинство, как я понял, имеют свою страницу, значит нам нужен перечень всех категорий, чтобы фильтровать названия страниц. Эта информацию содержится в таблице category([информация](category table)) в столбце cat_title. Файл pages-articles.xml содержит текст самих статей. Для работы с базами данных нам необходим mysql. Установить его можно, выполнив команду sudo apt-get install mysql-server mysql-client
После этого, необходимо зайти в mysql и создать там базы данных, для того чтобы импортировать базы данных Википедии. $ mysql -u username -p
mysql> create database category; mysql> create database categorylinks; mysql> create database page; Создав базы данных, приступим к импорту. Он может занять весьма продолжительное время. $ mysql -u username -p category < ruwiki-latest-category.sql
$ mysql -u username -p categorylinks < ruwiki-latest-categorylinks.sql $ mysql -u username -p page < ruwiki-latest-page.sql Формируем таблицу взаимосвязи категорий и восстанавливаем граф Теперь нам нужно получить таблицу, в которой будет отражено как между собой связаны категории и для дальнейшей работы выгрузить таблицу в csv. Сделать это можно следующим запросом mysql> select page_title, cl_to from categorylinks.categorylinks join page.page
on cl_from = page_id where page_title in (select cat_title from category) INTO outfile '/var/lib/mysql-files/category.csv' FIELDS terminated by ';' enclosed by '"' lines terminated by '\n'; Результат будет выглядеть следующим образом. Не забудьте вручную добавить название столбцов. Стоит заметить, что слева у нас потомок, а справа — его предки, поэтому восстанавливать граф будем от потомков к предкам. Кроме того, есть еще очень много разных служебных категорий, которые мне лично не нужны, поэтому я их отфилтровал, сократив количество строк с примерно 1,6 миллионов до 1,1. Сделать все это можно при помощи следующего кода. import pandas as pd
import networkx as nx from tqdm.auto import tqdm, trange #Filtering df = pd.read_csv("category.csv", sep=";", error_bad_lines=False) df = df.dropna() df_filtered = df[df.parant.str.contains("[А-Яа-я]+:") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Страницы,_") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Статьи_проекта_") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Хорошие_статьи") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Перенаправления,_") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Избранные_списки_") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Избранные_статьи_") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Списки_проекта") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Добротные_статьи_") != True] df_filtered = df_filtered[df_filtered.parant.str.contains("Статьи") != True] # Graph recovering G = nx.DiGraph() c = 0 for i, gr in tqdm(df_filtered.groupby('child')): vertex = set() edges = [] for i, r in gr.iterrows(): G.add_node(r.parant, color="white") G.add_node(r.child, color="white") G.add_edge(r.parant, r.child) Работаем с графом и извлекаем фильтрованные статьи Для того, чтобы пользоваться этим графом и решить поставленную в начале задачу, воспользуемся алгоритмом поиска в глубину с модификациями для обнаружения циклов, для чего мы пометили каждый узел белым цветом, и ограничения глубины поиска. counter = 0
nodes = [] def dfs(G, node, max_depth): global nodes, counter G.nodes[node]['color'] = 'gray' nodes.append(node) counter += 1 if counter == max_depth: counter -= 1 return for v in G.successors(node): if G.nodes[v]['color'] == 'white': dfs(G, v, max_depth) elif G.nodes[v]['color'] == 'gray': continue counter -= 1 В результате, в листе nodes у нас содержатся все категории начиная от указаной и до желаемой глубины от начала. Ниже представлен пример для начальной точки "Точные науки" с ограничением на глубину в 5 вершин. Всего их получилось около 2500 тысяч. Конечно, там содержатся категории, которые не относятся к точным наукам и, возможно, каких-то категорий, которые должны быть, там не окажутся, но с этим способом лучше не выйдет — либо больше покрытие и больше ненужных категорий, либо наоборот. Однако, это гораздо лучше, чем вручную отбирать эти категории. Результат нужно сохранить построчно в файл, он нам понадобится для фильтарции. Подкатегории с вершины Точные наукиSPLТочные_науки
Информатика CAM Авторы_учебников_информатики Архивное_дело Археографические_комиссии Археографические_комиссии_Украины Виленская_археографическая_комиссия Архивисты Архивариусы Архивисты_по_алфавиту Архивисты_по_векам Архивисты_по_странам Архивное_дело_на_Украине Архивисты_Украины ... Терминология_телевидения Терминология_японских_боевых_искусств Термины_для_знаменитостей Термины_и_понятия_аниме_и_манги Технические_термины Транспортная_терминология Фантастические_термины_по_их_изобретателям Филателистические_термины Философские_термины Цирковые_термины Экономические_термины Японские_исторические_термины Экономика_знаний Инкапсуляция_(программирование) ... Бесконечность Бесконечные_графы Единое Философы_математики Прокл_Диадох Функции Арифметические_функции Мультипликативные_функции Большие_числа Кусочно-линейные_функции Преобразования Дискретные_преобразования Интегральные_преобразования Преобразования_пространства Теория_потенциала Типы_функций Числа Для того, чтобы применить эти категории для фильтрации для русского языка, однако, нужно кое-что подправить в исходниках. Я использовал эту версию. Сейчас там что-то новое, возможно, исправления ниже уже не актуальны. В файле WikiExtractor.py нужно заменить "Category" на "Категория" в двух местах. Области с уже исправленным вариантом представлены ниже: tagRE = re.compile(r'(.*?)<(/?\w+)[^>]*?>(?:([^<]*)(<.*?>)?)?')
# 1 2 3 4 keyRE = re.compile(r'key="(\d*)"') catRE = re.compile(r'\[\[Категория:([^\|]+).*\]\].*') # capture the category name [[Category:Category name|Sortkey]]" def load_templates(file, output_file=None): ... if inText:
page.append(line) # extract categories if line.lstrip().startswith('[[Категория:'): mCat = catRE.search(line) if mCat: catSet.add(mCat.group(1)) После этого нужно запустить команду python WikiExtractor.py --filter_category categories --output wiki_filtered ruwiki-latest-pages-articles.xml
где categories — это файл с категорями. Отфильтрованные статьи будут лежать в wiki_filtered. На этом все. Спасибо за внимание. =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 06:22
Часовой пояс: UTC + 5