1
Fork 0
This repository has been archived on 2025-06-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2022ST-project-Phikipathia/cgi-bin/scraper.py
2022-05-19 19:45:43 +02:00

192 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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, gebruiker=True):
"""
>>> converteer('Northwestern Europe')
'Northwestern Europe'
>>> converteer('Northwestern_Europe', False)
'Northwestern_Europe'
>>> converteer('Medchal%E2%80%93Malkajgiri_district')
'MedchalMalkajgiri district'
>>> converteer('MedchalMalkajgiri district', False)
'Medchal%E2%80%93Malkajgiri_district'
"""
from urllib import parse
if tekst is None:
return None
if gebruiker:
return parse.unquote(tekst).replace('_', ' ').split('#')[0]
else:
return parse.quote(tekst.replace(' ', '_'))
def zoek_link(soep):
"""
Doorzoek de meegeleverde soep naar een geldige Wikipedia-link.
:return: een geldige link indien beschikbaar, anders
>>> 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)'
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/Malawi').content, 'html.parser'))
'Tumbuka_language'
Maar eventueel als alternatief 'Landlocked_country' ??
>>> zoek_link(BeautifulSoup(requests.get('https://en.wikipedia.org/wiki/%27s-Gravenweg_168,_Kralingen').content, 'html.parser'))
'Kralingen'
>>> zoek_link(BeautifulSoup(requests.get('https://fr.wikipedia.org/wiki/Langue').content, 'html.parser'))
'Syst%C3%A8me'
"""
# Ik heb besloten om ook lijsttypes te kunnen doorzoeken op nuttige linkjes. Dit is strikt genomen tegen de
# beschrijving van de opgave, maar volgens https://en.wikipedia.org/wiki/Wikipedia:Getting_to_Philosophy lijkt dit
# de logische methode, dus ik besloot ook lijsten te ondersteunen, als extra uitdaging. Ik vermoedde dat de opgave
# van het project eerder <p> specifieerde om ons wat hoofdbreuk te besparen.
ondersteuning = ['p', 'ul', 'ol']
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(ondersteuning, 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')
# Spans negeren is redelijk enkel en alleen om coördinaat stukjes te vermijden.
while potentieel is None or alinea.findChild().name == 'span':
alinea = alinea.findNextSibling(ondersteuning, recursive=False)
# 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', 'MedchalMalkajgiri 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']}
>>> run('fr', 'Langue', 'Philosophie')
{'pad': ['Langue', 'Système', 'Ensemble', 'Mathématiques', 'Connaissance', 'Notion', 'Connaissance (philosophie)', 'Philosophie']}
Loops
>>> run('nl', 'België', 'Philosophy')
{'error': "Cyclus gedetecteerd op 'Wetenschap', startende vanaf 'België'. Het onafgewerkte pad zal niet worden toegevoegd aan het overzicht. "}
>>> run('en', 'Tom Inglesby', 'Contract failure')
{'pad': ['Tom Inglesby', 'Johns Hopkins Center for Health Security', 'Nonprofit organization', 'Contract failure']}
Doodlopende eindes
>>> run('en', 'Gaumee Film Awards', 'Philosophy')
{'error': "Er konden geen nieuwe links meer gevonden worden op 'Gaumee Film Awards'"}
Foute linkjes
>>> run('en', 'Deze pagina bestaat niet', 'Philosophy')
{'error': "Er ging iets fout bij het inladen van 'Deze pagina bestaat niet'. Bestaat de website?"}
"""
# Negeer de 'random' waarde als er op deze manier gestart wordt.
lijst = [converteer(start, True)] if start != 'Special:Random' else []
base = f"https://{taal}.wikipedia.org/wiki/"
while start != stop:
# Pagina inladen.
pagina = requests.get(base + converteer(start, False))
# Stoppen indien het inladen van de pagina niet lukte.
if pagina.status_code != 200:
return {'error': f"Er ging iets fout bij het inladen van '{lijst[-1]}', vertrekkende vanaf '{lijst[0]}'. Bestaat de website?"}
# Verwerken.
soep = BeautifulSoup(pagina.content, 'html.parser')
# Volgende link zoeken.
start = converteer(zoek_link(soep))
if start is None:
return {'error': f"Er konden geen nieuwe links meer gevonden worden op '{lijst[-1]}'"}
# Cyclus detecteren
if start in lijst:
return {'error':
f"Cyclus gedetecteerd op '{start}', startende vanaf '{lijst[0]}'. Het onafgewerkte pad zal niet worden toegevoegd aan het overzicht. "
}
lijst.append(start)
return {'pad': lijst}