Miteinander kommunizierende Computer sind eine sehr Erfindung. Prominente Beispiele sind lokale Netzwerke, das Internet, das hier vorliegende IPython Notebook, Smartphones und Raumsonden.
Im folgenden wird umrissen, wie man mittels Python mit einem Server kommunizeren kann, wie Daten übertragen werden, und wie ein Server prinzipiell gestartet werden kann.
Python selbst liefert die Bibliotheken urllib (Py3 urllib) und urllib2 aus. Sie liefern eine rudimentäre Schnittstelle um mit der Außenwelt kommunizieren zu können.
Besser ist eine Bibliothek wie requests,
welche einfacher zu bedienen ist und auf diesen Bibliotheken aufbaut.
Wir importieren sie mit dem Namen req
:
import requests as req
Kommunikation im WWW läuft über das HTTP Protokol ab. Die Sprache besteht aus Verben und Objekten. Das Verb "GET" teilt einem anderen Computer ("Server") mit, dass der anfragende Computer ("Client") etwas haben möchte. Die dieser Kommunikation zugrunde liegenden Systeme stellen sicher, dass beide miteinander auch über große Distanzen sprechen können.
Der Inhalt solch eines "Request"s kann entweder mit .text
direkt ausgelesen werden,
oder z.B. mittels .json()
in eine JSON Datenstruktur geparst werden.
Im folgenden Beispiel holen wir uns die Zellen des dieses hier vorliegenden Notebooks direkt vom Git Repository. Das Dateiformat basiert auf JSON und es werden genau die hier sichtbaren Zellen geladen.
Die requests
Bibliothek verbindet sich dabei zu GitHub über das Internet, teilt dem Server mit, dass es genau diese Datei haben möchte, und dann verarbeitet es die Rohdaten zu einer JSON Datenstruktur mittels .json()
.
Anschließend eine kurze Suche nach dem Inhalt "vorliegenden Notebook" um genau diese Zelle zu finden und auszugeben.
import requests as req
netzwerk = req.get("http://github.com/haraldschilly/python-fuer-mathematiker/raw/master/doc/4-6-netzwerk.ipynb")
notebook = netzwerk.json()
import json
for cell in notebook["cells"]:
if cell["cell_type"] == "markdown":
lines = cell["source"]
if any("vorliegenden Notebook" in line for line in lines):
print("".join(lines))
break
Im folgenden Beispiel holen wir uns die Zellen des dieses hier vorliegenden Notebooks direkt vom [Git Repository](https://github.com/haraldschilly/python-fuer-mathematiker/). Das Dateiformat basiert auf [JSON](http://json.org) und es werden genau die hier sichtbaren Zellen geladen. Die `requests` Bibliothek verbindet sich dabei zu GitHub über das Internet, teilt dem Server mit, dass es genau diese Datei haben möchte, und dann verarbeitet es die Rohdaten zu einer JSON Datenstruktur mittels `.json()`. Anschließend eine kurze Suche nach dem Inhalt "vorliegenden Notebook" um genau diese Zelle zu finden und auszugeben.
Hier ein anderes Beispiel. Wir lesen die XML Daten aus dem Vorlesungsverzeichnis ein und listen aller Termine der Lehrveranstaltung "Programmierpraktikum" auf:
pp = req.get("http://online.univie.ac.at/vlvz?lvnr=250129&semester=S2015&format=xml").text
print(pp[:500] + "...")
<?xml version="1.0" encoding="iso-8859-15" ?> <vorlesungen> <semester name="Sommersemester 2015"></semester><vlvz anmeldung_bis="" anmeldung_bis_en="" anmeldung_text="" anmeldung_von="" anmeldung_von_en="" beschraenkt="J" block="" blocked="N" ects=" 5,0" erklaerung="Praktikum" gemeinsam="" html="" inum="A8525 " jahr="2015" kommentar="" kurztitel="Programmierpraktikum" kurztitel_en="Programmierprakti...
import xml.etree.ElementTree as ET
from dateutil.parser import parse as dateparser
pp_xml = ET.fromstring(pp.encode("utf8"))
for i, gruppe in enumerate(pp_xml.findall("vlvz/gruppen")):
termine = set()
for termin in gruppe.findall("von_bis"):
termine.add(str(dateparser(termin.get("datum")))[:10])
print("Gruppe %d: %s" % (i, sorted(termine)))
Gruppe 0: ['2015-03-05', '2015-03-19', '2015-03-26', '2015-04-16', '2015-04-23', '2015-04-30', '2015-05-07', '2015-05-21', '2015-05-28', '2015-06-11', '2015-06-18', '2015-06-25'] Gruppe 1: ['2015-03-04', '2015-03-11', '2015-03-18', '2015-03-25', '2015-04-15', '2015-04-22', '2015-04-29', '2015-05-06', '2015-05-13', '2015-05-20', '2015-05-27', '2015-06-03', '2015-06-10', '2015-06-17', '2015-06-24']
Webseiten sind in HTML formatiert. Die Rohdaten sind ähnlich wie XML in Tags eingeschlossene Textbausteine. Eine Bibliothek wie Beautiful Soup hilft, diese Tags einzulesen und als Datenstruktur zu bearbeiten. Holen wir zum Beispiel die aktuellen Nachrichten, extrahieren ausschließlich die Überschriften des Hauptbereichs, und modifizieren die Links so, dass sie auf die Originalseite verweisen und ein neues Tab öffnen:
from bs4 import BeautifulSoup
derstandard = BeautifulSoup(req.get("http://derstandard.at").text, "lxml")
from IPython.display import HTML
headlines = derstandard.select("div#documentCanvas h2 a") + derstandard.select("div#documentCanvas h3 a")
hl_list = BeautifulSoup("<ul>", "lxml")
for hl in headlines:
hl.attrs["href"] = "http://derstandard.at" + hl.attrs["href"]
hl.attrs["target"] = "_blank"
entry = hl_list.new_tag("li")
entry.append(hl)
hl_list.append(entry)
HTML(hl_list.prettify())
Eine Kombination aus den bisher vorgestellten Techniken ist z.B. der Bau einer eigenen kleinen Suchmaschine. Zuerst werden Webseiten nach Links durchsucht, und die verlinkten Seiten ebenfalls wieder nach Links durchsucht. Auf diese Art erhält man eine Sammlung von Webseiten.
Dafür baut man nun einen Index, der von den einzelnen Wörtern einer Seite wieder auf den jeweiligen Link der Webseite zurück verweist.
Offen ist noch, welche von den Webseiten, die den Suchbegriff enthalten, besonders wichtig sind. Eine Technik, ursprünglich von Google's CEO Larry Page und nach ihm benannt, ist PageRank. Es analysiert die Struktur aller Links untereinander und gewichtet diejenigen Seiten besonders hoch, auf die von höher gewichteten Seiten aus verlinkt werden.
from __future__ import print_function
from collections import defaultdict
from queue import Queue
import requests as req
import random
import re
import threading
import networkx as nx
from multiprocessing.pool import ThreadPool
from bs4 import BeautifulSoup
def mk_new_url(url, href, url_filter = None):
if href.startswith("http"):
if url_filter:
if url_filter not in href.split("/")[2]:
return None
new_url = href
elif href.startswith("#"):
return None
else:
if not href.startswith("/"):
href = "/" + href
new_url = url + href
if "?" in new_url:
new_url = new_url[:new_url.index("?")]
if new_url.endswith("/"):
new_url = new_url[:-1]
return new_url
word = re.compile(r"\b\w{4,}\b")
def index_task(url):
if url in visited:
return "d"
try:
content = req.get(url, timeout = 5).text
except:
# connection error, etc. -> pick next one
return "err"
page = BeautifulSoup(content, "lxml")
for a in page.select("a"):
if "href" in a.attrs:
new_url = mk_new_url(url, a.attrs["href"], url_filter)
if new_url:
urls.put(new_url)
link_graph.add_edge(url, new_url)
for match in word.finditer(page.text):
token = match.group().lower()
keywords[token].add(url)
visited.add(url)
return len(visited)
def get_urls(limit = 400):
while limit >= 0:
limit -= 1
url = urls.get()
yield url
# Start. Resetting all variables.
urls = Queue()
urls.put("http://www.wifiwien.at") # start URL
url_filter = "wifiwien.at" # this must be in the DNS name
visited = set()
keywords = defaultdict(set)
# 30 in parallel, most of the time they are waiting for the website!
link_graph = nx.DiGraph()
workers = ThreadPool(30)
for nb_urls in workers.imap_unordered(index_task, get_urls()):
print(nb_urls, end=" ")
print("END")
1 2 3 4 5 6 7 8 d 9 10 11 11 12 13 14 15 16 17 18 18 19 20 21 22 23 24 25 25 26 27 27 28 29 30 31 32 33 d d d d d d d 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 51 52 52 52 52 53 54 55 55 55 56 56 56 56 57 58 58 58 58 58 59 59 60 61 61 err 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 err 79 80 d 81 82 83 84 err 85 86 err err 87 88 89 err 90 91 92 93 94 err err 95 err err 96 97 d 98 99 100 d err 101 err 102 103 104 105 106 err 107 err err err 108 err 109 err 110 111 d d 112 113 114 115 116 117 d d d d d 118 119 120 121 122 123 124 125 126 127 128 129 130 131 131 132 133 133 134 135 136 137 138 139 139 140 141 142 143 144 144 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 158 159 d d 160 161 162 163 164 165 166 167 168 169 170 171 172 172 173 174 175 176 177 177 d 178 d 179 d d 179 180 181 182 d 183 d d 184 185 185 186 187 188 d d d d d 189 190 191 192 193 194 195 196 195 196 196 196 197 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 218 219 220 d d d 221 222 223 224 225 226 227 228 229 230 231 232 233 234 234 235 236 237 238 239 d 240 d 241 d 241 d d 242 d d d 243 244 245 246 246 247 248 d d d d d 249 250 251 d 252 252 253 254 255 256 256 257 258 259 260 261 261 262 263 264 265 266 267 268 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 END
link_weights = nx.algorithms.link_analysis.pagerank(link_graph)
weighted_links = sorted(link_weights.keys(), key=link_weights.get, reverse=True)
for idx, link in enumerate(weighted_links):
print("%s, %s" % (link, link_weights[link]))
if idx > 10:
break
http://www.wifiwien.at/go.aspx, 0.0001273084651805407 http://www.wifiwien.at/Default.aspx/Kurse/@/menuid/2512/formid/70, 8.175471313858729e-05 http://www.wifiwien.at/default.aspx/F%c3%b6rder--und-Steuer-Tipps/@/menuId/197/EBENE/DETAILS/LNR/6830, 7.78857700722379e-05 http://www.wifiwien.at/javascript:void(0);/default.aspx/javascript:;, 6.36173458175267e-05 http://www.wifiwien.at/mailto:Kundenservice@wifiwien.at/javascript:;, 6.36173458175267e-05 http://www.wifiwien.at/default.aspx/anmeldung-zum-wifi-newsletter/@/menuid/2346/javascript:void(0)/javascript:;, 6.36173458175267e-05 http://www.wifiwien.at/javascript:;/default.aspx, 6.36173458175267e-05 http://www.wifiwien.at/texts.aspx/default.aspx/default.aspx, 6.36173458175267e-05 http://www.wifiwien.at/javascript:void(0);/default.aspx, 6.36173458175267e-05 http://www.wifiwien.at/texts.aspx/default.aspx/javascript:;, 6.36173458175267e-05 http://www.wifiwien.at/javascript:void(0);/javascript:;/default.aspx, 6.36173458175267e-05 http://www.wifiwien.at/default.aspx/geschenkgutscheine-/@/menuid/620/javascript:void(0);/javascript:;, 6.36173458175267e-05
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(sorted(link_weights.values(), reverse=True))
ax = plt.gca()
ax.set_ylim((0.0003, 0.002))
ax.set_yscale('log')
def search(*terms):
from IPython.display import HTML
def get_matches(term):
urls = set()
for k in keywords.keys():
if term in k:
for url in keywords[k]:
urls.add(url)
return urls
result = get_matches(terms[0])
for term in terms[1:]:
result = result.intersect(get_matches[term])
# sort by pagerank
result = sorted(result, key = link_weights.get, reverse=True)
urls_html = ['<a href="{0}" target="_blank">{0}</a> ({1:.6e})'.format(r, link_weights[r])
for r in result]
return HTML('<br>'.join(urls_html))
Suche nach Seiten, die das Teilwort "mathemat" beinhalten:
search("java")
len(keywords)
7183
10 zufällig gewählte Schlüsselwörter
random.sample(keywords.keys(), 10)
['961906_ausbildung', 'spezialthemen', 'webinar', 'technologischer', 'erfahren', 'weitere', 'körperhaltung', 'lebensmittel', 'fotoshooting', 'üben']