Initial import to Tizen
[profile/ivi/python-twisted.git] / doc / historic / 2003 / europython / webclients.html
1 <html><head><title>Writing Web Clients</title></head><body>
2
3 <h1>Writing Web Clients</h1>
4
5 <h2>Web Clients -- The Tutorial</h2><ul>
6 <li>Welcome</li>
7
8 <li>Gimmick -- Buffy quotes</li>
9
10 </ul>
11 <hr />
12 <em>Anya (Family, season 5) -- Thank you for coming. We value your patronage.</em>
13 <h2>What Are Web Clients?</h2><ul>
14 <li>Clarification: non-interactive web clients</li>
15
16 <li>Special purpose</li>
17
18 <li>Often, quick and dirty hacks</li>
19
20 <li>Make a web page into API</li>
21
22 </ul>
23 <hr />
24 <em>Giles (Family, season 5) -- Could we please be a little less effusive, Anya?</em>
25 <h2>What Are Web Clients Useful For?</h2><ul>
26 <li>Mass download</li>
27
28 <li>Periodic checking</li>
29
30 <li>Automating tasks<ul><li>Make a web page more friendly</li>
31 </ul></li>
32
33 </ul>
34 <hr />
35 <em>Harmony (Family, season 5) -- Aww. You're my little lamb.</em>
36 <h2>Review of Modules</h2><ul>
37 <li>htmllib</li>
38
39 <li>sgmllib</li>
40
41 <li>httplib</li>
42
43 <li>urllib</li>
44
45 <li>urllib2</li>
46
47 <li>urlparse</li>
48
49 </ul>
50 <hr />
51 <em>Buffy (Family, season 5) -- Your definition of narrow is impressively wide.</em>
52 <h2>Modules -- htmllib</h2><ul>
53 <li>Most useful for easy filtering of images</li>
54
55 <li>...or links</li>
56
57 <li>Other things often easier with sgmllib</li>
58
59 <li>Or with re</li>
60
61 <li>Or with string manipulation</li>
62
63 </ul>
64 <hr />
65 <em>Xander (Family, season 5) -- The answer is somewhere here.</em>
66 <h2>Modules -- htmllib -- idiomatic usage</h2>
67 <pre>
68 # For lists
69 import htmllib, formatter
70
71 h = htmllib.HTMLParser(formatter.NullFormatter())
72 h.feed(htmlString)
73 print h.anchorlist
74 </pre>
75
76 <hr />
77 <em>Xander (Family, season 5) -- I'm helping, I'm reading, I'm quiet.</em>
78 <h2>Modules -- htmllib -- idiotmatic usage (cont'd)</h2>
79 <pre>
80 import htmllib, formatter
81
82 class IMGFinder(htmllib.HTMLParser):
83
84     def __init__(self, *args, **kw):
85         htmllib.HTMLParser.__init__(self, *args, **kw)
86         self.ims = []
87
88     def handle_image(self, src, *args): self.ims.append(src)
89
90 h = IMGFinder(formatter.NullFormatter())
91 h.feed(htmlString)
92 print h.ims
93 </pre>
94
95 <hr />
96 <em>Donny (Family, season 5) -- Look what I found!</em>
97 <h2>Modules -- htmllib -- base</h2><ul>
98 <li>Some sites use 'base' for different relative linking</li>
99
100 <li>For example, Zope does</li>
101
102 <li>In above examples, 'h.base' has the base</li>
103
104 </ul>
105 <hr />
106 <em>Dawn (Family, season 5) -- This is the source of my gladness.</em>
107 <h2>Modules -- htmllib -- base (example)</h2><ul>
108 <li>If the page on http://example.com/foo/bar.html has a link to '../baz.html'<ul><li>It means http://example.com/baz.html</li>
109 </ul></li>
110
111 <li>If the original page has base='/foo/quux'<ul><li>It means http://example.com/foo/baz.html</li>
112 </ul></li>
113
114 </ul>
115 <hr />
116 <em>Riley (Family, season 5) -- Every time I think I'm getting close to you...</em>
117 <h2>Modules -- urllib/urllib2</h2><ul>
118 <li>High-level interface</li>
119
120 <li>Treat URLs as file-like objects</li>
121
122 <li>...but still allows low-level operations</li>
123
124 <li>Interface largely compatible</li>
125
126 </ul>
127 <hr />
128 <em>Glory (Family, season 5) -- I am great and I am beautiful.</em>
129 <h2>Modules -- urllib/urllib2 (cont'd)</h2><ul>
130 <li>Can work through object-interface</li>
131
132 <li>More flexible</li>
133
134 <li>Interface no longer compatible</li>
135
136 <li>urllib2 better usually</li>
137
138 </ul>
139 <hr />
140 <em>Joyce (Ted, season 2) -- He redid my entire system.</em>
141 <h2>Modules -- urllib/urllib2 (examples)</h2><ul>
142 <li>urllib.urlopen("http://www.yahoo.com/").read() -&gt; contents</li>
143
144 <li>urllib.urlopen("http://www.yahoo.com/").info() -&gt; headers</li>
145
146 <li>Same works with urllib2</li>
147
148 <li>Automatically uses environment variables for proxies</li>
149
150 <li>urllib2 supports proxies with authentication</li>
151
152 </ul>
153 <hr />
154 <em>Xander (Ted, season 2) -- Yum-my!</em>
155 <h2>Digression -- HTTP Overview</h2><ul>
156 <li>Request/Response</li>
157
158 <li>Request is command followed by headers followed by body</li>
159
160 <li>Response is error code followed by headers followed by body</li>
161
162 <li>No welcome message</li>
163
164 </ul>
165 <hr />
166 <em>Tara (Family, season 5) -- ...in terms of the karmic cycle.</em>
167 <h2>Example HTTP Sessions</h2><ul>
168 <li>Client</li>
169 </ul>
170
171 <pre>
172 GET /foo/bar.html HTTP/1.0
173 Host: www.example.org
174 &lt;blank line&gt;
175 </pre>
176
177 <ul><li>Server</li></ul>
178
179 <pre>
180 HTTP/1.0 200 OK
181 Content-Type: text/html
182
183 &lt;html&gt;&lt;body&gt;lalalala&lt;/body&gt;&lt;/html&gt;
184 </pre>
185
186 <hr />
187 <em>Giles (Family, season 5) -- And you are talking about what on earth?</em>
188 <h2>Modules -- httplib</h2><ul>
189 <li>Low-level interface to innards of HTTP</li>
190
191 <li>Absolute control</li>
192
193 <li>No abstractions</li>
194
195 </ul>
196 <hr />
197 <em>Mr. MacLay (Family, season 5) -- We know how to control her...problem.</em>
198 <h2>Modules -- httplib -- example</h2><ul>
199 <li>Note: usually, the Host header is important<ul><li>Virtual hosting</li>
200 </ul></li></ul>
201
202 <pre>
203 &gt;&gt;&gt; import httplib
204 &gt;&gt;&gt; h=httplib.HTTP("moshez.org")
205 &gt;&gt;&gt; h.putrequest('GET', '/')
206 &gt;&gt;&gt; h.putheader('Host', 'moshez.org')
207 &gt;&gt;&gt; h.endheaders()
208 &gt;&gt;&gt; h.getreply()
209 (200, 'OK', &lt;mimetools.Message instance at 0x81220dc&gt;)
210 &gt;&gt;&gt; h.getfile().read(10)
211 "&lt;HTML&gt;\n&lt;HE"
212 </pre>
213 <hr />
214 <em>Anya (Family, season 5) -- ...and it was fun!</em>
215 <h2>Modules -- urlparse</h2><ul>
216 <li>urlparse.urljoin -- like os.path.join for URLs</li>
217
218 <li>For path manipulation<ul><li>urlparse.urlsplit</li>
219
220 <li>urlparse.urlunsplit</li>
221 </ul></li>
222
223 </ul>
224 <hr />
225 <em>Buffy (Family, season 5) -- You know what, you guys, just leave it here.</em>
226 <h2>Downloading Dilbert</h2>
227 <pre>
228 import urllib2, re
229
230 URL = 'http://www.dilbert.com/'
231 f = urllib2.urlopen(URL)
232 s = f.read()
233 href = re.compile('&lt;a href="(/comics/.*?/dilbert.*?gif)"&gt;')
234 m = href.search(value)
235 f = urllib2.urlretrieve(urlparse.urljoin(URL, m.group(1)),
236                         "dilbert.gif")
237 </pre>
238 <hr />
239 <em>Tara (Family, season 5) -- That was funny if you [...] are a complete dork.</em>
240 <h2>Downloading Dark Angel Transcripts</h2><ul>
241 <li>Common situation of mass download</li></ul>
242
243 <pre>
244 import urllib2, htmllib, formatter, posixpath
245 URL="http://www.darkangelfan.com/episode/"
246 LINK_RE = re.compile('/trans_[0-9]+\.shtml$')
247 s = urllib2.urlopen(URL).read()
248 h = htmllib.HTMLParser(formatter.NullFormatter())
249 h.feed(s)
250 links = [urlparse.urljoin(URL, link)
251               for link in h.anchorlist if LINK_RE.search(link)]
252 ### -- really download --
253 for link in links:
254     urllib2.urlretrieve(link, posixpath.basename(link))
255 </pre>
256
257 <hr />
258 <em>Intern (Family, season 5) -- Yeah. That makes like five this month.</em>
259 <h2>Downloading Dark Angel Transcripts (select)</h2>
260
261 <pre>
262 class Downloader:
263
264     def __init__(self, fin, fout):
265         self.fin, self.fout, self.fileno = fin, fout, fin.fileno
266
267     def read(self):
268         buf = self.fin.read(4096)
269         if not buf:
270             for f in [self.fout, self.fin]: f.close()
271             return 1
272         self.fout.write(buf)
273 </pre>
274 <hr />
275 <em>Joyce (Ted, season 2) -- I've been looking for the right moment.</em>
276 <h2>Downloading Dark Angel Transcripts (select, cont'd)</h2><ul>
277 <li>Same code up to 'really download'</li></ul>
278
279 <pre>
280 downloaders = [Downloader(urllib2.urlopen(link),
281                  open(posixpath.basename(link), 'wb'))
282                                       for link in links]
283 while downloaders:
284     toRead = select.select(None, [downloaders], [], [])
285     for downloader in toRead:
286          if downloader.read():
287              downloaders.remove(downloader)
288 </pre>
289 <hr />
290 <em>Buffy (Family, season 5) -- Tara's damn birthday is just one too many things for me to worry about.</em>
291 <h2>Downloading Dark Angel Transcripts (threads)</h2><ul>
292 <li>Bare bones example</li></ul>
293
294 <pre>
295 import threading
296
297 for link in links:
298     Thread(target=urllib2.urlretrieve,
299            args=(link,posixpath.basename(link)))
300 </pre>
301 <hr />
302 <em>Buffy (Ted, season 2) -- Sounds like fun.</em>
303 <h2>Digression - twisted.web.client</h2><ul>
304 <li>Part of the Twisted networking framework</li>
305
306 <li>High level interface to HTTP client</li>
307
308 <li>Completely asynchronous</li>
309
310 <li>Reports results via callbacks</li>
311
312 <li>client.getpage("http://www.yahoo.com").addCallbacks(gotResult, gotError)</li>
313
314 </ul>
315 <hr />
316 <em>Buffy (Ted, season 2) -- You're supposed to use your powers for good!</em>
317 <h2>Downloading Dark Angel Transcripts (web.client)</h2>
318 <pre>
319 from twisted.web import client
320 from twisted.internet import import reactor, defer
321
322 defer.DeferredList(
323 [client.downloadPage(link, posixpath.basename(link))
324             for link in links]).addBoth(lambda _: reactor.stop())
325 reactor.run()
326 </pre>
327 <hr />
328 <em>Ted (Ted, season 2) -- You don't have to worry about anything.</em>
329 <h2>HTTP Authentication</h2><ul>
330 <li>Client attempts to connect</li>
331
332 <li>Server sends back a 401 (please authenticate)</li>
333
334 <li>Client sends same request back -- with auth tokens</li>
335
336 <li>Only HTTP Basic authentication widely supported</li>
337
338 <li>Client can send auth tokens on more requests automatically</li>
339
340 </ul>
341 <hr />
342 <em>Buffy (Ted, season 2) -- Ummm... Who are these people?</em>
343 <h2>HTTP Authentication - manually</h2><ul>
344 <li>In HTTP, authentication is a header</li>
345
346 <li>Base authentication is sending username and password</li>
347 </ul>
348 <pre>
349 user = 'moshez'
350 password = 's3kr1t'
351 import httplib
352 h=httplib.HTTP("localhost")
353 h.putrequest('GET', '/protected/stuff.html')
354 h.putheader('Authorization',
355             base64.encodestring(user+":"+password).strip())
356 h.endheaders()
357 h.getreply()
358 print h.getfile().read()
359 </pre>
360 <hr />
361 <em>Tara (Family, season 5) -- And, uh, these are my-my friends.</em>
362 <h2>HTTP Authentication - urllib2</h2><ul>
363 <li>Can read username/password from URL</li>
364
365 <li>urllib2.urlopen("http://moshez:s3krit@example.com"
366                     "/protected/stuff.html")</li>
367
368 </ul>
369 <hr />
370 <em>Xander (Ted, season 2) -- I am really jinxing the hell out of us.</em>
371 <h2>Further Reading</h2><ul>
372 <li>htmllib docs <a href="http://www.python.org/doc/current/lib/module-htmllib.html">http://www.python.org/doc/current/lib/module-htmllib.html</a></li>
373
374 <li>sgmllib docs<a href="http://www.python.org/doc/current/lib/module-sgmllib.html">http://www.python.org/doc/current/lib/module-sgmllib.html</a></li>
375
376 <li>urllib docs<a href="http://www.python.org/doc/current/lib/module-urllib.html">http://www.python.org/doc/current/lib/module-urllib.html</a></li>
377
378 <li>urllib2 docs<a href="http://www.python.org/doc/current/lib/module-urllib2.html">http://www.python.org/doc/current/lib/module-urllib2.html</a></li>
379
380 <li>httplib docs<a href="http://www.python.org/doc/current/lib/module-httplib.html">http://www.python.org/doc/current/lib/module-httplib.html</a></li>
381
382 <li>re docs<a href="http://www.python.org/doc/current/lib/module-re.html">http://www.python.org/doc/current/lib/module-re.html</a></li>
383
384 <li>HTTP RFC<a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http://www.w3.org/Protocols/rfc2616/rfc2616.html</a></li>
385
386 <li>W3C HTML Page<a href="http://www.w3.org/MarkUp/">http://www.w3.org/MarkUp/</a></li>
387
388 <li>Twisted<a href="http://twistedmatrix.com">http://twistedmatrix.com</a></li>
389
390 </ul>
391 <hr />
392 <em>Willow (Ted, season 2) -- 'Book-cracker Buffy', it's kind of her nickname.</em>
393 <h2>Questions?</h2>
394 <em>Buffy (Family, season 5) -- I let you come, now sit down and look studious.</em>
395 <h2>Bonus Slides</h2>
396 <em>Tara (Family, season 5) -- You always make me feel special.</em>
397
398 <h2>Cookies</h2><ul>
399 <li>Carry state from one page to another</li>
400
401 <li>Server sends header: Set-Cookie</li>
402
403 <li>Client sends on later requests header: Cookie</li>
404
405 </ul>
406 <hr />
407 <em>Ted (Ted, season 2) -- Who's up for dessert? I made chocolate-chip cookies!</em>
408 <h2>urllib2 cookies</h2><ul>
409 <li>Unfortunately, no automatic cookie jar support</li>
410
411 <li>Can manually use .info() to read cookies...</li>
412
413 <li>...and the Request() API to send them to the server</li>
414
415 </ul>
416 <hr />
417 <em>Joyce (Ted, season 2) -- Mm! Buffy, you've got to try one of these!</em>
418 <h2>Logging Into Advogato</h2>
419 <pre>
420
421 import urllib2
422
423 u = urllib2.urlopen("http://advogato.org/acct/loginsub.html",
424                     urllib2.urlencode({'u': 'moshez',
425                                        'pass': 'not my real pass'})
426 cookie = u.info()['set-cookie']
427 cookie = cookie[:cookie.find(';')]
428 r = Request('http://advogato.org/diary/post.html',
429             urllib2.urlencode(
430             {'entry': open('entry').read(), 'post': 'Post'}),
431             {'Cookie': cookie})
432 urllib2.urlopen(r).read()
433 </pre>
434
435 <hr />
436 <em>Anya (Family, season 5) -- I have a place in the world now.</em>
437 <h2>On Being Nice - Robots</h2><ul>
438 <li>Some sites don't want automatic crawlers</li>
439
440 <li>It is up to you whether to play nice</li>
441
442 <li>But you should know the rules before you break them</li>
443
444 <li>Robots file -- at /robots.txt</li>
445
446 </ul>
447 <hr />
448 <em>Willow (Ted, season 2) -- There were design features in that robot that pre-date...</em>
449 <h2>Using robotparser</h2>
450 <pre>
451 import robotparser
452 rp = robotparser.RobotFileParser()
453 rp.set_url('http://www.example.com/robots.txt')
454 rp.read()
455 if not rp.can_fetch('', 'http://www.example.com/'):
456     sys.exit(1)
457 </pre>
458
459 <hr />
460 <em>Buffy (Ted, season 2) -- Tell me you didn't keep any parts.</em>
461 <h2>webchecker</h2><ul>
462 <li>In the source distribution, in Tools/</li>
463
464 <li>Understands robots.txt</li>
465
466 <li>Can override which links gets chased</li>
467
468 </ul>
469 <hr />
470 <em>Willow (Ted, season 2) -- What do you mean, check him out?</em>
471 <h2>websucker</h2><ul>
472 <li>In the source distribution, in Tools/</li>
473
474 <li>Uses webchecker as a module</li>
475
476 <li>Saves the pages it downloads</li>
477
478 </ul>
479 <hr />
480 <em>Buffy (Ted, season 2) -- Find out his secrets, hack into his life.</em>
481
482 </body></html>