Web scrapping

0

Wyszukujesz tagi find/find_all i sprawdzasz atrybut text

>>> from bs4 import BeautifulSoup
>>> text = """
... <tr class="odd deactivate" xeid="EeQYQqB9">
...    <td class="live odds-nowrp down-live" xoid="E-3ihs6x2rrhky0x97dah" xodd="1.1|1.06" style="background-color: rgb(255, 255, 255);">
...        <span>1.06</span>
...    </td>
...   <td class="live odds-nowrp up-live" xoid="E-3ihs6x2rrhky0x97dai" xodd="15.25|10.88" style="background-color: rgb(255, 255, 255);">
...        <span>11.03</span>
...    </td>
... </tr>
... """
>>> soup = BeautifulSoup(text)
>>> [span.text for span in soup.find_all('span')]
[u'1.06', u'11.03'
0

A na przykład dla całej strony? Próbowałem stosować find_all ale z takim rezultatem

[] - Ludwigsburg - Hamburg
[] - CSM Focsani - Athletic Constanta
[] - Rustavi - Kutaisi
[] - Karsiyaka Belediyespor - Gecit Spor
[] - Beylikduzu - Ankara Kayi Genclik
[] - Bornova - Merkezefendi

Dla kodu:

import json
import time
import pprint
import re
import sys

import requests
from bs4 import BeautifulSoup

url = "https://fb.oddsportal.com/feed/livegames/live/3/0.dat?_{}".format(int(time.time() * 1000))
headers = {
    'User-Agent': 'curl/7.64.0',
    'Referer': 'https://www.oddsportal.com/inplay-odds/live-now/basketball/',
}
page = requests.get(url, headers=headers)

result = re.search("globals.jsonpCallback\('/feed/livegames/live/3/0.dat', (.*)\);", page.text)
if not result:
    sys.exit("Result not found")

json_text = result.group(1)
json_data = json.loads(json_text).get('d')

soup = BeautifulSoup(json_data.get('text', ''), 'html.parser')

for m in soup.select('tr[xeid]'):
    name = m.find('td', class_='name').text
    odds= m.find('td', class_='odds-nowrp').find_all('span')
    print(u'{} - {}'.format(odds, name))
0

A na przykład dla całej strony? Próbowałem stosować find_all ale z takim rezultatem

Już ci mówiłem, że tych danych tam nie ma. Patrzysz na stronę działającą live, dynamicznie aktualizowaną JavaScriptem, a nie statyczny html, który ściągnąłeś. Wpisz sobie w kod

with open('test.html', 'w+') as f:
    f.write(str(soup))

I zanalizuj zapisany test.html, to zobaczysz co tak naprawdę w nim tkwi i co możesz scrapować. Musisz ze strony wyciągnąć identyfikatory takie jak E-3lfkfx2rrhky0x9gt09, ściąć początkowe E- i poszukać odpowiednich danych w JSON-ie.

0

Czyli z tego testu.html muszę wyciągnąć te wszystkie identyfikatory E-, jak je wyciągnę i usunę E- z początku to będę musiał się do nich odwołać, a jak się odwołam to dostanę JSON-y, w których są wartości tych znaczników span, które chce mieć?

0

Czyli z tego testu.html muszę wyciągnąć te wszystkie identyfikatory E-, jak je wyciągnę i usunę E- z początku to będę musiał się do nich odwołać, a jak się odwołam to dostanę JSON-y, w których są wartości tych znaczników span, które chce mieć?

Przeczytaj sobie jeszcze raz ten post i przeanalizuj ostatni przykład.

JSON już masz. Siedzi w zmiennej json_data. Żadnych nowych JSON-ów nie dostaniesz. Bo i skąd? Co w ogóle rozumiesz przez "odwołać się do identyfikatora"? W rzeczy samej, pisałem ci w zalinkowanym poście

Tabela z pola text zawiera tabelę z nazwami drużyn a pole odds właściwe wartości.

W rzeczy samej, w przykładzie tym juz wyciągam identyfikatory i usuwam początkowe E-, co robi linia

xoid = m.find('td', class_='odds-nowrp')['xoid'].split('-')[-1]

Linię później odwołuję się do samego JSON-a by pobrać jakieś tam dane

json_data.get('odds').get(xoid, '')

To tylko przykład, bo nie wiem jak interpretować te dane i co chcesz z nimi zrobić. Dlatego też ci pisałem

Teraz trzeba ten JSON sparsować sobie po swojemu.

0

Czyli jeśli dobrze rozumiem ta linijka

xoid = m.find('td', class_='odds-nowrp')['xoid'].split('-')[-1]

pobiera identyfiator, za którego pomocą odwołujesz się do danych w JSON.
Ale w dalszym ciągu nie widze nawet w tym JSON tych wartości znacznika span, które widzę po kliknięciu zbadaj na stroniehtml.jpg

I jak porównuje dane z JSON z tym co mam tutaj to w JSON mam dane z xodd="5.5|4.53"

0

Patrz pan, a umnie coś takiego wydaje się działać

import json
import time
import pprint
import re
import sys

import requests
from bs4 import BeautifulSoup

url = "https://fb.oddsportal.com/feed/livegames/live/3/0.dat?_{}".format(int(time.time() * 1000))
headers = {
    'User-Agent': 'curl/7.64.0',
    'Referer': 'https://www.oddsportal.com/inplay-odds/live-now/basketball/',
}
page = requests.get(url, headers=headers)

result = re.search("globals.jsonpCallback\('/feed/livegames/live/3/0.dat', (.*)\);", page.text)
if not result:
    sys.exit("Result not found")
json_text = result.group(1)
json_data = json.loads(json_text).get('d')

soup = BeautifulSoup(json_data.get('text', ''), 'html.parser')
for m in soup.select('tr[xeid]'):
    name = m.find('td', class_='name').text
    ids = m.find_all('td', class_='odds-nowrp')
    xoid, xodd = (s['xoid'].split('-')[-1] for s in m.find_all('td', class_='odds-nowrp'))
    xoid_data = json_data.get('odds', {}).get(xoid)
    xodd_data = json_data.get('odds', {}).get(xodd)
    if xoid_data is not None:
        xoid_data = xoid_data[1]
    if xodd_data is not None:
        xodd_data = xodd_data[1]   
    print(u'{}/{} - {}'.format(xoid_data, xodd_data, name))

Na Linuksie odpalam sobie to tak

$ watch python test.py

To widzę zmiany na bieżąco.

0

Wkleiłem, uruchomiłem i rzeczywiście działa, (dla niektórych rozpoczynających meczy zmienna name ma dodatkowy długi tekst przed nazwami drużyn, ale z tym to chyba nawet ja sobie już poradzę...).
Ale nie byłbym sobą gdybym jeszcze nie zapytał, otóż dla takiego kodu, który zbiera wszystkie linki do meczy na żywo z tej strony:

import json
import time
import pprint
import re
import sys

import requests
from bs4 import BeautifulSoup

url = "https://fb.oddsportal.com/feed/livegames/live/3/0.dat?_{}".format(int(time.time() * 1000))
headers = {
    'User-Agent': 'curl/7.64.0',
    'Referer': 'https://www.oddsportal.com/inplay-odds/live-now/basketball/',
}
page = requests.get(url, headers=headers)

result = re.search("globals.jsonpCallback\('/feed/livegames/live/3/0.dat', (.*)\);", page.text)
if not result:
 sys.exit("Result not found")

json_text = result.group(1)
json_data = json.loads(json_text).get('d')

soup = BeautifulSoup(json_data.get('text', ''), 'html.parser')
nameList = soup.findAll('td',{'class':'name table-participant'})

lista = []
for name in nameList:
     lista.append("https://www.oddsportal.com"f"{name.find('a', href=True).get('href')}")

for x in lista : print(x)

Chciałbym oczywiście wchodzić w te linki. Pokazałeś mi, że dla tej strony requesty muszę wysłać w ten sposób:

url = "https://fb.oddsportal.com/feed/livegames/live/3/0.dat?_{}".format(int(time.time() * 1000))
headers = {
    'User-Agent': 'curl/7.64.0',
    'Referer': 'https://www.oddsportal.com/inplay-odds/live-now/basketball/',
}
page = requests.get(url, headers=headers)

Czyli w tej chwili mam z tego 'User-Agent' i 'Referer' w liście lista, czyli brakuje url.
Jak mógłbym zaradzić temu problemowi, żeby znajdywać te requesty URL różne dla każdego meczu?
1 mecz.png
2 mecz.png

1

Oh boy, oh boy, oh boy...

Wlazłem sobie na przykładowy url, https://www.oddsportal.com/basketball/greece/a1-women/panathinaikos-as-niki-lefkadas-dtXLVHkr/inplay-odds/, API pobiera mi się z requestów do https://fb.oddsportal.com/feed/live/1-3-dtXLVHkr-3-1-yj8b2.dat?_=1572790354807. Z tego co widzę, wszystkie takie requesty mają format

'https://fb.oddsportal.com/feed/live/1-3-ID1-3-1-ID2.dat

Więc trzeba znaleźć jakie jest ID1 oraz ID2. ID1 jest trywialne - możemy je pobrać z samego URL-a (dtXLVHk). Pytanie czym jest ID2. Szukam debuggerem - nie widzi nigdzie. Ściągam wszystkie requesty jako plik HAR (opcja w FF po wybraniu prawym klawiszem myszy) - też nigdzie nie ma. Trzeba zajrzeć do JavaScriptu Link można znaleźć w źrodle strony, idzie on tutaj - https://www.oddsportal.com/res/x/global-191101065340.js. Przepuszczamy to przez https://beautifier.io/ aby uzyskać sensowne formatowanie. Przeglądając JS w oczy wpada ten kawałek kodu

    this.updateScore = function() {
        if (page.updateScorePauseTime) {
            return
        }
        var url = '/feed/postmatchscore/' + this.params.sportId + '-' + this.eventId + '-' + this.process(this.params.xhash) + '.dat';
        var request = new Request(url);
        var proxy = "2" * 1;
        if (proxy) {
            request.setProxyType(proxy)
        }
        request.setPriority(Request.PRIORITY_LOW);
        request.setCallback(page.onUpdateScore);
        request.get()
    };

Zastnawia mnie ten parametr xhash, więc zaczynam go szukać. Szukając po tym identyfikatorze odkrywamy w źródłe strony taki kawałek

<script type="text/javascript">
    //<![CDATA[
    var op = new OpHandler();if(!page)var page = new PageEvent({"id":"29pLVtoG","xhash":"%79%6a%34%64%30","xhashf":"%79%6a%64%33%33","ukeyBase":"E-3849624","isLive":true,"isPostponed":false,"isStarted":true,"isFinished":false,"isFinishedGracePeriod":true,"sportId":3,"versionId":1,"home":"Hatay W","away":"Izmit Belediyespor W","tournamentId":43855,"eventBonus":[]});var menu_open = null;vJs();op.init();if(page && page.display)page.display(); var sigEndPage = true;
    try
    {
        if (sigEndJs)
        {
            globals.onPageReady();
        }
    } catch (e)
    {
    }

    //]]>
</script>

I tutaj leży nasz parametr xhash - %79%6a%34%64%30. Nie udało się znaleźć wcześniej bo jest encodowany. Można to zdekodować w Pythonie kodem urllib.unquote(url).decode('utf8') (uwaga, w Pythonie 3 funkcja jest inna). Dobra, mamy to (chyba). Jako bonus, widać, że w tym JSONie jest też nasz pierwszy identyfikator, więc nie trzeba go czytać z urla. Najpierw parsujemy naszą stronę do zupy:

url = 'https://www.oddsportal.com/basketball/turkey/kbsl-women/hatay-izmit-belediyespor-29pLVtoG/inplay-odds/'
headers = {
    'User-Agent': 'curl/7.64.0',
    'Referer': 'https://www.oddsportal.com/inplay-odds/live-now/basketball/',
}
page = requests.get(url, headers=headers)
soup = BeautifulSoup(page.text, 'html.parser')

Łapiemy skrypt

script = soup.select_one('script:contains("new OpHandler")').text

Wyciągamy regexem JSON i parsujemu

json_text = re.search('PageEvent\((.*?)\);', script)
if not json_text:
    print('script not found')
    sys.exit()

try:
    json_data = json.loads(json_text.group(1))
except ValueError:
    print('json not parsed')
    sys.exit()

I teraz możemy odczytać oba identyfikatory

id1 = json_data.get('id')
id2 = urllib.unquote(json_data.get('xhash')).decode('utf8')

I skonstruować URL

output_url = 'https://fb.oddsportal.com/feed/live/1-3-{}-3-1-{}.dat?_={}'
print(output_url.format(id1, id2, int(time.time() * 1000)))

Całość

import json
import re
import sys
import time
import urllib

import requests
from bs4 import BeautifulSoup

url = 'https://www.oddsportal.com/basketball/turkey/kbsl-women/hatay-izmit-belediyespor-29pLVtoG/inplay-odds/'
headers = {
    'User-Agent': 'curl/7.64.0',
    'Referer': 'https://www.oddsportal.com/inplay-odds/live-now/basketball/',
}
page = requests.get(url, headers=headers)
soup = BeautifulSoup(page.text, 'html.parser')

script = soup.select_one('script:contains("new OpHandler")').text

json_text = re.search('PageEvent\((.*?)\);', script)
if not json_text:
    print('script not found')
    sys.exit()

try:
    json_data = json.loads(json_text.group(1))
except ValueError:
    print('json not parsed')
    sys.exit()

id1 = json_data.get('id')
id2 = urllib.unquote(json_data.get('xhash')).decode('utf8')

output_url = 'https://fb.oddsportal.com/feed/live/1-3-{}-3-1-{}.dat?_={}'
print(output_url.format(id1, id2, int(time.time() * 1000))

Uf.


Z grubsza biorąc tak właśnie wygląda web scrapping - ładujemy się na stronę i pół biedy jak to prosty HTML, który można odczytać od razu. Trzeba często jednak bawić się w detektywa, czytać requesty, analizować skrypty, wygrzebywać z kodu kody sesji, identyfikatory, klucze, ustawiać ciastka, wysyłać zapytania do API i tym podobne. Wybrałeś sobie bardzo kiepską stronę na naukę web scrapingu, bo większość rzeczy jest tu pohowana w różnych śmiesznych miejscach i trzeba się było głęboko wgryźć w bebechy i fantazję devów, którzy to coś składali do kupy żeby odtworzyć wszystkie akcje i zapytania.

Z mojej strony pass, więcej już się tą stroną bawić nie będę, więc jak jeszcze będziesz miał jakieś problemy, to musisz je zbadać sam. Pokazałem ci z grubsza jak to się robi.

0

Rozumiem oraz dziękuje za informację i przede wszystkim dużą cierpliwość

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0