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-10-25 22:48
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.

Pozostało 580 znaków

2019-10-25 23:44
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?

Pozostało 580 znaków

2019-10-26 06:40
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))
edytowany 3x, ostatnio: Spearhead, 2019-10-26 07:07

Pozostało 580 znaków

2019-10-26 17:36
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?

edytowany 1x, ostatnio: slawek999888, 2019-10-26 17:40

Pozostało 580 znaków

2019-10-26 17:58
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.

  • ws.png (0,16 MB) - ściągnięć: 17
edytowany 1x, ostatnio: Spearhead, 2019-10-26 17:59

Pozostało 580 znaków

2019-10-26 20:39
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
}

Pozostało 580 znaków

2019-10-27 02:02
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?

Pozostało 580 znaków

2019-10-27 08:28
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.

Pozostało 580 znaków

2019-11-01 15:43
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.

Pozostało 580 znaków

2019-11-01 16:07
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'

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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

Użytkownik: slawek999888