Web scraping: Interactive prototyping
Prototyping a solution for Web scraping a friend's blog interactively…
- Fetching the root page:
>>> import urllib >>> u0 = "http://blog.tapuz.co.il/linsom/" >>> r0 = urllib.urlopen(u0) # Response. Nice feature: urllib handles redirections transparently:
>>> r0.geturl() 'http://www.tapuz.co.il/blog/userBlog.asp?FolderName=linsom' Not so nice: response body seems to end prematurely. Need to specify size to read()? No, doesn't seem to help.
>>> r0 = urllib.urlopen(u0) >>> b0 = r0.read(int(1e6)) # Body. >>> len(b0) 335709 >>> b0[-40:] # How ends? 'tyFF2();\r\n }\r\n\t}\t\r\n\r\n \r\n' Docs say: "caveat: the read() method, if the size argument is omitted or negative, may not read until the end of the data stream". Foo.
Bah, it's their rotten HTML.
- Saving the response so can analyze it:
>>> import os >>> os.chdir("/tmp") # Someplace to dump stuff. >>> open("b0","w").write(r0.read()) Note: if the response body is not read immediately after sending the request, the connection closes, apparently, and throws "socket.error: (104, 'Connection reset by peer')".
Oy, but note the encoding, too:
>>> r0.info() >>> r0.info().items() [('x-powered-by', 'ASP.NET'), ('set-cookie', 'TapuzBlog=blogId59549=1&Blognum59549=yes&blogId=1; expires=Sat, 22-Nov-2008 22:00:00 GMT; domain=tapuz.co.il; path=/, ASPSESSIONIDQSCDRBQS=BHLEMCODAENIMBFCIIDJCABP; path=/'), ('expires', 'Fri, 21 Nov 2008 17:31:49 GMT'), ('server', 'Microsoft-IIS/6.0'), ('connection', 'close'), ('cache-control', 'private'), ('date', 'Sat, 22 Nov 2008 10:11:50 GMT'), ('content-type', 'text/html; Charset=windows-1255')] - Crawling:
Blog's root page show the last few entries, but it would be more convenient to harvest them one by one, no?
- Links to posts seem to all look like this:
href='ViewEntry.asp?EntryId=1352026' so can be matched with a simple regular expression:
>>> import re >>> m = re.search(r"href='(ViewEntry\.asp\?EntryId=.+?)'>", b0) >>> m.group(1) 'ViewEntry.asp?EntryId=1371470' - Fetch that post (then recursively):
>>> u1 = u0[ : u0.rindex("/")] + "/" + m.group(1) >>> r1 = urllib.urlopen(u1); b1 = r1.read(int(1e6)) >>> len(b1) 307991 This response, too, seems truncated, ends the same, but I wouldn't be surprised if their horrid HTML just ends like that.
- Links to posts seem to all look like this:
- Parsing HTML:
"Sterilize" the HTML so can render it in Konqueror without running embedded scripts, ignoring styling, etc, and in UTF-8:
$ ./sterilize_html.py < b1 > b1-sterilized (Cleaning the main HTML: 17,355 lines, replaced 235 occurences of "http://" with "httpXXX://", 65 "<\s*SCRIPT" with "<XXXscript", 7 "<\s*iframe" with "<XXXiframe", 67 "on([a-z]+=)" with "onXXX\1", removed leading spaces from 12,350 lines. And trailing. File shrunk from 846KB to 301KB. Page won't render because embedded JavaScript contains HTML. Need to also disable "<object", "<param", "marquee", and "<embed". Still won't render: it's the embedded scripts. Did everything again, incrementally, found 29 scripts that rearrange content blocks on the page; they all call "insertBefore". To reveal structure: replaced 286 "border=" with "borderXXX=", 660 "width=", 496 "ing=", 646 "style=", 92 "color=", 438 "height=", etc. Crude, but works. Told you it's horrible.)
Found the text:
על מגע touch על מגע touchעל מגע המגע המחבר בין אדם לאדם ויש כל מיני סוגים כל אדם זקוק למגע ויש הבדל עצום בין מגע לנגיעה חטופה אדם שרק נוגע בחטוף בודק או רק זורק סימן של קירבה אדם שיודע להושיב את ידו לגעת - מגיע כל אדם מתמלא ממגע יש כאלה שמפחדים נבהלים בדיוק כמו שהנפש שלהם לא משוחררת כך גופם קפוא מלקבל או לתת נגיעה היא סוג...פורסם ב21 בנובמבר 2008, 22:47 במדור 
על מגעהמגע המחבר בין אדםלאדםויש כל מיני סוגיםכל אדם זקוק למגעויש הבדל עצום בין מגע לנגיעה חטופהאדם שרק נוגע בחטוף בודק או רק זורק סימן של קירבהאדם שיודע להושיב את ידו לגעת - מגיעכל אדם מתמלא ממגעיש כאלה שמפחדיםנבהליםבדיוק כמו שהנפש שלהם לא משוחררתכך גופם קפוא מלקבל או לתתנגיעה היא סוג של רצון לקשרמגע הוא החיבור המופלא לכל סוג של אהבהמגע מרפאנותן את הביטחון שלולילד - בזוגיות - בחברותבמשפחהלתת לאנשים שחשובים לנו לדעת שאנחנו שם בשבילםויותר ממילה פרח או מתנההמגעהחיבוק שיכול תמיד להחזיר בנו נשימה© כל הזכויות שמורות רויתשחם RAVIT SHAHAM
"התעוררות "Now, how to find it programmatically?!
- Beautiful Soup (BS):
- Install Beautiful Soup: unpacked the tarball, then:
$ sudo python setup.py install This copied everything to /usr/lib/python2.5/site-packages/.
- Beautiful Soup doesn't guess the encoding right, it seems.
>>> b1soup=bs.BeautifulSoup(b1,fromEncoding="windows-1255") Doing it myself works:
>>> b1soup=bs.BeautifulSoup(unicode(b1,"windows-1255","replace")) >>> file("b1-soup","w").write(b1soup.prettify()) BS outputs UTF-8 by default (I think). Yes, docs say: "Beautiful Soup stores only Unicode strings".
- BS code seems a bit, eh, archaic. Not too clean, nor efficient. Inconvenient for interactive DOM exploration. Use Firebug!?
- Install Beautiful Soup: unpacked the tarball, then:
…
Notes
(None.)
(Appending notes disabled temporarily.)
Last modified 2009-08-03 09:44:37 +0000