[Python] Hoe info (attrib, value) van div element uitlezen?

Pagina: 1
Acties:

Onderwerpen


  • Woudloper
  • Registratie: November 2001
  • Niet online

Woudloper

« - _ - »

Topicstarter
Inleiding
Momenteel doe ik met enkele collega's mee met de Profcoach competitie. Een leuke competitie, maar deze biedt helaas onvoldoende statistische informatie. Graag wil ik van de verschillende teams diverse informatie uitlezen.

Aangezien het een website is, leek het mij verstandig dit met een cloud oplossing zoals ScraperWiki aan te pakken aangezien je hier veel libraries hebt en mogelijkheden om met Python, Ruby of PHP een script te runnen.

Diverse problemen zijn inmiddels opgelost, zoals het faken van een browser met Mechanize om te kunnen inloggen. De data is helaas slechts toegankelijk als je ingelogd bent:

Het tussentijdse resultaat van de scraper staat hier:Probleem/Vraagstelling:
Momenteel zit ik helaas in een probleem waar ik geen oplossing voor heb. Middels een cssselect vraag ik alle divs uit die de class player hebben.

Hieronder een stukje code zoals deze bij bovengenoemde scraper is gedefinieerd:
Python:
1
2
3
4
5
6
7
8
9
for el in root.cssselect("div.player"):
        tds = el
        #print lxml.html.tostring(tds)
        data = {
            'player_name' : tds.cssselect("div.name"),
            'player_points' : tds.cssselect("div.team"),
            'player_position' : get_position (tds)
        }
        print data


Deze levert de volgende structuur op bij het commando: lxml.html.tostring(tds):
HTML:
1
2
3
4
5
6
7
<div class="player pos4" personid="b8f3e432-850b-48fa-9fc4-ad4ea9cdfda0" slot="0">
    <div class="delete"></div>
    <div class="captain"></div>
    <div class="info"></div>
    <img src="http://cdn.eredivisielive.nl/images/teams/2/small/487825.jpg" width="34" height="50" alt=""><div class="name" title="Ryan Babel">Babel</div>
    <div class="team">+5</div>
</div>


Daadwerkelijke vragen:
  1. Hoe kan ik de title informatie uitlezen bij de div met class "name"?

    Momenteel diverse pogingen gedaan met gebruikmakend van etree of .text_content(), echter levert dat geen resultaat op. Enerzijds klaagt de applicatie dat het niet mogelijk is bij strings en .text_content() kan niet bij objecten van het type list
  2. Hoe kan ik de content van de class "team" opvragen?
  3. Hoe kan ik controleren of de class "captain" als CSS definitie display:block heeft meegekregen?

    Had ik dit anders als moeten veiligstellen bij het opvragen van de pagina middels mechanize? Probleem is echter dat deze geen generated content oplevert.

  • Juup
  • Registratie: Februari 2000
  • Niet online
Heb je text_content() al geprobeerd?

en attrib["class"] enzo?

[ Voor 28% gewijzigd door Juup op 22-09-2012 00:21 ]

Een wappie is iemand die gevallen is voor de (jarenlange) Russische desinformatiecampagnes.
Wantrouwen en confirmation bias doen de rest.


  • C0rnelis
  • Registratie: Juni 2010
  • Laatst online: 26-08 22:21
Ik zie niet helemaal wat je probeert te doen:

Is de output van
Python:
1
2
3
4
5
6
7
8
9
for el in root.cssselect("div.player"):
    tds = el
    #print lxml.html.tostring(tds)
    data = {
        'player_name' : tds.cssselect("div.name"),
        'player_points' : tds.cssselect("div.team"),
        'player_position' : get_position (tds)
    }
    print data


jouw input?
HTML:
1
2
3
4
5
6
7
<div class="player pos4" personid="b8f3e432-850b-48fa-9fc4-ad4ea9cdfda0" slot="0"> 
    <div class="delete"></div> 
    <div class="captain"></div> 
    <div class="info"></div> 
    <img src="http://cdn.eredivisielive.nl/images/teams/2/small/487825.jpg" width="34" height="50" alt=""><div class="name" title="Ryan Babel">Babel</div> 
    <div class="team">+5</div> 
</div>


Je kunt van veel libraries gebruik maken.. ook van BeautifulSoup? Je kunt daar gemakkelijk html doorzoeken, ook op classes.

Verder, voor het opvragen van CSS properties, kan ik je niet vertellen wat mechanize wel/niet kan en wat de verschillen zijn met Selenium, maar die laatste lijkt wel een methode te bieden voor het uitlezen van css-properties.

[ Voor 18% gewijzigd door C0rnelis op 22-09-2012 00:40 ]


  • Woudloper
  • Registratie: November 2001
  • Niet online

Woudloper

« - _ - »

Topicstarter
Juup schreef op zaterdag 22 september 2012 @ 00:20:
Heb je text_content() al geprobeerd?

en attrib["class"] enzo?
Ja, deze heb ik al geprobeerd. Zodra ik deze toevoeg aan de informatie bij de classes krijg ik de volgende foutmelding:
code:
1
2
Line 87 - 'player_name' : tds.cssselect("div.name").text_content(),
AttributeError: 'list' object has no attribute 'text_content'
C0rnelis schreef op zaterdag 22 september 2012 @ 00:31:
Ik zie niet helemaal wat je probeert te doen:

Is de output van Python de input voor HTM?
[/code]
Nee, dat is niet het geval. Ik wil graag een tabel opleveren met de volgende informatie via de volgende code op Scraperwiki:
NaamPositieCaptainSterspelerPunten
Dries MertensBasisspelerJaNee+4
Je kunt van veel libraries gebruik maken.. ook van BeautifulSoup? Je kunt daar gemakkelijk html doorzoeken, ook op classes.
Kan, maar dan introduceer je alleen maar extra libraries en verwacht ik dezelfde foutmeldingen zoals ik hierboven krijg wanneer ik mechanize gebruik.
Verder, voor het opvragen van CSS properties, kan ik je niet vertellen wat mechanize wel/niet kan en wat de verschillen zijn met Selenium, maar die laatste lijkt wel een methode te bieden voor het uitlezen van css-properties.
Dit laatste werkt ook wanneer je .cssselect("player") gebruikt middels de mechanize.

  • Klaasvaak
  • Registratie: Maart 2010
  • Laatst online: 12-09 21:10
De foutmelding geeft aan dat tds.cssselect("div.name") een list geeft. Dan zal het div element op index 0 zitten.

tds.cssselect("div.name")[0].text_content()

  • C0rnelis
  • Registratie: Juni 2010
  • Laatst online: 26-08 22:21
Een beetje door de documentatie bladerend kom je toch al snel uit op iets als

Python:
1
2
3
4
5
6
7
tree = lxml.html.fromstring(html)
players = tree.cssselect('.player')
for player in players:
    print 'Naam: %s' % player.cssselect('.name')[0].get('title')
    print 'Positie: %s' % ('Bankspeler' if player.get('slot') is None else 'Basisspeler')
    print 'Captain: %s' % ('Ja' if len(player.cssselect('.captain')) > 0 else 'Nee')
    print 'Punten: %s' % (player.cssselect('.team')[0].text_content())


(Hoe je sterspeler bepaald, is dan aan jou). Op het moment dat je list[0] gaat gebruiken, is aan jou om te garanderen dat er dan ook daadwerkelijk een index 0 bestaat.. en anders netjes afvangen om te voorkomen dat je scraper crasht.

En je kunt het dus inderdaad ook wel zonder BeautifulSoup, maar html != xml, wat ze ook aangeven op de website van lxml zelf:
Really broken pages

The normal HTML parser is capable of handling broken HTML, but for pages that are far enough from HTML to call them 'tag soup', it may still fail to parse the page in a useful way. A way to deal with this is ElementSoup, which deploys the well-known BeautifulSoup parser to build an lxml HTML tree.
However, note that the most common problem with web pages is the lack of (or the existence of incorrect) encoding declarations. It is therefore often sufficient to only use the encoding detection of BeautifulSoup, called UnicodeDammit, and to leave the rest to lxml's own HTML parser, which is several times faster.

Acties:
  • 0 Henk 'm!

  • Woudloper
  • Registratie: November 2001
  • Niet online

Woudloper

« - _ - »

Topicstarter
Klaasvaak schreef op zaterdag 22 september 2012 @ 11:41:
De foutmelding geeft aan dat tds.cssselect("div.name") een list geeft. Dan zal het div element op index 0 zitten.

tds.cssselect("div.name")\[0].text_content()
Hier zit vast een denkfout bij mij. Ik was ervan uitgegaan dat ik geen index 0 meer diende te selecteren aangezien er slechts één waarde voldeed aan de .cssselect.
C0rnelis schreef op zaterdag 22 september 2012 @ 13:14:
Een beetje door de documentatie bladerend kom je toch al snel uit op iets als

Python:
1
2
3
4
5
6
7
tree = lxml.html.fromstring(html)
players = tree.cssselect('.player')
for player in players:
    print 'Naam: %s' % player.cssselect('.name')[0].get('title')
    print 'Positie: %s' % ('Bankspeler' if player.get('slot') is None else 'Basisspeler')
    print 'Captain: %s' % ('Ja' if len(player.cssselect('.captain')) > 0 else 'Nee')
    print 'Punten: %s' % (player.cssselect('.team')[0].text_content())


(Hoe je sterspeler bepaald, is dan aan jou). Op het moment dat je list\[0] gaat gebruiken, is aan jou om te garanderen dat er dan ook daadwerkelijk een index 0 bestaat.. en anders netjes afvangen om te voorkomen dat je scraper crasht.
Komt ook overeen met de opmerking hierboven. Bij controle in de code is het altijd zo dat er eigenlijk altijd één div met die waarde aanwezig is. Overigens zit ik nog met één dilemma, maar dat dien ik denk ik op een andere manier op te lossen.

Het is namelijk zo dat de <div class="captain"> via Ajax/Javascript na het laden van de website de css waarde display:block krijgt. Dit moet ik op een één of andere manier in de browser emulator via Mechanize proberen te achterhalen...
En je kunt het dus inderdaad ook wel zonder BeautifulSoup, maar html != xml, wat ze ook aangeven op de website van lxml zelf
Duidelijk. Aangezien de website pretendeert HTML5 te zijn verwacht ik dat het redelijk goed moet komen en anders goed in de documentatie van BeatifulSoup gaan duiken.

Acties:
  • 0 Henk 'm!

  • C0rnelis
  • Registratie: Juni 2010
  • Laatst online: 26-08 22:21
Woudloper schreef op zondag 23 september 2012 @ 19:48:
Het is namelijk zo dat de <div class="captain"> via Ajax/Javascript na het laden van de website de css waarde display:block krijgt. Dit moet ik op een één of andere manier in de browser emulator via Mechanize proberen te achterhalen...

[...]

Duidelijk. Aangezien de website pretendeert HTML5 te zijn verwacht ik dat het redelijk goed moet komen en anders goed in de documentatie van BeatifulSoup gaan duiken.
Dit hangt van twee dingen af: Allereerst: jij beweerde dat je de css properties kunt aflezen met cssselect. Het tweede is afhankelijk van hetgene wat mechanize download aan html. Is dit (standaard) de broncode? In dat geval krijg je namelijk niet alle wijzingen in de DOM mee die later worden toegepast door het draaien van javascript. Als deze FAQ nog geldt, dan heb je waarschijnlijk pech omdat die geen ondersteuning biedt voor javascript.

In het beste geval kun je er vanuit gaan dat je hele goede resultaten krijgt met een xml-parser wanneer er juist gebruik wordt gemaakt van xhtml, niet van html5. In html5 is het bjivoorbeeld toegestaan om bepaalde tags niet te sluiten, terwijl in xhtml alle tags gesloten moeten worden (ook img, br, link etc. Dit laatste zie je ook terug in xml (alle tags worden gesloten).

[ Voor 34% gewijzigd door C0rnelis op 23-09-2012 20:37 ]


Acties:
  • 0 Henk 'm!

  • Woudloper
  • Registratie: November 2001
  • Niet online

Woudloper

« - _ - »

Topicstarter
@C0rnelis: bedankt voor je reacties. Heeft mij erg geholpen. Inmiddels ook de source weten te beoordelen en hoewel de Javascript niet wordt uitgevoerd is het wel mogelijk om het ID van de captain te achterhalen met behulp van de volgende code:
Python:
1
2
# Get Captain ID
CaptainID = html[html.find("updateCaptain('")+15:html.find("'", html.find("updateCaptain('")+15)]


Enige wat nu nog gedaan moet worden is om deze te mappen op de personid in de class .player. Uit de losse pols zou dat zoiets als het volgende worden:

Python:
1
print 'Captain?: %s' % ('Ja' if player.get('personid') is CaptainID else 'Nee')
Pagina: 1