Wykorzystanie WebKit/PyQt4 do zbierania danych, część 1
Udostępnienie kompletnego silnika przeglądarki WebKit w bibliotece Qt i zarazem PyQt4 dało programistom pole do tworzenia nowych aplikacji operujących na stronach internetowych. Przykładowo przeglądając kod źródłowy różnych stron zobaczymy że reklamy (szczególnie flashowe, reklamy z systemów reklamowych) umieszczane są w postaci wklejek JavaScriptowych - nie wiemy jaka reklama zostanie wyświetlona i gdzie będzie kierować. By dostać takie informacje musimy mieć dostęp do zrederowanej strony, gdzie ta wklejka wyświetliła konkretną reklamę. Można osiągnąć to stosująć QtWebKit. Nasza aplikacja rozwijana w szeregu artykułów będzie zbierać dane o wyświetlanych reklamach z zadanych stron internetowych. Biznesowo informacje kto i gdzie emituje reklamy mogą być przydatne (czy też np. kontrola własnych kampanii reklamowych).
Plan
Zaczniemy od aplikacji zbierającej reklamy i zapisującej dane do bazy SQLite. Cele dla aplikacji to:- Automatycznie ładuje po kolei strony z podanej listy N razy
- Po każdym załadowaniu strony wyszukuje w jej kodzie reklam i zapisuje do bazy ClickTag (URL reklamy), źródło itd.
- Rozpoznawane formaty reklam rozwiązane za pomocą prostych funkcji umożliwiając łatwą rozbudowę możliwości "zbieracza"
- Ładuje adres ClickTag i zapisuje do bazy tytuł i adres strony finalnej
Zbieracz
Plan działania aplikacji jest następujący:- Klikamy start - aplikacja pobiera pierwszą stronę z listy i ładuje ją
- Po załadowaniu odświeża ją N-razy
- Za każdym razem wyciąga ze strony reklamy i zapisuje do bazy (strona, na której była reklama, adres na który reklama kieruje)
- Kontynuuje pobierając kolejną stronę z listy aż do przerobienia wszystkich stron

# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from gather import Ui_gatherer
class GatherAds(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_gatherer()
self.ui.setupUi(self)
# ilość odświeżeń strony
self.refreshSite = 5
# lista stron
self.sites = [{'url': 'http://auth.gazeta.pl/googleAuth/login.do', 'site': 'gazeta.pl'},
{'url': 'http://kobieta.gazeta.pl/kobieta/0,0.html', 'site': 'gazeta.pl'},
{'url': 'http://kobieta.wp.pl', 'site': 'wp.pl'},
]
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = GatherAds()
myapp.show()
sys.exit(app.exec_())
Powyższy kod wyświetla okno aplikacji, oraz zawiera przykładową listę stron, z których będą wyciągane reklamy. self.sites jest listą słowników, z których każdy zawiera url - adres URL strony, oraz site - tekstową nazwę głównego serwisu (grupowanie stron z jednego serwisu).
Kolejny etap to implementacja mechanizmu ładującego po kolei strony. Musimy uwzględnić to że kolejną stronę możemy załadować dopiero po wczytaniu poprzedniej. Oto implementacja takiego rozwiązania:# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from gather import Ui_gatherer
class GatherAds(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_gatherer()
self.ui.setupUi(self)
self.refreshSite = 5
self.currentIndex = 0
self.sites = [{'url': 'http://auth.gazeta.pl/googleAuth/login.do', 'site': 'gazeta.pl'},
{'url': 'http://kobieta.gazeta.pl/kobieta/0,0.html', 'site': 'gazeta.pl'},
{'url': 'http://kobieta.wp.pl', 'site': 'wp.pl'},
]
QtCore.QObject.connect(self.ui.startButton,QtCore.SIGNAL("clicked()"), self.start)
QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("loadFinished (bool)"), self.loadFinished)
QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("loadProgress (int)"), self.loadProgress)
def start(self):
"""
Start loading the web pages
"""
self.ui.startButton.setEnabled(False)
nexturl = self.__getNextUrl()
if nexturl:
self.ui.webView.load(nexturl)
def loadFinished(self):
"""
A page was loaded - get the data and load next page
"""
page = self.ui.webView.page()
frame = page.currentFrame()
content = frame.toHtml()
print u'Page content, got %s bytes' % len(content)
# process the data here
self.currentIndex += 1
nexturl = self.__getNextUrl()
if nexturl:
self.ui.webView.load(nexturl)
def loadProgress(self, progress):
"""
Print the progress of page load
"""
print progress
def __getNextUrl(self):
"""
Return next URL in list
"""
if len(self.sites) - 1 >= self.currentIndex:
newurl = QtCore.QUrl(self.sites[self.currentIndex]['url'])
else:
print 'No next url'
newurl = False
return newurl
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = GatherAds()
myapp.show()
sys.exit(app.exec_())
Najważniejsza jest tutaj metoda __getNextUrl, która zwraca URL podanego elementu listy. self.currentIndex zaczyna od 0 (pierwszy element listy) i po wczytaniu strony w loadFinished jest inkrementowany o 1, po czym znowu wywoływana jest metoda __getNextUrl zwracająca już kolejny URL - i tak aż do końca (gdy metoda zwróci False). Metoda start podpięta pod kliknięcie przycisku "Start" ładuje pierwszą stronę rozpoczynając tym samym cały cykl. Pomocniczo wykorzystałem loadProgress by wyświetlać w konsoli postęp ładowania stron.
Strony już się ładują, lecz można ten proces przyśpieszyć. Nie potrzebujemy pobierać np grafik. Za pomocą QtWebKit.QWebSettings można wyłączyć ich pobieranie - przyśpieszając tym samym ładowanie się stron. Wystarczy do __init__ dodać poniższy kod (i zaimportować QtWebKit):s = self.ui.webView.settings()
s.setAttribute(QtWebKit.QWebSettings.AutoLoadImages, False)
s.setAttribute(QtWebKit.QWebSettings.JavascriptCanOpenWindows, False)
s.setAttribute(QtWebKit.QWebSettings.PluginsEnabled, False)
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui, QtWebKit
from gather import Ui_gatherer
class GatherAds(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_gatherer()
self.ui.setupUi(self)
self.refreshSite = 3
self.currentIndex = 0
self.currentRefresh = 0
self.sites = [{'url': 'http://auth.gazeta.pl/googleAuth/login.do', 'site': 'gazeta.pl'},
{'url': 'http://kobieta.gazeta.pl/kobieta/0,0.html', 'site': 'gazeta.pl'},
{'url': 'http://kobieta.wp.pl', 'site': 'wp.pl'},
]
s = self.ui.webView.settings()
s.setAttribute(QtWebKit.QWebSettings.AutoLoadImages, False)
s.setAttribute(QtWebKit.QWebSettings.JavascriptCanOpenWindows, False)
s.setAttribute(QtWebKit.QWebSettings.PluginsEnabled, False)
QtCore.QObject.connect(self.ui.startButton,QtCore.SIGNAL("clicked()"), self.start)
QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("loadFinished (bool)"), self.loadFinished)
QtCore.QObject.connect(self.ui.webView,QtCore.SIGNAL("loadProgress (int)"), self.loadProgress)
def start(self):
"""
Start loading the web pages
"""
self.ui.startButton.setEnabled(False)
nexturl = self.__getNextUrl()
if nexturl:
self.ui.webView.load(nexturl)
def loadFinished(self):
"""
A page was loaded - get the data and load next page
"""
page = self.ui.webView.page()
frame = page.currentFrame()
content = frame.toHtml()
print u'Page content, got %s bytes' % len(content)
# process the data here
if self.currentRefresh < self.refreshSite:
print 'Refresh +1'
self.currentRefresh += 1
else:
print 'Index +1'
self.currentRefresh = 0
self.currentIndex += 1
nexturl = self.__getNextUrl()
if nexturl:
self.ui.webView.load(nexturl)
def loadProgress(self, progress):
"""
Print the progress of page load
"""
print progress
def __getNextUrl(self):
"""
Return next URL in list
"""
# set the progress bar of pages loaded
progress_value = (float(self.currentIndex)/float(len(self.sites)))*100
self.ui.sitesBar.setValue(progress_value)
# set the progress bar of refreshes
progress_value = (float(self.currentRefresh)/float(self.refreshSite))*100
self.ui.iterationBar.setValue(progress_value)
if len(self.sites) - 1 >= self.currentIndex:
newurl = QtCore.QUrl(self.sites[self.currentIndex]['url'])
else:
print 'No next url'
newurl = False
return newurl
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = GatherAds()
myapp.show()
sys.exit(app.exec_())
Nasza aplikacja jest już kompletna jeżeli chodzi o ładowanie stron. Teraz trzeba napisać logikę odpowiedzialną za wydobywanie danych ze strony i zapis odpowiednich wyników do bazy danych. Tym zajmiemy się w następnym artykule.

Comment article