#!/usr/bin/env python
# Copyright, 2007, Brian Mingus, Trevor Pincock, Laura Rassbach
#
# This file is part of get_ratings
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
"""
Download and create a list of quality articles and the ranked
dimensions
"""
debug = True
# Standard Library Imports
# For downloading, searching, file operations, stack access
import datetime
import urllib2
import thread
import sys
import re
# Third Party Imports
# For walking the DOM
from BeautifulSoup import BeautifulSoup
# So we don't parse all of every page
from BeautifulSoup import SoupStrainer
class GetRatings:
def debug(self, msg, name, error=False):
"""
error is a boolean value - is this an error or not?
Pass str(sys._getframe().f_code.co_name) as name
"""
try:
if error:
print "Error: " + str(name) + ": " + str(msg)
elif debug:
print "Debug: " + str(name) + ": " + str(msg)
except:
print "Even your debug statements are buggy!"
def URLOpener(self):
"""
Returns a urllib2 object
"""
self.opener = urllib2.build_opener()
self.opener.addheaders = [('User-agent', 'Mozilla/5.0')]
return self.opener
def GetHTML(self, url):
"""
Returns the HTML of a url passed to it
"""
website = self.URLOpener()
website = website.open(url.decode("utf-8"))
website = website.read()
return website
def Beautify(self, html):
"""
Returns a BeautifulSoup object based on input html
"""
return BeautifulSoup(html)
def GetIndex(self):
"""
Returns a BeautifulSoup object of the main article quality index at
http://en.wikipedia.org/wiki/Wikipedia:Version_1.0_Editorial_Team/Index
"""
index = self.Beautify(self.GetHTML('http://en.wikipedia.org/wiki/Wikipedia:Version_1.0_Editorial_Team/Index'))
return index
def isQualityTable(self, page):
"""
Takes a BeautifulSoup object and determines if it is an article quality table containing the quality metrics of articles
It's heuristic is simply the *lack* of a digit in the last character of the URL combined with
the existence of the word 'Comments'.
"""
if not page.html.find(href=re.compile('/wiki/Wikipedia:Version_1.0_Editorial_Team/.*/[0-9]+')):
if page.html.find(text='Comments'):
return True
else:
return False
def isListofQualityTables(self, page):
"""
Takes a BeautifulSoup object and determines if it is a list of quality metrics of articles (a list of lists)
It's heuristic is whether there is a URL whose last character is a digit anywhere on the page
"""
if page.html.find(href=re.compile('/wiki/Wikipedia:Version_1.0_Editorial_Team/.*/[0-9]+')):
return True
else:
return False
def GetTitle(self, page):
"""
Takes a BeautifulSoup object and returns the title of the article
"""
name = str(sys._getframe().f_code.co_name) # Debugging
title_match = '^.*Team\/(.*) - Wikipedia, the free encyclopedia$'
title_match = re.compile(title_match)
try:
title = title_match.search(page.html.title.string).group(1)
return title
except ValueError:
self.debug("Couldnt get the article title from the
", name, True)
def ParseDumpArticleQualityTable(self, page):
"""
This function has the unsightly job of parsing the table row by row and writing the output to a CSV
We don't care about most of the data it sees - just uncomment whatever else you want
"""
name = str(sys._getframe().f_code.co_name) # Debugging
Article, Importance, Date, Assessment, Version, Comments = '','','','','',''
# TODO: str(page) is a major inefficiency in this script
table = BeautifulSoup(str(page), parseOnlyThese=SoupStrainer('table', border='1'))
trs = table.tr.nextSibling.nextSibling.findNextSiblings('tr')
del trs[len(trs) - 1] # Last item is extraneous
date = datetime.date.today()
f = open('ETQR' + str(date) + '.csv', 'a')
write = True
for i in range(len(trs)):
pointer = trs[i].findNext('td')
try:
Article = pointer.next.string # Article
except:
write = False
pointer = pointer.findNext('td') #Importance = pointer.next.string
pointer = pointer.findNext('td') # Date = pointer.next.string
pointer = pointer.findNext('td') # Assessment
try:
Assessment = pointer.next.a.string # Odd man out
except:
# This is for FA/GA articles
try:
Assessment = pointer.findNext('b').findNext('a').string
except:
write = False
pointer = pointer.findNext('td') # Version = pointer.next.string
pointer = pointer.findNext('td') # Comments = pointer.next.string
if write:
# Only log thesex
if Assessment in ['FA','A', 'GA', 'B', 'Start']:
data = str(Article) + '____' + str(Assessment)
data = data.encode("utf-8") + '\n'
self.debug("Write: " + str(write) + ": " + data, name)
f.write(data)
f.close()
def ParseDumpListOfQualityTables(self, page):
"""
Knows how to parse a LIST of quality tables, and then call ParseDumpArticleQualityTable on them
"""
name = str(sys._getframe().f_code.co_name) # Debugging
links = page.html.findAll('a', href=re.compile('/wiki/Wikipedia:Version_1.0_Editorial_Team/.*/[0-9]+'))
title = self.GetTitle(page)
self.debug("len(links): " + str(len(links)), name)
for i in range(len(links)):
try:
url = 'http://en.wikipedia.org' + str(links[i]['href']).decode("utf-8")
except:
return 0
try:
new_page = self.Beautify(self.GetHTML(url).decode("utf-8"))
except:
self.debug("Check page ", name, True)
try:
thread.start_new_thread(self.ParseDumpArticleQualityTable, (new_page,))
#self.ParseDumpArticleQualityTable(new_page)
#self.WriteToCSVRecursive(new_page)
except:
self.debug("Exception in " + str(title), name, True)
def ParseIndex(self, index):
"""
Parses the main index and returns a BeautifulSoup list of all the appropriate tags
"""
# This gets n - 1 articles - small bug.
index_list = index.html.findAllNext('a',href=re.compile('.*articles_by_quality.*'),text=re.compile('.*articles by quality.*'))
return index_list
def WriteToCSVRecursive(self, page):
"""
Not actually recursive, but if it's actually needed in the future it almost works. Uncomment below...
"""
name = str(sys._getframe().f_code.co_name) # Debugging
title = self.GetTitle(page)
self.debug('', name)
if self.isQualityTable(page):
self.debug("Table: " + title, name)
thread.start_new_thread(self.ParseDumpArticleQualityTable, (page,))
#self.ParseDumpArticleQualityTable(page)
# ParseDumpListOfQualityTables calls WriteToCSVRecursive
elif self.isListofQualityTables(page):
self.debug("List: " + title, name)
self.ParseDumpListOfQualityTables(page)
else:
self.debug("Unknown: " + title, name)
def GetList(self):
"""
1. Download the index
2. Parse it with BeautifulSoup to get a list of articles
3. Determine if we have a list of articles or a list of lists of articles
"""
index = self.GetIndex() # 1.
list_of_article_titles = self.ParseIndex(index) # 2.
for i in range(len(list_of_article_titles)): # 3.
url = 'http://en.wikipedia.org' + list_of_article_titles[i].parent['href']
page = self.Beautify(self.GetHTML(url.decode("utf-8")))
self.WriteToCSVRecursive(page)
def main(self):
self.GetList()
if __name__ == "__main__":
articleList = GetRatings()
sys.exit(articleList.main())