Wykorzystanie WebKit/PyQt4 do zbierania danych, część 2
3 November 2009
Comments
Kolejny etap to napisanie parserów wydobywających linki reklam. W przypadku wielu flashowych reklam link, na który kieruje reklama przekazuje się w parametrze animacji, który zazwyczaj nazywany jest clickTag (lub podobna nazwa). W przypadku innych systemów - trzeba rozpoznać wzór i napisać wyrażenie regularne. Pomocne w tym celu będzie przejrzenie kodu HTML zwracanego przez webkita. Przykładowo widżet AdTaily wstawia linki postaci:
<a style="position: relative; font-weight: normal; text-align: left; background-image: none; background-repeat: initial; background-attachment: initial; -webkit-background-clip: initial; -webkit-background-origin: initial; background-color: initial; padding-left: 0px; padding-right: 0px; padding-top: 0px; padding-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; margin-bottom: 10px; display: block; width: 125px; height: 125px; background-position: initial initial; " href="http://www.megiteam.pl/" title="Hosting nowych technologii" rel="nofollow" target="_blank">
Parser AdTaily
- W katalogu z aplikacją stworzyłem plik parsers.py, który zawierał będzie wszystkie parsery.
- Poniżej pierwszy "parser" - wyciągający linki z widżetu AdTaily:
# -*- coding: utf-8 -*- from re import findall def get_adtaily(html): """ Extract data from adtaily widgets """ links = findall(r'background-position: initial initial; " href="(.*?)"', unicode(html)) ret = [] for i in links: if not i.startswith('http://www.adtaily'): ret.append(i) return ret
- Importujemy parsers w run.py i modyfikujemy w tym pliku metodę loadFinished do postaci:
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) links = get_adtaily(content) print links 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)
- W powyższym kroku zmodyfikowaliśmy metodę tak by przekazywała kod HTML pobranej strony do metody get_adtaily i wyświetlała zwróconą listę. Teraz jeżeli odpalisz aplikację na jakiejś stronie z widżetem AdTaily (np. www.python.rk.edu.pl) to powinieneś zobaczyć wyszukane URLe.
Reklamy Flashowe - clickTag
- W przypadku reklam flashowych powszechnie stosuje się link pod zmienną clickTag - gdzie umieszcza się link przekierowujący (liczący kliknięcia przez serwis wyświetlający reklamę) na "licznik" i z licznika na docelową stronę. Może to wyglądać nawet tak:
<embed height="200" width="750" name="td_flash" id="td_flash" wmode="opaque" swliveconnect="true" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" quality="best" menu="false" flashvars="clickTAG=http%3A%2F%2Fclk.tradedoubler.com%2Fclick%3Fp%3D74000%26a%3D1545102%26g%3D17957364%26pools%3D305907%2C282487&CLICKTAG=http%3A%2F%2Fclk.tradedoubler.com%2Fclick%3Fp%3D74000%26a%3D1545102%26g%3D17957364%26pools%3D305907%2C282487&clicktag=http%3A%2F%2Fclk.tradedoubler.com%2Fclick%3Fp%3D74000%26a%3D1545102%26g%3D17957364%26pools%3D305907%2C282487&clickTag=http%3A%2F%2Fclk.tradedoubler.com%2Fclick%3Fp%3D74000%26a%3D1545102%26g%3D17957364%26pools%3D305907%2C282487&ClickTag=http%3A%2F%2Fclk.tradedoubler.com%2Fclick%3Fp%3D74000%26a%3D1545102%26g%3D17957364%26pools%3D305907%2C282487" src="http://ads.open.pl/kreacje/2009/05_09/doplata/google_750x200_z_doplata.swf"/>
- Parsers dla "clickTag" będzie wyglądał tak:
def get_clickTag(html): """ Extract data from flash ads """ links = findall(r'clicktag=(.*?)"', unicode(html).lower()) ret = [] for i in links: ret.append(unquote(i)) return ret
- Jeżeli potrzebujesz pobierać reklamy z określonych serwisów to musisz sprawdzić w jaki sposób wstawiają reklamy i napisać pod to parsery. Powyższy parser clickTag będzie działał dla większości reklam flashowych wyświetlanych na popularnych serwisach. (unquote importujemy z urllib)
Zapis danych do bazy
Za obsługę baz danych w Qt odpowiada komponent QtSql. Jest on dostępny także w PyQt4, lecz jego API nie jest zgodne z DB API dla modułów Pythona (bo nie było projektowane z myślą o Pythonie).- Zaczynamy od dodania importu:
from PyQt4.QtSql import *
- Połączenie się z bazą jest proste, można umieścić poniższy kod w metodzie __init__:
Wybieramy sterownik - SQLite, następnie podajemy nazwę bazy i nawiązujemy "połączenie" (w przypadku SQLite nie trzeba podawać hosta, hasła i loginu użytkownika bazy).
self.db = QSqlDatabase.addDatabase("QSQLITE") self.db.setDatabaseName("ads") self.dbstatus = self.db.open() if self.dbstatus: print 'DB ok' else: print 'DB error'
- Potrzebujemy tabeli, do której będziemy wrzucać dane:
Wykonaj powyższe zapytanie np. z wiersza poleceń:
CREATE TABLE "ads_data" ( "id" integer NOT NULL PRIMARY KEY, "link" varchar(255) NOT NULL, "dest_title" varchar(255) NULL, "dest_url" varchar(255) NULL, "date" datetime NOT NULL, "source" varchar(255) NOT NULL, "is_parsed" bool NULL );
sqlite3 ./ads - Teraz należy dodawać do bazy wyszukane odnośniki:
Zaczynamy od stworzenia obiektu query = QSqlQuery(self.db) i wykonania zapytania SQL za pomocą metody exec_. Dane zostaną dodane, a jeżeli zapytanie nie powiedzie się - wyświetlony zostanie komunikat błędu
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) links = get_adtaily(content) links2 = get_clickTag(content) for i in links2: links.append(i) query = QSqlQuery(self.db) try: source = self.sites[self.currentIndex]['site'] except: return # dodajemy poszczególne odnośniki for link in links: qry = "INSERT INTO ads_data ('link', 'date', 'source', 'is_parsed') VALUES ('%s', '%s', '%s', 0);" % (link, date.today(), source) if query.exec_(qry): print u'Poszedł INSERT' else: print 'Insert Error' print qry print query.lastError().text() print 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)
- Powyższy przykład pokazuje gotowe rozwiązanie logowania linków do bazy danych. Warto rozbudować go o np. sprawdzanie czy dany link danego dnia został już dodany by uniknąć zbędnych duplikatów. Zebrane dane należy jeszcze przerobić - napisać drugą aplikację, która otworzy link i pobierze adres i tytuł strony, na której wyląduje (czym zajmiemy się w trzeciej części kursu).
- Poniżej metoda blokująca dodawanie duplikatów do bazy danych:
Tutaj wykorzystaliśmy zapytanie pobierające dane. Iterowanie przez pobrane wiersze odbywa się za pomocą query.next(), które zwraca True, jeżeli udało się zwrócić kolejny wiersz (stosuj "while query.next()" jeżeli chcesz przeiterować wszystkie wyniki). W naszym przypadku pobieramy jeden wiersz (liczenie) i interesuje nas pierwsza wartość zwrócona przez zapytanie. Metoda query.value(index) zwróci daną w postaci obiektu QVariant, który trzeba znormalizować np. toString() lub toInt().
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) links = get_adtaily(content) links2 = get_clickTag(content) for i in links2: links.append(i) query = QSqlQuery(self.db) try: source = self.sites[self.currentIndex]['site'] except: # no more sites return for link in links: cnt = 0 if query.exec_("SELECT COUNT(*) FROM ads_data WHERE date = '%s' AND source = '%s' AND link = '%s'" % (date.today(), source, link)): query.next() cnt = query.value(0).toInt()[0] print cnt if cnt < 1: qry = "INSERT INTO ads_data ('link', 'date', 'source', 'is_parsed') VALUES ('%s', '%s', '%s', 0);" % (link, date.today(), source) if query.exec_(qry): print u'Poszedł INSERT' else: print 'Insert Error' print qry print query.lastError().text() print else: print 'pass' 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)
Kod źródłowy
RkBlog
Comment article