Web scrapping

Odpowiedz Nowy wątek
2019-10-25 21:43
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)

edytowany 1x, ostatnio: slawek999888, 2019-10-25 21:45

Pozostało 580 znaków

2019-11-01 16:37
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))

Pozostało 580 znaków

2019-11-01 16:53
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.

edytowany 3x, ostatnio: Spearhead, 2019-11-01 16:57

Pozostało 580 znaków

2019-11-01 17:14
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ć?

Pozostało 580 znaków

2019-11-01 17:21
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.

Pozostało 580 znaków

2019-11-01 17:55
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"

  • html.jpg (0,67 MB) - ściągnięć: 11

Pozostało 580 znaków

2019-11-02 16:19
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.

Pozostało 580 znaków

2019-11-02 19:05
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

Pozostało 580 znaków

2019-11-03 16:19

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.

edytowany 1x, ostatnio: Spearhead, 2019-11-03 16:19

Pozostało 580 znaków

2019-11-03 16:22
0

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

edytowany 1x, ostatnio: slawek999888, 2019-11-03 16:36

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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

Robot: CCBot

Użytkownik: slawek999888