Grote push
This commit is contained in:
parent
2baf09ff6e
commit
4bcf82fa3e
4 changed files with 269 additions and 6 deletions
20
cgi-bin/init.py
Normal file
20
cgi-bin/init.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Dit script wordt gebruikt om de methodes in de scraper aan te roepen via JavaScript. Zo kan dit toch nog redelijk
|
||||
onafhankelijk gebeuren en kan er gemakkelijker ge-debugged worden.
|
||||
De bestandsextensie werd aangepast om te kunnen debuggen op zowel Windows als Linux.
|
||||
"""
|
||||
|
||||
import cgi
|
||||
import json
|
||||
|
||||
from scraper import run, converteer
|
||||
|
||||
parameters = cgi.FieldStorage()
|
||||
data = json.loads(parameters.getvalue('data'))
|
||||
antwoord = run(data['taal'], converteer(data['start']), converteer(data['einde']))
|
||||
|
||||
print("Content-Type: application/json")
|
||||
print() # Lege lijn na headers
|
||||
print(json.dumps(antwoord))
|
151
cgi-bin/scraper.py
Normal file
151
cgi-bin/scraper.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Dit is het 'CGI-script'.
|
||||
De bestandsextensie werd aangepast om te kunnen debuggen op zowel Windows als Linux.
|
||||
"""
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
|
||||
|
||||
def converteer(tekst, user=True):
|
||||
if user:
|
||||
return tekst.replace(' ', '_')
|
||||
else:
|
||||
return tekst.replace('_', ' ')
|
||||
|
||||
|
||||
def zoek_link(soep):
|
||||
"""
|
||||
Doorzoek de meegeleverde soep naar een geldige Wikipedia-link.
|
||||
:return: een geldige link indien beschikbaar, anders None
|
||||
|
||||
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/Belgium').content, 'html.parser'))
|
||||
'Northwestern_Europe'
|
||||
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/Glossary_of_engineering').content, 'html.parser'))
|
||||
'Glossary_of_civil_engineering'
|
||||
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/Engineering').content, 'html.parser'))
|
||||
'Scientific_method'
|
||||
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)').content, 'html.parser'))
|
||||
'High-level_programming_language'
|
||||
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/Department_of_Standards_Malaysia').content, 'html.parser'))
|
||||
'Ministry_of_International_Trade_and_Industry_(Malaysia)'
|
||||
"""
|
||||
# TODO Zorg ervoor dat tekst schuin of vetjes ook nog herkend wordt
|
||||
|
||||
lijsttypes = ['ul', 'ol'] # Lijsttypes die we kunnen gebruiken
|
||||
link = None
|
||||
|
||||
# Navigeer naar het niveau op de pagina waarop we zullen zoeken.
|
||||
soep = soep.find('div', {'id': 'bodyContent'}) \
|
||||
.find('div', {'class': 'mw-parser-output'})
|
||||
|
||||
# Niet recursief zoeken om niet in een tabel of iets dergelijke vast te komen.
|
||||
# Het niveau onder 'mw-parser-output' is wat we nodig hebben en waar alle nodige alinea's in staan.
|
||||
alinea = soep.find(['p', lijsttypes], recursive=False)
|
||||
|
||||
# Ga steeds verder omlaag (niet dieper) totdat er een geldige link is gevonden.
|
||||
while link is None:
|
||||
|
||||
# Navigeer naar het volgende stuk tekst (alinea of lijst) met een link.
|
||||
# Negeer linkjes in superscript, dus zoek enkel op het huidige niveau.
|
||||
potentieel = alinea.find('a')
|
||||
|
||||
while potentieel is None or alinea.findChild().name == 'span':
|
||||
|
||||
alinea = alinea.findNextSibling(['p', lijsttypes])
|
||||
|
||||
# Het zou kunnen dat de bewerking hierboven ervoor zorgt dat we op het einde van de pagina zijn
|
||||
if alinea is None:
|
||||
return None
|
||||
|
||||
potentieel = alinea.find('a')
|
||||
|
||||
# Blijf zoeken naar linkjes in dezelfde alinea totdat deze voorwaarden voldaan zijn, maar stop van zodra er geen
|
||||
# naasten meer gevonden kunnen worden.
|
||||
while potentieel is not None and (
|
||||
tussen_haakjes(potentieel, alinea)
|
||||
# Soms kunnen hier 'interne'/externe linkjes of linkjes die niet werken (rode) tussen zitten.
|
||||
or not potentieel['href'].startswith('/wiki')
|
||||
or not potentieel['href'].count(':') == 0 # Bijzondere Wikipedia links negeren.
|
||||
):
|
||||
potentieel = potentieel.findNext('a')
|
||||
|
||||
if potentieel is not None:
|
||||
link = potentieel['href'].split('/')[2]
|
||||
|
||||
return link
|
||||
|
||||
|
||||
def tussen_haakjes(zin, omgeving):
|
||||
"""
|
||||
Controleer de geldigheid van de link door het aantal openende haakjes te vergelijken met het aantal sluitende
|
||||
haakjes die VOOR de onderzochte link voorkomen.
|
||||
Deze methode is meer waterdicht dan het vergelijken van indexen van haakjes.
|
||||
Strikt genomen zou het kunnen voorkomen dat een paragraaf tussen haken staat, die we dus niet kunnen meerekenen,
|
||||
maar dit voelt grammaticaal onjuist en we zullen er hier dan ook vanuit gaan dat dit niet het geval is.
|
||||
|
||||
>>> tussen_haakjes('woord', 'dit is een heel belangrijk woord dat we nu zoeken')
|
||||
False
|
||||
>>> tussen_haakjes('woord', 'dit is een (heel) belangrijk woord dat we nu zoeken')
|
||||
False
|
||||
>>> tussen_haakjes('woord', 'dit (is een heel belangrijk woord dat we nu) zoeken')
|
||||
True
|
||||
>>> tussen_haakjes('woord', 'dit (is (een heel) belangrijk woord) dat we nu zoeken')
|
||||
True
|
||||
"""
|
||||
|
||||
openende_haakjes = str(omgeving).count("(", 0, str(omgeving).find(str(zin)))
|
||||
sluitende_haakjes = str(omgeving).count(")", 0, str(omgeving).find(str(zin)))
|
||||
return openende_haakjes != sluitende_haakjes
|
||||
|
||||
|
||||
def run(taal, start, stop):
|
||||
"""
|
||||
Paden kunnen gecontroleerd worden met https://www.xefer.com/wikipedia, al doet dit niets met 'speciale' Wikipedia-
|
||||
links.
|
||||
|
||||
Gewone controles
|
||||
>>> run('en', "Belgium", "Philosophy")
|
||||
{'pad': ['Belgium', 'Northwestern Europe', 'Subregion', 'Region', 'Geography', 'Science', 'Scientific method', 'Empirical evidence', 'Proposition', 'Logic', 'Reason', 'Consciousness', 'Sentience', 'Emotion', 'Mental state', 'Mind', 'Phenomenon', 'Immanuel Kant', 'Philosophy']}
|
||||
>>> run('en', 'Department of Standards Malaysia', 'Philosophy')
|
||||
{'pad': ['Department_of_Standards_Malaysia', 'Ministry of International Trade and Industry (Malaysia)', 'Ministry (government department)', 'Executive (government)', 'Government', 'State (polity)', 'Germans', 'Germany', 'Central Europe', 'Europe', 'Continent', 'Landmass', 'Region', 'Geography', 'Science', 'Scientific method', 'Empirical evidence', 'Proposition', 'Logic', 'Reason', 'Consciousness', 'Sentience', 'Emotion', 'Mental state', 'Mind', 'Phenomenon', 'Immanuel Kant', 'Philosophy']}
|
||||
>>> run('en', 'Malkajgiri mandal', 'Philosophy')
|
||||
{'pad': ['Malkajgiri_mandal', 'Medchal%E2%80%93Malkajgiri district', 'District', 'Administrative division', 'Sovereign state', 'Polity', 'Politics', 'Decision-making', 'Psychology', 'Science', 'Scientific method', 'Empirical evidence', 'Proposition', 'Logic', 'Reason', 'Consciousness', 'Sentience', 'Emotion', 'Mental state', 'Mind', 'Phenomenon', 'Immanuel Kant', 'Philosophy']}
|
||||
|
||||
Loops
|
||||
>>> run('nl', 'België', 'Philosophy')
|
||||
|
||||
Doodlopende eindes
|
||||
"""
|
||||
|
||||
# Negeer de 'random' waarde als er op deze manier gestart wordt.
|
||||
lijst = [converteer(start)] if start != 'Special:Random' else []
|
||||
base = f"https://{taal}.wikipedia.org/wiki/"
|
||||
|
||||
while start != stop:
|
||||
# Pagina inladen.
|
||||
pagina = requests.get(base + start)
|
||||
|
||||
# Verwerken.
|
||||
soep = BeautifulSoup(pagina.content, 'html.parser')
|
||||
|
||||
# Volgende link zoeken.
|
||||
start = zoek_link(soep)
|
||||
|
||||
# TODO Dit verder uitwerken
|
||||
if start is None:
|
||||
return {'error': f'Er konden geen nieuwe links meer gevonden worden op {lijst[-1]}'}
|
||||
|
||||
# Cyclus detecteren
|
||||
nieuw = converteer(start.split('/')[-1], False)
|
||||
if nieuw in lijst:
|
||||
return {
|
||||
'error':
|
||||
f'Cyclus gedetecteerd op {nieuw}, het onafgewerkte pad zal niet worden toegevoegd aan het overzicht'
|
||||
}
|
||||
|
||||
lijst.append(nieuw)
|
||||
|
||||
return {'pad': lijst}
|
18
index.html
18
index.html
|
@ -1,7 +1,21 @@
|
|||
<html>
|
||||
<!doctype html>
|
||||
<html lang="HTML5">
|
||||
<head>
|
||||
<title>Wikipedia</title>
|
||||
<meta charset=UTF-8>
|
||||
<title>Phikipathia - Philosophy Wikipedia Path</title>
|
||||
</head>
|
||||
<body>
|
||||
<label for="taal">Taal: </label><input type="text" id="taal" placeholder="en"/>
|
||||
<label for="start">Startpagina: </label><input type="text" id="start" placeholder="Special:Random"/>
|
||||
<label for="einde">Eindpunt: </label><input type="text" id="einde" placeholder="Philosophy"/>
|
||||
<button type="button" id="toevoegKnop">Voeg toe</button>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h1>Phikipathia</h1>
|
||||
<h3>Philosophy Wikipedia Path visualiser</h3>
|
||||
<div id="visualisatie">
|
||||
</div>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
78
index.js
Normal file
78
index.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
|
||||
// TODO decodeURIComponent(String)
|
||||
|
||||
// Initialiseer de webpagina
|
||||
const taal = document.querySelector('#taal');
|
||||
const start = document.querySelector('#start');
|
||||
const einde = document.querySelector('#einde');
|
||||
const toevoegKnop = document.querySelector('#toevoegKnop');
|
||||
const visualisatie = document.querySelector('#visualisatie');
|
||||
|
||||
maakElementIn(getOrDefault(einde), visualisatie);
|
||||
toevoegKnop.addEventListener('click', () => zoek());
|
||||
|
||||
/**
|
||||
* Zoek een
|
||||
* pad met scraper.py en voeg deze toe in de lijst.
|
||||
*/
|
||||
function zoek() {
|
||||
|
||||
// Schakel de knop 'toevoegen' uit zolang het zoekproces bezig is, zodat er geen meerdere
|
||||
// paden tegelijk toegevoegd kunnen worden.
|
||||
toevoegKnop.disabled = true;
|
||||
|
||||
const verzoek = { // Standaardwaarden gebruiken indien het veld leeg is.
|
||||
taal: getOrDefault(taal),
|
||||
start: getOrDefault(start),
|
||||
einde: getOrDefault(einde)
|
||||
};
|
||||
|
||||
fetch(`cgi-bin/init.py?data=${JSON.stringify(verzoek)}`)
|
||||
.then(antwoord => antwoord.json())
|
||||
.then(data => voegToe(data))
|
||||
.catch(reason => alert(reason))
|
||||
.finally(() => toevoegKnop.disabled = false); // Herstel de knop. Ook als er ondertussen iets mis ging.
|
||||
|
||||
}
|
||||
|
||||
function voegToe(data) {
|
||||
if (data.hasOwnProperty('error')) {
|
||||
alert(data.error);
|
||||
} else {
|
||||
let pad = data.pad;
|
||||
|
||||
// Locatie van invoegen bepalen.
|
||||
let moeder = visualisatie;
|
||||
let kinderen = visualisatie.getElementsByTagName('li');
|
||||
let kindernamen = Array.from(kinderen).map(item => item.getAttribute('item'));
|
||||
let i = 0;
|
||||
while (moeder === visualisatie && i < pad.length) {
|
||||
if (kindernamen.indexOf(pad[i]) !== -1) {
|
||||
moeder = kinderen[kindernamen.indexOf(pad[i])];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Alleen nog die elementen toevoegen die nog niet op het pad staan.
|
||||
pad = pad.slice(0, i - 1);
|
||||
|
||||
// Nieuw element creëren (zonder toe te voegen aan het DOM).
|
||||
for (let item of pad.reverse()) {
|
||||
moeder = maakElementIn(item, moeder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function maakElementIn(titel, moeder) {
|
||||
let wrap = document.createElement('ul');
|
||||
let nieuw = document.createElement('li');
|
||||
nieuw.innerHTML = titel;
|
||||
nieuw.setAttribute('item', titel);
|
||||
wrap.appendChild(nieuw);
|
||||
moeder.appendChild(wrap);
|
||||
return nieuw;
|
||||
}
|
||||
|
||||
function getOrDefault(element) {
|
||||
return element.value === '' ? element.getAttribute('placeholder') : element.value;
|
||||
}
|
Reference in a new issue