Neste post, gostaria de complementar este artigo e dizer como você pode usar o Wikipedia WikiExtractor de forma flexível, filtrando artigos por categoria.
Tudo começou com o fato de que eu precisava de definições para vários termos. Os termos e suas definições são geralmente a primeira frase em todas as páginas da Wikipedia. Seguindo o caminho mais simples, extraí todos os artigos e rapidamente peguei tudo o que precisava com os clientes regulares. O problema é que o tamanho das definições ultrapassou 500 MB e havia muitas coisas desnecessárias, por exemplo, entidades nomeadas, cidades, anos, etc. que eu não preciso.
Presumi corretamente que a ferramenta WikiExtractor (vou usar uma versão diferente, o link estará abaixo) tem algum tipo de filtro, e acabou por ser um filtro de categoria. Categorias são tags para artigos que possuem uma estrutura hierárquica para organizar as páginas. Fiquei feliz em colocar a categoria "Ciências Exatas", acreditando muito ingenuamente que todos os artigos relacionados às ciências exatas serão incluídos na lista, mas um milagre não aconteceu - cada página tem seu próprio conjunto minúsculo de categorias e não há informações em uma única página sobre como essas categorias se relacionam. Isso significa que se eu precisar de páginas sobre ciências exatas, devo indicar todas as categorias que são descendentes de "Ciências exatas".
Bem, não importa, agora vou encontrar um serviço, pensei, que enviaria facilmente todas as categorias de um determinado começo para mim. Infelizmente, só encontrei isso onde você pode ver como essas categorias estão relacionadas. Uma tentativa de iterar manualmente sobre as categorias também não teve sucesso, mas fiquei "feliz" por essas categorias não terem uma estrutura de árvore, como pensei todo esse tempo, mas apenas um gráfico direcionado com ciclos. Além disso, a hierarquia em si flutua muito - direi de antemão que, definindo o ponto de partida "Matemática", você pode facilmente chegar a Alexandre I. Como resultado, eu só tive que restaurar este gráfico localmente e de alguma forma obter uma lista de categorias de interesse para mim.
, : - , , , - .
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 . , , , , - , , , — , . , , .
, .
_
CAM
__
_
_
__
__
__
__
__
___
_
...
_
___
__
_____
_
_
____
_
_
_
_
__
_
_()
...
_
_
_
_
_
_
-_
_
_
_
_
_
Para aplicar essas categorias de filtragem para o idioma russo, no entanto, você precisa ajustar algo nas fontes. Eu usei essa versão. Agora há algo novo, talvez as correções abaixo não sejam mais relevantes. No arquivo WikiExtractor.py, você precisa substituir "Categoria" por "Categoria" em dois lugares. As áreas com a versão já corrigida são apresentadas a seguir:
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))
Depois disso, você precisa executar o comando
python WikiExtractor.py --filter_category categories --output wiki_filtered ruwiki-latest-pages-articles.xml
onde categorias é o arquivo com categorias. Os artigos filtrados estarão em wiki_filtered.
Isso é tudo. Obrigado pela atenção.