Web scrapping

0

Witam,

import requests
from bs4 import BeautifulSoup

page = requests.get("https://www.oddsportal.com/inplay-odds/live-now/basketball/")
soup = BeautifulSoup(page.content, 'html.parser')
mecze = soup.find(id='live-match-data')

print(mecze)

Czy jest tu ktoś kto byłby wstanie wytłumaczyć mi co robię niedobrze? Jedyne co wyskakuje to None.
Czym się rożni moja strona od tamtej?
Zrobiłem to samo co ten koleś (albo tak mi się tylko wydaje) i jemu pobrało dane (9:30 na filmiku)

0

Przed parsowaniem strony sprawdź czy strona została w ogóle poprawnie ściągnięta (wartość page.status_code — u mnie jest 404). Do odpowiedzi 404 też jest dołączany jakiś html, ale pewnie nie ma w nim elementu z id równym live-match-data.
Dlaczego jest 404 nie wiem, ale być może serwerowi nie podobają się z jakiegoś powodu nagłówki żądania wysyłane przez requests. Przy żądaniu wysłanym przez Firefoxa wszystko działa. Po wysłaniu za pomocą requests żądania identycznego z Firefoxem też:

import requests
from bs4 import BeautifulSoup

headers = {
    'Host': 'www.oddsportal.com', 
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0', 
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 
    'Accept-Language': 'pl,en-US;q=0.7,en;q=0.3', 
    'Accept-Encoding': 'gzip, deflate, br', 
    'Connection': 'keep-alive', 
    'Cookie': 'op_state=1; op_testCountryCode=0; op_oddsportal=ccqugvivdii9ubmftqshn5ghv4; op_cookie-test=ok; op_last_id=60; _ga=GA1.2.2146857933.1572033759; _gid=GA1.2.39303547.1572033759; op_user_time_zone=0; op_user_full_time_zone=31; op_user_cookie=2616558159; op_user_hash=ade4a358f7341b6cd8831efbf40025d5; op_user_time=1572033759; _gat_UA-821699-19=1', 
    'Upgrade-Insecure-Requests': '1'} 

page = requests.get("https://www.oddsportal.com/inplay-odds/live-now/basketball/", headers=headers)
if page.status_code == 200: 
    soup = BeautifulSoup(page.content, 'html.parser')
    mecze = soup.find(id='live-match-data')

    print(mecze)
else:
    print("Kod odpowiedzi HTTP:", page.status_code)

Druga sprawa, to fakt, że na tej Twojej stronie element div z id live-match-data jest pusty w kodzie źródłowym strony. Pewnie jest później wypełniony danymi przez jakiś skrypt (po zajrzeniu za pomocą Inspektora w Firefoksie widać, że coś tam jest), ale te dane nie są dostępne podczas parsowania strony.

0

1.Czyli mam rozumieć na tym przykładzie, że parsowanie w Google chrome klikając zbadaj jest trochę naiwne(zainstalować firefox i web inspector)?
2.W takim razie skoro jest pusty to jak się dobrać do listy meczy (które są linkami do innych stron tej strony, które mnie interesują) w tej tabelce?

3

Jak sobie wejdziesz na development tools na zakładkę networks i przez chwilkę poobserwujesz co się dzieje to zobaczysz, że co chwile lecą requesty pokroju https://fb.oddsportal.com/feed/livegames/live/3/0.dat?_=1572061639867. JavaScript pobiera sobie z tego dane i dynamicznie ładuje w tabelę, więc w statycznie pobranej stronie tabela jest pusta - JS nie pracuje. Kopiując to jako polecenie curla i odzierając ze zbędnych nagłówków dostajemy ostatecznie:

curl 'https://fb.oddsportal.com/feed/livegames/live/3/0.dat?_=1572061639867' -H 'Referer: https://www.oddsportal.com/inplay-odds/live-now/basketball/'

Więc przekładamy to na Pythona:

import time
import requests

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

Nie działa - zwraca to

globals.jsonpCallback('/feed/livegames/live/3/0.dat', {'e':'404'});

Dodajemy flagę -v do curla i okazuje się, że wysyła on jeszcze od siebie ekstra nagłówki. Okazuje się, że User-Agent jest potrzebny:

import time
import requests

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)
print(page.text)

Daje to efekt

$ python test.py 
globals.jsonpCallback('/feed/livegames/live/3/0.dat', {"s":1,"d":{"odds":{"3j4dcx2rrhky0x991jf":[1.27,1.04],"3j4dcx2rrhky0x991jg":[25,13.93]},"bookieCount":{"3772844":28},"text":"<table class=\" table-main\"><colgroup><col width=\"50\" \/><col width=\"*\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><\/colgroup><tbody><tr class=\"dark center\" xtid=\"43863\"><th class=\"first2 tl\" colspan=\"4\"><a class=\"bfl sicona s3\" href=\"\/basketball\/\">Basketball<\/a><span class=\"bflp\">\u00bb<\/span><a class=\"bfl\" href=\"\/basketball\/usa\/\"><span class=\"ficon f-200\"> <\/span>USA<\/a><span class=\"bflp\">\u00bb<\/span><a href=\"\/basketball\/usa\/nba\/\">NBA<\/a><\/th><th>1<\/th><th>2<\/th><th xparam=\"Number of available bookmakers odds~2\">B's<\/th><\/tr><tr class=\"odd deactivate\" xeid=\"IcjV8ChG\"><td class=\"table-time datet t1572057000-1-1-0-0 \"><\/td><td class=\"name table-participant\" colspan=\"3\"><a href=\"\/basketball\/usa\/nba\/los-angeles-lakers-utah-jazz-IcjV8ChG\/inplay-odds\/\">Los Angeles Lakers - Utah Jazz<\/a><\/td><td class=\"live odds-nowrp\" xoid=\"E-3j4dcx2rrhky0x991jf\" xodd=\"azxpfaz0t\">-<\/td><td class=\"live odds-nowrp\" xoid=\"E-3j4dcx2rrhky0x991jg\" xodd=\"xefacz9c\">-<\/td><td class=\"center info-value\">28<\/td><\/tr><\/tbody><\/table>","empty":"<table class=\" table-main\"><tr><td id=\"emptyMsg\"><div class=\"message-info\"><ul><li><div class=\"cms\">There are no LIVE matches odds offered by bookmakers now. Please check <a href=\"\/inplay-odds\/scheduled\/basketball\">Scheduled matches<\/a> for upcoming live odds program.<\/div><\/li><\/ul><\/div><\/td><\/tr><\/table>"},"refresh":20});

A zatem pobraliśmy kawałek kodu JS, który dynamicznie wstawia zawartość na stronę. Sam JS nas nie obchodzi, więc wydzieramy wstawiany JSON przy pomocy regexu:

import time
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")
print(result.group(1))

To nam zwraca JSON-y postaci

{
   "s":1,
   "d":{
      "odds":{
         "3j4dcx2rrhky0x991jf":[
            1.27,
            1.04
         ],
         "3j4dcx2rrhky0x991jg":[
            40,
            15.23
         ]
      },
      "bookieCount":{
         "3772844":26
      },
      "text":"<table class=\" table-main\"><colgroup><col width=\"50\" \/><col width=\"*\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><\/colgroup><tbody><tr class=\"dark center\" xtid=\"43863\"><th class=\"first2 tl\" colspan=\"4\"><a class=\"bfl sicona s3\" href=\"\/basketball\/\">Basketball<\/a><span class=\"bflp\">\u00bb<\/span><a class=\"bfl\" href=\"\/basketball\/usa\/\"><span class=\"ficon f-200\"> <\/span>USA<\/a><span class=\"bflp\">\u00bb<\/span><a href=\"\/basketball\/usa\/nba\/\">NBA<\/a><\/th><th>1<\/th><th>2<\/th><th xparam=\"Number of available bookmakers odds~2\">B's<\/th><\/tr><tr class=\"odd deactivate\" xeid=\"IcjV8ChG\"><td class=\"table-time datet t1572057000-1-1-0-0 \"><\/td><td class=\"name table-participant\" colspan=\"3\"><a href=\"\/basketball\/usa\/nba\/los-angeles-lakers-utah-jazz-IcjV8ChG\/inplay-odds\/\">Los Angeles Lakers - Utah Jazz<\/a><\/td><td class=\"live odds-nowrp\" xoid=\"E-3j4dcx2rrhky0x991jf\" xodd=\"azxpfaz0t\">-<\/td><td class=\"live odds-nowrp\" xoid=\"E-3j4dcx2rrhky0x991jg\" xodd=\"t0faezxc\">-<\/td><td class=\"center info-value\">26<\/td><\/tr><\/tbody><\/table>",
      "empty":"<table class=\" table-main\"><tr><td id=\"emptyMsg\"><div class=\"message-info\"><ul><li><div class=\"cms\">There are no LIVE matches odds offered by bookmakers now. Please check <a href=\"\/inplay-odds\/scheduled\/basketball\">Scheduled matches<\/a> for upcoming live odds program.<\/div><\/li><\/ul><\/div><\/td><\/tr><\/table>"
   },
   "refresh":20
}

Tabela z pola text zawiera tabelę z nazwami drużyn a pole odds właściwe wartości. Teraz trzeba ten JSON sparsować sobie po swojemu. Coś w rodzaju

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
    xoid = m.find('td', class_='odds-nowrp')['xoid'].split('-')[-1]
    odds = json_data.get('odds').get(xoid, '')
    print(u'{} - {}'.format(odds, name))
0

" A zatem pobraliśmy kawałek kodu JS, który dynamicznie wstawia zawartość na stronę. Sam JS nas nie obchodzi, więc wydzieramy wstawiany JSON przy pomocy regexu":
Tutaj się troche pogubiłem

result = re.search("globals.jsonpCallback\('/feed/livegames/live/3/0.dat', (.*)\);", page.text)

globals.jsonpCallback('/feed/livegames/live/3/0.dat' widze, że wziąłeś z początku kawałka kodu JS, który dynamicznie wstawia zawartość na stronę, tylko to wyrażenie regularne po przecinku jest dla mnie tajemnicą i nie rozumiem skąd je wziąłeś i dlaczego akurat ono pasuje.

Jak i gdzie mam zobaczyć te JSON'y u siebie?

W tym skrypcie wyciągane są nazwy drużyn tekstowo, a ,np gdybym chciał w nie wejść (linki przekierowujące głębiej w stronę) to jak się za to zabrać?
Dotychczas tylko wypisywałem wszystkie na stronie i szukałem ich w znacznikach <a i atrybutach href i wytarczyło wkleić mniej wiecej cos takiego:

for link in soup.find_all('a', href=True):
print(f"{link.get('href')} - {link.string}")

Docelowo chciałbym stworzyć robota, który monitoruje każdy z tych meczy, wchodzi w linki wewnetrzne, a w tych linkach wewnetrznych wchodzi w zakłądki przedmeczowe i na żywo over/under i je porównuje, a jak porówna i coś znajdzie to może i nawet na email wysłał czy coś w tym stylu.

i tu ostatnie pytanie. Czy polecacie jakieś inne konkretne zródło wiedzy w tym celu oprócz książki 'Ekstrakcja Danych Python"- Ryan Mitchell?

0

wyrażenie regularne po przecinku jest dla mnie tajemnicą i nie rozumiem skąd je wziąłeś i dlaczego akurat ono pasuje.

Ta funkcja jsonpCallback przyjmuje dwa argumenty, z czego drugi jest JSON-em z danymi, które mnie interesują. Stosuje więc regex, który zwraca wszystkie znaki pomiędzy jej pierwszym argumentem a końcowym zamknięciem nawiasu i średnikiem wskazującym koniec wywołania. Jest to w nawiasach, żeby można było odczytać jako grupę (capture groups). Jak tego nie rozumiesz, to musisz sobie poczytać o wyrażeniach regularnych w Pythonie - https://pymotw.com/3/re/index.html

regex = (
    "globals.jsonpCallback\('/feed/livegames/live/3/0.dat', " # wywołanie funkcji JS, pierwszy argument
    "(.*)"                                                    # znak 0-n razy, złapany jako grupa
    "\);"                                                     # nawias zamykający oraz średnik
)

Jak i gdzie mam zobaczyć te JSON'y u siebie?

Są print-y w przykładach, próbowałeś je uruchomić? Próbowałeś odpalić curla? Próbowałeś zajrzeć do developer tools?

ws.png

W tym skrypcie wyciągane są nazwy drużyn tekstowo, a ,np gdybym chciał w nie wejść (linki przekierowujące głębiej w stronę) to jak się za to zabrać?

Musisz znaleźć elementy <a>, pobrać atrybut href i dla otrzymanego w ten sposób URL-a wysłać kolejne zapytanie GET z użyciem requests.get.

i tu ostatnie pytanie. Czy polecacie jakieś inne konkretne zródło wiedzy w tym celu oprócz książki 'Ekstrakcja Danych Python"- Ryan Mitchell?

Obczaj sobie Scrapy - framework do web scrapingu.

0

odpalałem wszystkie twoje printy po kolei, patrzyłem na developer tools i odpalałem curla i wszystko było jak u ciebie
tylko o pisząc

Jak i gdzie mam zobaczyć te JSON'y u siebie?

Myślałem o tym fragmencie ( powineinem bardziej doprecyzować )

{
   "s":1,
   "d":{
      "odds":{
         "3j4dcx2rrhky0x991jf":[
            1.27,
            1.04
         ],
         "3j4dcx2rrhky0x991jg":[
            40,
            15.23
         ]
      },
      "bookieCount":{
         "3772844":26
      },
      "text":"<table class=\" table-main\"><colgroup><col width=\"50\" \/><col width=\"*\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><col width=\"50\" \/><\/colgroup><tbody><tr class=\"dark center\" xtid=\"43863\"><th class=\"first2 tl\" colspan=\"4\"><a class=\"bfl sicona s3\" href=\"\/basketball\/\">Basketball<\/a><span class=\"bflp\">\u00bb<\/span><a class=\"bfl\" href=\"\/basketball\/usa\/\"><span class=\"ficon f-200\"> <\/span>USA<\/a><span class=\"bflp\">\u00bb<\/span><a href=\"\/basketball\/usa\/nba\/\">NBA<\/a><\/th><th>1<\/th><th>2<\/th><th xparam=\"Number of available bookmakers odds~2\">B's<\/th><\/tr><tr class=\"odd deactivate\" xeid=\"IcjV8ChG\"><td class=\"table-time datet t1572057000-1-1-0-0 \"><\/td><td class=\"name table-participant\" colspan=\"3\"><a href=\"\/basketball\/usa\/nba\/los-angeles-lakers-utah-jazz-IcjV8ChG\/inplay-odds\/\">Los Angeles Lakers - Utah Jazz<\/a><\/td><td class=\"live odds-nowrp\" xoid=\"E-3j4dcx2rrhky0x991jf\" xodd=\"azxpfaz0t\">-<\/td><td class=\"live odds-nowrp\" xoid=\"E-3j4dcx2rrhky0x991jg\" xodd=\"t0faezxc\">-<\/td><td class=\"center info-value\">26<\/td><\/tr><\/tbody><\/table>",
      "empty":"<table class=\" table-main\"><tr><td id=\"emptyMsg\"><div class=\"message-info\"><ul><li><div class=\"cms\">There are no LIVE matches odds offered by bookmakers now. Please check <a href=\"\/inplay-odds\/scheduled\/basketball\">Scheduled matches<\/a> for upcoming live odds program.<\/div><\/li><\/ul><\/div><\/td><\/tr><\/table>"
   },
   "refresh":20
}
0

Myślałem o tym fragmencie ( powineinem bardziej doprecyzować )

To teraz ja nie rozumiem. To po prostu jeden z JSON-ów z tych odpowiedzi, tyle, że przerzuciłem go przez https://jsonformatter.curiousconcept.com/ aby był ładniejszy. Co w takim razie widzisz?

0

Widzę teraz, że po prostu to przefiltrowałeś i pogrupowałeś, a ja nie dostrzegłem, że to jest to samo co jest w tej wersji ciagłej.

0

Jak zmienić ten twój kod:

for m in soup.select('tr[xeid]'):
   name = m.find('td', class_='name').text
   xoid = m.find('td', class_='odds-nowrp')['xoid'].split('-')[-1]
   odds = json_data.get('odds').get(xoid, '')
   print(u'{} - {}'.format(odds, name))

żeby wydobywać ze strony wartośći ze znacznika span?

<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>

Nie ukrywam, że od dłuższej chwili się na tym zaciąłem.

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