@f_u_s_s
Любопытный кодер

Как получить список отзывов об организации с карт Яндекса?

Доброго дня!

Понадобилось реализовать подгрузку отзывов на сайте организации с карт яндекса. В документации не нашел подобного метода АПИ и полез смотреть как сами карты их получают, нашел такой вот AJAX-запрос, который отправляется при загрузке страницы яндекса с выбранной организацией:
https://yandex.ru/maps/api/business/fetchReviews?csrfToken=<csrf-токен>&ajax=1&id=<ID_организации>&page=1

Но проблема в том, что этот метод завязан на CSRF-токене и, если он неверный - то результатом будет 403 код, и актуальный токен. Если в браузере взять полученный новый токен и подставить в ссылку - то все отлично, список отзывов я получаю в JSON-формате. Но в если делать тоже самое программно, то для каждого get-запроса генерируется новый токен.

Пример (сверху - полученный токен при заведомо ошибочном запросе, снизу - результат запроса с этим токеном):
5b580e11028ab489528098.png
О том, почему и зачем так работает - в случае именно с этим методом апи - мне понятно. Не понятно каким образом можно получить список отзывов программно (и можно ли в целом это реализовать без шаманства) ? Нужно именно на стороне сервера это делать, потому как сами отзывы будут подтягиваться в БД сайта для модерации и т.д.
  • Вопрос задан
  • 16464 просмотра
Пригласить эксперта
Ответы на вопрос 6
@gvordex
Пример реализации на php:
function get_request_result($request)
           {
                $ch = curl_init();
                 curl_setopt($ch, CURLOPT_URL, $request);

        
                /*Храним сеанс в сессии*/
                curl_setopt($ch, CURLOPT_COOKIEFILE, true);
                curl_setopt($ch, CURLOPT_COOKIEFILE, "cookiefile");
                curl_setopt($ch, CURLOPT_COOKIEJAR, "cookiefile");
                /**/

                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                $server_output = curl_exec($ch);

                curl_close($ch);

                return json_decode($server_output);
            }

$request_token = "https://yandex.ru/maps/api/business/fetchReviews";
$token = get_request_result($request)->csrfToken;
$request_reviews = "https://yandex.ru/maps/api/business/fetchReviews?ajax=1&businessId=id_организации&csrfToken={$token}&page=1&pageSize=5";
$reviews_obj =  get_request_result($request_reviews);


Тогда можно программно получать список отзывов.
Ответ написан
Перестал работать запрос по AJAX, поэтому написал парсер для страницы
https://yandex.ru/maps/org/{organization_id}/reviews/

Для начала напишем простейшую функцию для запроса cURL
function getRequestResult($request){
    $ch=curl_init();
    curl_setopt($ch,CURLOPT_URL,$request);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $server_output = curl_exec($ch);
    curl_close($ch);
    return $server_output;
}
$org_url='https://yandex.ru/maps/org/{&organization_id}/reviews/';
$html=getRequestResult($org_url);

Получили огромный HTML страницы, давайте обрежем его. Нам нужно содержимое div с классом reviews-view__reviews.
$pos=strpos($html,'<div class="reviews-view__reviews">');
$part=substr($html,$pos);
$pos2=strpos($part, '</section>');
$part=substr($fx,0,$pos2);

Далее, используя любой HTML парсер, добиваемся нужного результата. Я использовал Simple HTML DOM
require_once("simple_html_dom.php");
$html=str_get_html($part);
if(count($html->find('.business-review-view'))){
    foreach($html->find('.business-review-view') as $div){
        //Значит, нашли отзыв
        $review=[];
        //Ищем автора
        if(count($div->find('.business-review-view__author a'))){
            $reviewAuthor=$div->find('.business-review-view__author a span')[0]->innertext;
            $review['author']=$reviewAuthor;
        }
        if(count($div->find('.business-review-view__author meta'))){
            $reviewImage=$div->find('.business-review-view__author meta')[0]->content;
            $review['image']=$reviewImage;
        }
        if(count($div->find('.business-review-view__date'))){
            $reviewDate=$div->find('.business-review-view__date meta')[0]->content;
            $review['date']=$reviewDate;
        }
        if(count($div->find('.business-review-view__body-text'))){
            $reviewText=$div->find('.business-review-view__body-text')[0]->innertext;
            $review['text']=$reviewText;
        }
        $reviews[]=$review;
    }
}

В итоге, получаем в массив reviews такие данные: аватар пользователя, имя, дата публикации отзыва и, собственно, текст отзыва.

Лучше не использовать этот код постоянно, данные лучше кешировать и при необходимости вытаскивать их из кеша
Yandex всегда может поменять html код и тогда метод не будет работать
Ответ написан
@tokarev_p_a
Используй сессии.
Как я победил. Создал сессию. Далее делал начальный POST запрос и получал от Яндекса токен. После подставлял его в интересующую ссылку и делала повторный запрос. В итоге получал нужную мне информацию.

from bs4 import BeautifulSoup
import requests
import json
from urllib.parse import urlparse

url = "https://yandex.ru/maps/api/business/fetchReviews?ajax=1&businessId=221702091001&csrfToken=97fd195e6cf78eb6f395d7c395d296ab5c11d1f1:1571717475&page=1&pageSize=5&ranking=by_time&reqId=1571655534207434-3187132131-sas1-1604&sessionId=1571655349968_626529"

url_post = "https://yandex.ru/maps/api/business/fetchReviews"

s = requests.session()
p = s.post(url_post)

data = json.loads(p.content)

parsed = urlparse(url)
querys = parsed.query.split("&")
querys[2]='csrfToken'+"="+data['csrfToken']
querys = '&'.join(querys)

url=url_post+"?"+querys

r = s.get(url)

soup = BeautifulSoup(r.content, 'html.parser')
print(soup)
Ответ написан
а зачем через яндекс карты, можно спарсить отзывы так
https://market.yandex.ru/shop/<ID_организации>/reviews
?
Ответ написан
@sk-evgen
Метод перестал работать (. Есть еще идеи?!
Ответ написан
Комментировать
@yara_bear
также есть запрос, кто-нибудь смог решить?
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы