From 04a1e98b7989331b78c8aed179d8c2767b3ff869 Mon Sep 17 00:00:00 2001 From: Marian Steinbach Date: Sun, 5 May 2019 22:26:41 +0200 Subject: [PATCH] =?UTF-8?q?Pr=C3=BCfe=20Existenz=20von=20/favicon.ico=20un?= =?UTF-8?q?d=20werte=20dies=20ebenso=20wie=20ein=20Icon,=20das=20im=20HTML?= =?UTF-8?q?=20Head=20verlinkt=20ist=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix full JSON export * Update ignore list * Update README * Check for /favicon.ico and rate it as icon available * Remove broken cookies test --- .gitignore | 6 +-- README.md | 71 ++++++++++++++++------------------ checks/__init__.py | 10 +++-- checks/load_favicons.py | 35 +++++++++++++++++ checks/load_favicons_test.py | 43 ++++++++++++++++++++ checks/load_in_browser_test.py | 16 -------- export/__init__.py | 20 ++++++++-- rating/favicon.py | 8 +++- 8 files changed, 143 insertions(+), 66 deletions(-) create mode 100644 checks/load_favicons.py create mode 100644 checks/load_favicons_test.py diff --git a/.gitignore b/.gitignore index a536d3d..e694cdc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,8 @@ venv cache -webapp/node_modules secrets temp __pycache__ .vscode/settings.json -webapp/dist/bundle.js -dev-shm -/export-* +kubernetes/green-spider-secret.yaml +/volumes \ No newline at end of file diff --git a/README.md b/README.md index a1cf9b3..e2c32a7 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ Zur Auswertung: [https://green-spider.netzbegruenung.de/](https://green-spider.n ## Tools -- Spider: Sammelt Informationen über Websites von B90/GRÜNE Gliederungen - -- Screenshotter: Erstellt Seiten-Screenshots. Siehe [netzbegruenung/green-spider-screenshotter](https://github.com/netzbegruenung/green-spider-screenshotter/) - -- Webapp: Darstellung der Spider-Ergebnisse. Siehe [netzbegruenung/green-spider-webapp](https://github.com/netzbegruenung/green-spider-webapp/) - -- Indexer: Lädt Ergebnisdaten in Elasticsearch. Siehe [netzbegruenung/green-spider-indexer](https://github.com/netzbegruenung/green-spider-indexer) +- **Spider:** Sammelt Informationen über Websites von B90/GRÜNE Gliederungen +- **Screenshotter:** Erstellt Seiten-Screenshots. Siehe [netzbegruenung/green-spider-screenshotter](https://github.com/netzbegruenung/green-spider-screenshotter/) +- **Webapp:** Darstellung der Spider-Ergebnisse. Siehe [netzbegruenung/green-spider-webapp](https://github.com/netzbegruenung/green-spider-webapp/). Dazu gehören + - **API**: [netzbegruenung/green-spider-api](https://github.com/netzbegruenung/green-spider-api) + - **Elasticsearch** + - **Indexer:** Lädt Ergebnisdaten in Elasticsearch. Siehe [netzbegruenung/green-spider-indexer](https://github.com/netzbegruenung/green-spider-indexer) +- **Auswertung**: R Projekt zur Auswertung der Ergebnisse. Siehe [netzbegruenung/green-spider-analysis](https://github.com/netzbegruenung/green-spider-analysis) ## Aktivitäten @@ -24,40 +24,37 @@ Green Spider ist ein Projekt des [netzbegrünung](https://blog.netzbegruenung.de Zur Kommunikation dient der Chatbegrünung-Kanal [#green-spider](https://chatbegruenung.de/channel/green-spider) sowie die [Issues](https://github.com/netzbegruenung/green-spider/issues) hier in diesem Repository. -## Anleitung +## Betrieb + +Alle Informationen zum Betrieb befinden sich im Verzeichnis [devops](https://github.com/netzbegruenung/green-spider/tree/master/devops). + +## Entwicklung + +Green Spider ist in Python 3 geschrieben und wird aktuell unter 3.6 getestet und ausgeführt. + +Aufgrund zahlreicher Dependencies empfiehlt es sich, den Spider Code lokal in Docker +auszuführen. + +Das Image wird über den folgenden Befehl erzeugt: + +```nohighlight +make +``` + +Das dauert beim ersten Ausführen einige Zeit, wiel einige Python-Module das Kompilieren diverser Libraries erfordern. +Nach dem ersten erfolgreichen Durchlauf dauert ein neuer Aufruf von `make` nur noch wenige Sekunden. + +### Tests ausführen + +In aller Kürze: `make test` ### Spider ausführen -Zum Ausführen des Spider auf einem Server siehe Verzeichnis [devops](https://github.com/netzbegruenung/green-spider/tree/master/devops). - -Voraussetzungen zum lokalen Ausführen: - -- Docker -- Schlüssel mit Schreibrecht für die Ergebnis-Datenbank - -Um alle Sites aus aus [netzbegruenung/green-directory](https://github.com/netzbegruenung/green-directory) zu spidern: +Der Spider kann einzelne URLs verarbeiten, ohne die Ergebnisse in eine Datenbank zu schreiben. +Am einfachsten geht das über den `make spider` Befehl, so: ```nohighlight -make spiderjobs -make spider +make spider ARGS="--url http://www.example.com/" ``` -Alternativ kann wie im nachfolgenden Beispiel gezeogt das Spidern einer einzelnen URL angestoßen werden. Diese muss nicht zwingend Teil des `green-directory` sein. - -```nohighlight -docker run --rm -ti \ - -v $PWD/secrets:/secrets - quay.io/netzbegruenung/green-spider:latest \ - --credentials-path /secrets/datastore-writer.json \ - jobs --url https://www.trittin.de/ - -make spider -``` - -### Screenshots erstellen - -Siehe Verzeichnis [devops](https://github.com/netzbegruenung/green-spider/tree/master/devops). - -### Webapp deployen - -Siehe Verzeichnis [devops](https://github.com/netzbegruenung/green-spider/tree/master/devops). +Ohne `ARGS` aufgerufen, arbeitet der Spider eine Jobliste ab. Dies erfordert Zugriff auf die entsprechende Datenank. diff --git a/checks/__init__.py b/checks/__init__.py index e5c4c93..9761025 100644 --- a/checks/__init__.py +++ b/checks/__init__.py @@ -5,21 +5,22 @@ functionality of a site or individual pages. import logging -from checks import charset from checks import certificate +from checks import charset from checks import dns_resolution -from checks import duplicate_content from checks import domain_variations +from checks import duplicate_content from checks import frameset from checks import generator from checks import html_head from checks import http_and_https from checks import hyperlinks -from checks import page_content +from checks import load_favicons from checks import load_feeds from checks import load_in_browser -from checks import url_reachability +from checks import page_content from checks import url_canonicalization +from checks import url_reachability from checks.config import Config @@ -46,6 +47,7 @@ def perform_checks(input_url): ('frameset', frameset), ('hyperlinks', hyperlinks), ('generator', generator), + ('load_favicons', load_favicons), ('load_feeds', load_feeds), ('load_in_browser', load_in_browser), ] diff --git a/checks/load_favicons.py b/checks/load_favicons.py new file mode 100644 index 0000000..d300d46 --- /dev/null +++ b/checks/load_favicons.py @@ -0,0 +1,35 @@ +""" +Loads /favicon if no icon has been found otherwise +""" + +import logging +from time import mktime +from datetime import datetime +from urllib.parse import urlparse + +import requests + +from checks.abstract_checker import AbstractChecker + +class Checker(AbstractChecker): + def __init__(self, config, previous_results=None): + super().__init__(config, previous_results) + self.favicons = {} + + def run(self): + for url in self.config.urls: + self.load_favicon(url) + + return self.favicons + + def load_favicon(self, url): + """ + This loads /favicon.ico for the site's URL + """ + parsed = urlparse(url) + ico_url = parsed.scheme + "://" + parsed.hostname + "/favicon.ico" + r = requests.head(ico_url) + if r.status_code == 200: + self.favicons[url] = { + 'url': ico_url, + } diff --git a/checks/load_favicons_test.py b/checks/load_favicons_test.py new file mode 100644 index 0000000..1a7efb1 --- /dev/null +++ b/checks/load_favicons_test.py @@ -0,0 +1,43 @@ +from pprint import pprint + +import httpretty +from httpretty import httprettified +import unittest + +from checks import load_favicons +from checks.config import Config + +@httprettified +class TestFavicons(unittest.TestCase): + + def test_favicons(self): + # This site has a favicon + url1 = 'http://example1.com/favicon.ico' + httpretty.register_uri(httpretty.HEAD, url1, + body='', + adding_headers={ + "Content-type": "image/x-ico", + }) + + # This site has no favicon + url2 = 'http://example2.com/favicon.ico' + httpretty.register_uri(httpretty.HEAD, url2, + status=404, + body='Not found', + adding_headers={ + "Content-type": "text/plain", + }) + + + config = Config(urls=['http://example1.com/path/', 'http://example2.com/']) + checker = load_favicons.Checker(config=config) + + result = checker.run() + pprint(result) + + self.assertEqual(result, { + 'http://example1.com/path/': { + 'url': 'http://example1.com/favicon.ico' + } + }) + diff --git a/checks/load_in_browser_test.py b/checks/load_in_browser_test.py index bd7c448..d821bee 100644 --- a/checks/load_in_browser_test.py +++ b/checks/load_in_browser_test.py @@ -26,21 +26,5 @@ class TestLoadInBrowser(unittest.TestCase): self.assertEqual(result[url]['font_families'], ['"times new roman"']) - def test_cookies(self): - """Loads a page that sets cookies""" - url = 'https://httpbin.org/cookies/set/cookiename/cookievalue' - config = Config(urls=[url]) - checker = load_in_browser.Checker(config=config, previous_results={}) - result = checker.run() - - self.assertEqual(result[url]['cookies'], [{ - 'domain': 'httpbin.org', - 'httpOnly': False, - 'name': 'cookiename', - 'path': '/', - 'secure': False, - 'value': 'cookievalue' - }]) - if __name__ == '__main__': unittest.main() diff --git a/export/__init__.py b/export/__init__.py index 5e8db08..639f011 100644 --- a/export/__init__.py +++ b/export/__init__.py @@ -2,15 +2,27 @@ Exports data from the database to JSON files for use in a static webapp """ -from hashlib import md5 -import json +import datetime import logging import sys import os +from hashlib import md5 +import json import requests +class DateTimeEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, datetime.datetime): + return obj.isoformat() + elif isinstance(obj, datetime.date): + return obj.isoformat() + elif isinstance(obj, datetime.timedelta): + return (datetime.datetime.min + obj).time().isoformat() + else: + return super(DateTimeEncoder, self).default(obj) + def export_results(client, entity_kind): """ Export of the main results data @@ -31,6 +43,6 @@ def export_results(client, entity_kind): 'score': entity.get('score'), }) - output_filename = "spider_result.json" + output_filename = "/json-export/spider_result.json" with open(output_filename, 'w', encoding="utf8") as jsonfile: - json.dump(out, jsonfile, indent=2, sort_keys=True, ensure_ascii=False) + json.dump(out, jsonfile, indent=2, sort_keys=True, ensure_ascii=False, cls=DateTimeEncoder) diff --git a/rating/favicon.py b/rating/favicon.py index 5387a1e..ade85a0 100644 --- a/rating/favicon.py +++ b/rating/favicon.py @@ -8,7 +8,7 @@ class Rater(AbstractRater): rating_type = 'boolean' default_value = False - depends_on_checks = ['html_head'] + depends_on_checks = ['html_head', 'load_favicons'] max_score = 1 def __init__(self, check_results): @@ -23,6 +23,12 @@ class Rater(AbstractRater): value = True score = self.max_score break + + # /favicon.ico as fall back + if url in self.check_results['load_favicons']: + value = True + score = self.max_score + break return { 'type': self.rating_type,