Python BeautifulSoup no raspa esta url

Estoy intentando eliminar algunas filas de datos del jugador (tr) de una URL, sin embargo, no parece que ocurra nada cuando ejecuto mi código. Estoy seguro de que mi código está bien porque funciona con otros sitios web de estadísticas que contienen tablas. ¿Alguien puede decirme por qué no pasa nada? Gracias por adelantado.

import urllib import urllib.request from bs4 import BeautifulSoup def make_soup(url): thepage = urllib.request.urlopen(url) soupdata = BeautifulSoup(thepage, "html.parser") return soupdata soup = make_soup("https://www.whoscored.com/Regions/252/Tournaments/7/Seasons/6365/Stages/13832/PlayerStatistics/England-Championship-2016-2017") for record in soup.findAll('tr'): print(record.text) 

Respuesta corta : los datos del jugador que busca NO están en esa URL.

Entonces tal vez quieras preguntar por qué ? Los he visto en esa página, ¿por qué no están allí?

Así que intentaré explicar lo que sucede cuando navegas esa URL con un navegador moderno como Chrome.

Tu: Escribe la url y pulsa enter.

Chrome: Gotcha. Conseguiré esa página para usted lo antes posible, solo un segundo. (obteniendo contenido de esa url), ¡genial, ahora lo tengo! Pero espera, déjame leerlo / analizarlo primero antes de enseñártelo (leyendo lo que está dentro del contenido), oh, mierda, este javascript me dice que obtenga información adicional de otra URL, ok, lo haré; oh, espera, aquí hay otro para decirme que cargue un anuncio en el encabezado, bueno, no me gusta pero solo haré lo que me digan; solo un segundo, estos css me dicen que muestre los nombres de los jugadores en negrita, ok no mal; oh, aquí hay otra foto de url xxx que necesito cargar, no hay problema … oh hombre, ¿cuántas cosas hay para que las procese? No estoy contento con este sitio web … (trabajando en un montón de otras cosas …) ¡Finalmente todo está listo! Ahora echa un vistazo!

Tú: el jugador xxx es bastante bueno, lo comprobaré. (haga clic en jugador xxx)

Chrome :: ……

Como puede ver cada vez que navega por una página web, un navegador hace muchas cosas “detrás de la escena” para mostrarlo a los usuarios. Básicamente: url ingresó >> contenido de url recuperado >> contenido analizado >> contenido adicional recuperado >> todas las cosas renderizadas >> página mostrada (uno o más pasos se pueden hacer simultáneamente)

Y con sus códigos, solo es “contenido de url recuperado”, además, esas estadísticas que desea son “contenido adicional” que debe cargarse desde otro lugar , por lo que no tiene nada.

¿Cómo consigo esas estadísticas entonces? Una vez que conozca las direcciones URL responsables de cargar esas estadísticas, simplemente vaya tras ellas. ¿Cómo puedo encontrar esos urls? Bueno, siempre puedes leer javascripts … si eres lo suficientemente paciente …

La forma más fácil de obtener lo que desea es analizar el tráfico mientras se carga esa página y descubrir a todos aquellos que están detrás del tráfico de escenas . Recomendaría el violinista , pero puedes usar cualquier herramienta que consideres adecuada.

Ahora veamos que pasa cuando cargas esa página: análisis de tráfico

En realidad, hay cientos de solicitudes realizadas para mostrar completamente la página que visitas, y todo lo que necesitas hacer es averiguar cuál alimenta las estadísticas “reales” o “reales”. Hay una url, incluso con “StatisticsFeed”, ¿podría ser la única? Vamos a ver:

https://www.whoscored.com/StatisticsFeed/1/GetPlayerStatistics? & positionOptions = & timeOfTheGameEnd = & timeOfTheGameStart = & isMinApp = true & page = & includeZeroValues ​​= & numberOfPlayersToPick = 10

 { "playerTableStats": [{ "name": "Conor Hourihane", "firstName": "Conor", "lastName": "Hourihane", "playerId": 134172, "height": 181, "weight": 62, "age": 25, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-MC-", "positionText": "Midfielder", "playedPositionsShort": "M(C)", "teamId": 142, "teamName": "Barnsley", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "ie", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.8705882352941181, "ranking": 1, "apps": 17, "subOn": 0, "minsPlayed": 1530, "manOfTheMatch": 4, "yellowCard": 5.0, "redCard": 0.0, "goal": 3, "assistTotal": 8, "shotsPerGame": 2.2352941176470589, "aerialWonPerGame": 0.6470588235294118, "passSuccess": 81.370449678800867 }, { "name": "Anthony Knockaert", "firstName": "Anthony", "lastName": "Knockaert", "playerId": 86794, "height": 172, "weight": 69, "age": 25, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-AML-AMR-", "positionText": "Midfielder", "playedPositionsShort": "AM(LR)", "teamId": 211, "teamName": "Brighton", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "fr", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.6722222222222216, "ranking": 2, "apps": 18, "subOn": 1, "minsPlayed": 1471, "manOfTheMatch": 5, "yellowCard": 4.0, "redCard": 0.0, "goal": 6, "assistTotal": 0, "shotsPerGame": 2.3888888888888888, "aerialWonPerGame": 0.22222222222222221, "passSuccess": 83.420593368237348 }, { "name": "Lewis Dunk", "firstName": "Lewis", "lastName": "Dunk", "playerId": 86441, "height": 192, "weight": 88, "age": 25, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 211, "teamName": "Brighton", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-eng", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.660000000000001, "ranking": 3, "apps": 18, "subOn": 0, "minsPlayed": 1620, "manOfTheMatch": 3, "yellowCard": 8.0, "redCard": 0.0, "goal": 1, "assistTotal": 1, "shotsPerGame": 0.61111111111111116, "aerialWonPerGame": 3.5, "passSuccess": 79.72251867662753 }, { "name": "Tom Clarke", "firstName": "Tom", "lastName": "Clarke", "playerId": 133974, "height": 180, "weight": 77, "age": 28, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 181, "teamName": "Prestn", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-eng", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.6126315789473677, "ranking": 4, "apps": 19, "subOn": 0, "minsPlayed": 1692, "manOfTheMatch": 4, "yellowCard": 0.0, "redCard": 0.0, "goal": 2, "assistTotal": 0, "shotsPerGame": 0.89473684210526316, "aerialWonPerGame": 5.4736842105263159, "passSuccess": 66.666666666666657 }, { "name": "Pontus Jansson", "firstName": "Pontus", "lastName": "Jansson", "playerId": 121123, "height": 194, "weight": 89, "age": 25, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 19, "teamName": "Leeds", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "se", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.5976923076923066, "ranking": 5, "apps": 13, "subOn": 0, "minsPlayed": 1126, "manOfTheMatch": 1, "yellowCard": 6.0, "redCard": 0.0, "goal": 1, "assistTotal": 0, "shotsPerGame": 0.53846153846153844, "aerialWonPerGame": 3.5384615384615383, "passSuccess": 86.336633663366342 }, { "name": "Angus MacDonald", "firstName": "Angus", "lastName": "MacDonald", "playerId": 110825, "height": 184, "weight": 70, "age": 24, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 142, "teamName": "Barnsley", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-eng", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.5066666666666677, "ranking": 6, "apps": 12, "subOn": 0, "minsPlayed": 1080, "manOfTheMatch": 0, "yellowCard": 3.0, "redCard": 0.0, "goal": 0, "assistTotal": 0, "shotsPerGame": 0.33333333333333331, "aerialWonPerGame": 4.833333333333333, "passSuccess": 72.147651006711413 }, { "name": "Marc Roberts", "firstName": "Marc", "lastName": "Roberts", "playerId": 138949, "height": 183, "weight": 81, "age": 26, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 142, "teamName": "Barnsley", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-eng", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.503125, "ranking": 7, "apps": 16, "subOn": 0, "minsPlayed": 1440, "manOfTheMatch": 1, "yellowCard": 3.0, "redCard": 0.0, "goal": 2, "assistTotal": 2, "shotsPerGame": 0.625, "aerialWonPerGame": 7.0625, "passSuccess": 61.595547309833023 }, { "name": "Bradley Johnson", "firstName": "Bradley", "lastName": "Johnson", "playerId": 12490, "height": 178, "weight": 68, "age": 29, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-MC-ML-", "positionText": "Midfielder", "playedPositionsShort": "M(CL)", "teamId": 20, "teamName": "Derby", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-eng", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.4954545454545443, "ranking": 8, "apps": 11, "subOn": 0, "minsPlayed": 952, "manOfTheMatch": 1, "yellowCard": 4.0, "redCard": 0.0, "goal": 2, "assistTotal": 1, "shotsPerGame": 1.3636363636363635, "aerialWonPerGame": 4.0909090909090908, "passSuccess": 71.908127208480565 }, { "name": "Christophe Berra", "firstName": "Christophe", "lastName": "Berra", "playerId": 8287, "height": 186, "weight": 81, "age": 31, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 165, "teamName": "Ipswich", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-sct", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.4789473684210526, "ranking": 9, "apps": 19, "subOn": 0, "minsPlayed": 1710, "manOfTheMatch": 3, "yellowCard": 4.0, "redCard": 0.0, "goal": 0, "assistTotal": 1, "shotsPerGame": 0.94736842105263153, "aerialWonPerGame": 6.2105263157894735, "passSuccess": 58.636363636363633 }, { "name": "Adam Webster", "firstName": "Adam", "lastName": "Webster", "playerId": 109922, "height": 191, "weight": 0, "age": 21, "isManOfTheMatch": false, "isActive": true, "isOpta": true, "playedPositions": "-DC-", "positionText": "Defender", "playedPositionsShort": "D(C)", "teamId": 165, "teamName": "Ipswich", "seasonId": 6365, "seasonName": "2016/2017", "tournamentId": 7, "tournamentRegionId": 252, "tournamentRegionCode": "gb-eng", "regionCode": "gb-eng", "tournamentName": "Championship", "tournamentShortName": "EC", "rating": 7.4780000000000006, "ranking": 10, "apps": 15, "subOn": 1, "minsPlayed": 1227, "manOfTheMatch": 2, "yellowCard": 1.0, "redCard": 0.0, "goal": 0, "assistTotal": 0, "shotsPerGame": 0.2, "aerialWonPerGame": 5.0666666666666664, "passSuccess": 58.256029684601117 }], "paging": { "currentPage": 1, "totalPages": 34, "resultsPerPage": 10, "totalResults": 338, "firstRecordIndex": 1, "lastRecordIndex": 10 }, "statColumns": ["apps", "subOn", "minsPlayed", "goal", "assistTotal", "yellowCard", "redCard", "shotsPerGame", "passSuccess", "aerialWonPerGame", "manOfTheMatch"] } 

¡Exactamente! ¿Y ahora que? Simule esta solicitud y analice el contenido, ya que ya tiene formato JSON, el módulo incorporado json haría el trabajo fácilmente, ni siquiera tiene que usar BeautifulSoup

Podría preguntar, ¿por qué no obtengo nada cuando navego directamente en este enlace? Esto se debe a que establecen un límite en su servidor para que solo las solicitudes con encabezados válidos reciban fonts. Entonces, ¿cómo puedo pasar por alto eso? Simule “vívidamente” con los parámetros correctos (en su mayoría, encabezados) para que le crean.

Esta página usa javascript para obtener datos, puede encontrar datos sin procesar en este enlace:

 https://www.whoscored.com/StatisticsFeed/1/GetPlayerStatistics?category=summary&subcategory=all&statsAccumulationType=0&isCurrent=true&playerId=&teamIds=&matchId=&stageId=13832&tournamentOptions=7&sortBy=Rating&sortAscending=&age=&ageComparisonType=&appearances=&appearancesComparisonType=&field=Overall&nationality=&positionOptions=&timeOfTheGameEnd=&timeOfTheGameStart=&isMinApp=true&page=&includeZeroValues=&numberOfPlayersToPick=10 

Cada campo de la URL se puede cambiar para obtener los datos que necesita.

Sucede porque el sitio web no quiere que lo rasques.

Protección de la cápsula

Utilicé selenium para enviar la solicitud e imaginé el navegador simulado que ha creado.

Está utilizando Incapsula, que es un servicio de seguridad (incluso tienen información sobre cómo raspar en su sitio web).

  • Esto podría ser útil