1
Fork 0

Grote push

This commit is contained in:
Tibo De Peuter 2022-05-19 09:07:29 +02:00
parent 2baf09ff6e
commit 4bcf82fa3e
4 changed files with 269 additions and 6 deletions

20
cgi-bin/init.py Normal file
View 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
View 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}

View file

@ -1,7 +1,21 @@
<html>
<head>
<title>Wikipedia</title>
</head>
<body>
</body>
<!doctype html>
<html lang="HTML5">
<head>
<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
View 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;
}