Проблема
Итак, open uri просто делает HTTP-запросы и дает вам доступ к телу.В этом случае тело HTML.Этот HTML имеет заполнитель для этих данных, который вы видите.Затем этот HTML-код говорит о загрузке некоторого javascript, который будет выполнять другой запрос к серверу для получения данных, и когда данные поступят, он заменит заполнитель реальными данными.Итак, чтобы справиться с этим, вам, в конечном счете, нужно все, что возвращается из того запроса, который делает javascript.
Три решения
Заказывается от моего наименее любимого к моему самому любимому.
- Вы можете попробовать оценить JavaScript, чтобы он работал на HTML.Это будет болезненно, поэтому я не рекомендую это делать, но если вы хотите пойти по этому пути, я думаю, что есть драгоценный камень под названием «Рубиновый гонщик» или что-то в этом роде (IIRC, он включает в себя версию 8).
- Вы можете запустить веб-браузер, позволить браузеру обрабатывать все cray-cray, а затем запрашивать у браузера html после его обновления.Это то, что делает решение Рахула, и это действительно хорошее решение.Это не мой фаворит, потому что он довольно тяжелый, и вы переведены на информацию, отображаемую в html.Это называется «очистка», и она довольно хрупкая (некоторые дизайнеры перемещают что-то по странице и разрывы сценариев), и информация представлена в формате представления человеком, что означает, что вам обычно приходится выполнять множество мелких операций синтаксического анализа.
- Вы можете открыть devtools вашего браузера, перейти на вкладку сети, отфильтровать запросы XHR и перезагрузить страницу.Один из них сделал запрос на получение данных, которые использовались для заполнения заполнителя.Выясните, какой это, и затем вы можете сделать этот запрос самостоятельно.Есть также способы, которые могут быть хрупкими, например, иногда вам нужно иметь правильные куки, и вам часто приходится экспериментировать с тем, что послал браузер, чтобы выяснить, сколько вам нужно (обычно это намного меньше, чем было отправлено, чтоверно для вашего случая). Подсказка: Когда вы делаете это, отдельный запрос данных разбирается и анализируется (т. Е. Сохраните его в файл, а затем, просматривая данные, получите его из файла, а не отправляйте новый запрос каждый раз).время ... таким образом, оно не изменится на вас, и вы не получите ограничение скорости)
Решение # 3
Итак, мне было любопытно, и я попыталсяРешение № 3 сам, и это сработало довольно замечательно, посмотрите:
require 'uri'
require 'net/http'
# build a post request to the URL that the page got the data from
uri = URI 'https://www.cargurus.com/Cars/inventorylisting/ajaxFetchSubsetInventoryListing.action?sourceContext=untrackedExternal_true_0'
req = Net::HTTP::Post.new(uri)
# set some headers
req['origin'] = 'https://www.cargurus.com' # for cross origin requests
req['cache-control'] = 'no-cache' # no caching, just in case,
req['pragma'] = 'no-cache' # we prob don't want stale data
# looks like you can pass it an awful lot of filters to use
req.set_form_data(
"page"=>"1", "zip"=>"", "address"=>"", "latitude"=>"", "longitude"=>"",
"distance"=>"100", "selectedEntity"=>"d841", "transmission"=>"ANY",
"entitySelectingHelper.selectedEntity2"=>"", "minPrice"=>"", "maxPrice"=>"",
"minMileage"=>"", "maxMileage"=>"", "bodyTypeGroup"=>"", "serviceProvider"=>"",
"filterBySourcesString"=>"", "filterFeaturedBySourcesString"=>"",
"displayFeaturedListings"=>"true", "searchSeoPageType"=>"",
"inventorySearchWidgetType"=>"AUTO", "allYearsForTrimName"=>"false",
"daysOnMarketMin"=>"", "daysOnMarketMax"=>"", "vehicleDamageCategoriesRaw"=>"",
"minCo2Emission"=>"", "maxCo2Emission"=>"", "vatOnly"=>"false",
"minEngineDisplacement"=>"", "maxEngineDisplacement"=>"", "minMpg"=>"",
"maxMpg"=>"", "minEnginePower"=>"", "maxEnginePower"=>"", "isRecentSearchView"=>"false"
)
# make the request (200 means it worked)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request req }
res.code # => "200"
# parse the response
require 'json'
json = JSON.parse res.body
# we're on page 1 of 1, and there are 48 results on this page
json['page'] # => 1
json['listings'].size # => 48
json['remainingResults'] # => false
# apparently we're looking at some sort of car or smth
json['modelId'] # => "d841"
json['modelName'] # => "Mazda MAZDASPEED6"
# a bunch of places sell this car
json['sellers'].size # => 47
json['sellers'][0]['location'] # => "Portland OR, 97217"
# the first of our 48 cars seems to be a deal
listing = json['listings'][0]
listing['mainPictureUrl'] # => "https://static.cargurus.com/images/forsale/2018/05/24/02/58/2006_mazda_mazdaspeed6-pic-61663369386257285-152x114.jpeg"
listing['expectedPriceString'] # => "$8,972"
listing['priceString'] # => "$6,890"
listing['daysOnMarket'] # => 61
listing['savingsRecommendation'] # => "Good Deal"
listing['carYear'] # => 2006
listing['mileageString'] # => "81,803"
# none of the 48 are salvaged or lemons
json['listings'].count { |l| l['lemon'] } # => 0
json['listings'].count { |l| l['salvage'] } # => 0
# the savings recommendations seem reasonably distributed
json['listings'].group_by { |l| l["savingsRecommendation"] }.map { |rec, ls| [rec, ls.size] }
# => [["Good Deal", 4],
# ["Fair Deal", 11],
# ["No Price Analysis", 23],
# ["High Price", 8],
# ["Overpriced", 2]]