Страницы

Поиск по вопросам

Показаны сообщения с ярлыком api. Показать все сообщения
Показаны сообщения с ярлыком api. Показать все сообщения

воскресенье, 29 марта 2020 г.

Как реализовать систему лайкинга на постах и в дальнейшем обрабатывать их? [закрыт]

#api #django_rest_framework


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 9 месяцев назад.
                                                                                
           
                
        
Всем привет! Стоит такая задача - 
В API есть модель "Post". В ней есть пару полей из разряда "id", "price", "name",
"description" и т.д. 
Необходимо реализовать систему лайкинга. То есть когда зарегистрированный пользователь
на клиенте(в данном случае, мобильное приложение) нажимает на кнопочку лайка, срабатывал
какой-то функционал лайкинга. Также необходимо, чтобы через API можно было достучаться
к этим "лайкнутым" постам.
Не знаю как это можно реализовать, чтобы в дальнейшем не перелопачивать всю API,
когда потребуется всё сделать по-человечески...

Пользователь стучится ко всем постам по такому URL - url.com/post/all
К конкретному, с большим кол-вом данных url.com/post/detail/1 - где "1" это ID поста

Есть ли какие-то примеры, best practices? Никак не могу найти.
    


Ответы

Ответ 1



Модель Для начала Вам потребуется создать модель. Предлагаю использовать ContentType. Это позволит связывать наш лайк сразу с несколькими моделями, что очень удобно в свою очередь. models.py: from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericForeignKey class Like(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likes') content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveSmallIntegerField() content_object = GenericForeignKey('content_type', 'object_id') class Post(models.Model): likes = GenericRelation(Like) @property def total_likes(self): return self.likes.count() API Предлагаю сделать like методом для нашей модели. Но перед этим нам нужно подготовить несколько скриптов. services: from django.contrib.contenttypes.models import ContentType from core.models import Like from django.contrib.auth.models import User def add_like(obj, user): obj_type = ContentType.objects.get_for_model(obj) like, is_created = Like.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user) return like def remove_like(obj, user): obj_type = ContentType.objects.get_for_model(obj) Like.objects.filter(content_type=obj_type, object_id=obj.id, user=user).delete() def is_fan(obj, user) -> bool: obj_type = ContentType.objects.get_for_model(obj) likes = Like.objects.filter(content_type=obj_type, object_id=obj.id, user=user) return likes.exists() def get_fans(obj): obj_type = ContentType.objects.get_for_model(obj) return User.objects.filter(likes__content_type=obj_type, likes__object_id=obj.id) mixins: from rest_framework.decorators import action from rest_framework.response import Response from core.api import services from core.api.serializers import FanSerializer class LikedMixin: @action(detail=True, methods=['post']) def like(self, request, pk=None): obj = self.get_object() services.add_like(obj, request.user) return Response() @action(detail=True, methods=['post']) def unlike(self, request, pk=None): obj = self.get_object() services.remove_like(obj, request.user) return Response() @action(detail=True, methods=['get']) def get_fans(self, request, pk=None): obj = self.get_object() fans = services.get_fans(obj) serializer = FanSerializer(fans, many=True) return Response(serializer.data) serializers: class FanSerializer(serializers.ModelSerializer): full_name = serializers.SerializerMethodField() class Meta: model = User fields = ( 'username', 'full_name', ) @staticmethod def get_full_name(obj): return obj.get_full_name() Не забудьте обновить поля в serializers также для модели Вашей записи и после этого можно приступать к финальной части. Представление Наследуемся от недавно созданного mixin и радуемся! views.py: class PostViewSet(LikedMixin, viewsets.ModelViewSet): ... Теперь у нас есть несколько новых API методов для нашей записи. Примечание: не забудьте, что метод like требует POST запроса, это может ввести Вас в заблуждение Наглядности все равно не хватает, поэтому вот исходники с некоторым окружением.

Как обработать Javascript-объект (невалидный JSON) в PHP?

#php #json #api


Возникла проблема - API отдаёт данные в JSON формате, примерно в таком виде:

{
  messages: [
    {
      channel: "79991234567",
      phone: "79117777777",
      dateTime: 1544844684000,
      type: 1,
      status: 99,
      text: "Добрый день"
    }, {
      channel: "79991234567",
      phone: "79117777777",
      dateTime: 1544849654000,
      type: 5,
      status: 2,
      text: "",
      content: "https://app.wazzup24.com/api/v1/store/07146d53b21e4a41aca49b6bc2391c7e8100b6dac6df"
    }
  ],
  statuses: [
    {
      messageId: "7271506a-1959-4e39-acaf-2ccc19d77ef2",
      status: 3
    }
  ],
  channels: [
    {
      channel: "79991234567",
      state: "active"
    }
  ]
}


Полученные данные нужно обработать через PHP и я пробовал сделать это через json_decode,
но эта функция не возвращала никаких данных. После некоторого поиска и просмотра примеров,
я понял, что причина скорее всего в том, что это JS-объект, но он не валиден как JSON,
т.к. ключи и некоторые свойства не обёрнуты в кавычки. Можно ли как-то еще решить эту
проблему?
Заранее спасибо.
    


Ответы

Ответ 1



ОТВЕТ СОДЕРЖИТ ОШИБКУ В массивах строками становятся числа (а отрицательные вообще всё ломают), null и true/false. Если разница по сравнению с json'ом только в том, что часть ключей написаны без кавычек, а всё остальное соответствует формату JSON, то это легко исправляется заменой по регулярному выражению (?:"((?:\\.|.)*)"|([.\w]+))(\s*:\s*(?:[[{]|"(?:\\.|.)*"|[-.\w]+))? на "$1$2"$3. Вот пример на JS: var str = document.querySelector('textarea').value var json = str.replace(/(?:"((?:\\.|.)*)"|([.\w]+))(\s*:\s*(?:[[{]|"(?:\\.|.)*"|[-.\w]+))?/g, '"$1$2"$3') console.log(JSON.parse(json)) .as-console-wrapper.as-console-wrapper { max-height: 100vh }

Ответ 2



Проблема решена, всем спасибо за ответы. При дальнейших поисках оказалось, что я был неправ: проблема не в том, что названия не обёрнуты в кавычки, а в том, что, как я узнал, PHP по умолчанию не парсит запросы кроме application/x-www-form-urlencoded multipart/form-data. В моем случае отправлялся запрос application/json и когда я пытался, получить к нему доступ из массива $_POST возвращался пустой массив. Помогло решение от @E_p (из вопроса Получение данных, отправляемых POST в виде json): $postData = file_get_contents('php://input'); $data = json_decode($postData, true); В моем случае это сработало. Еще раз спасибо всем за участие!

четверг, 13 февраля 2020 г.

Разработка высоконагруженного API [закрыт]

#php #sql #api #nosql


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 3 года назад.
                                                                                
           
                
        
Здравствуйте.

Собираюсь приступить к разработке API для мобильного приложения, количество ежедневных
пользователей которого по планам должно будет колебаться в районе десяти-двадцати миллионов.

Какие технологии лучше всего использовать при разработке высоконагруженного API-сервиса?
Допустимо ли использование "PHP" в качестве основного языка, или здесь непременно должен
иметь место "NodeJS"/"Ruby"? Если да - то почему? Каковы минусы и плюсы SQL- и NoSQL-СУБД
применительно к планируемой нагрузке? Какие средства кэширования редкообновляемой информации
лучше всего использовать?

Заранее благодарю за ответы.
    


Ответы

Ответ 1



Допустимо ли использование "PHP" в качестве основного языка, или здесь непременно должен иметь место "NodeJS"/"Ruby" Это все платформы одного порядка. С чуть большими возможностями у последних двух, но задаваться нужно вопросом выбора между "интерпретируемым языком общего назначения" и java-scala, c# и какими-то менее мейнстримовыми, о которых я не вспомнил. Упомянутые интерпретируемые (так уж повелось) более толерантны к ошибкам, временами сложнее в дебаге и проще в обновлении и непосредственно написании, упомянутые компилируемые нетерпимы к ошибкам, но имеют как желаемый прирост скорости, так и зачастую более интересный API. Какие технологии лучше всего использовать при разработке высоконагруженного API-сервиса? Те, которые отвечают вашим требованиям. Написать API, которое вытаскивает данные тысячу раз в секунду - не проблема, проблема - решить реальную бизнес-задачу. Пока неизвестно, что там, можно только гадать, где реально нужно стелить соломы, а так какая-нибудь связка php-mysql-redis спокойно сможет выдерживать эти пресловутые тысячи запросов и масштабироваться без кряхтения. В случае, если у вас начинается более интересная игра, начинают требоваться распределенные блокировки, сиюсекундное обновление кеша на всех хостах, параллельное перемалывание задачи на всех хостах без известности о том, сколько этих хостов и вообще прямой коммуникации между ними - тут начинается беда, которую надо разгребать конкретными решениями. Самая большая проблема, которая стоит перед разработкой этого сервиса - это shared-nothing архитектура, которая не предполагает закрепление за отдельным узлом какого-либо состояния, состояние хранится в базе данных. На первый взгляд кажется, что это условие и так выполняется, на практике все начинает выползать: при загрузке файла прогресс загрузки этого файла должен быть виден на всех узлах разом, кэширующий слой должен быть соединен воедино, чтобы все клиенты кэша видели одно и то же, простая блокировка ресурса превращается в целое приключение. Этому сложно научиться, не набив себе шишек, поэтому чем раньше вы присутпите к написанию на чем угодно и выкладке на несколько узлов, тем раньше прочувствуете эту проблему. Каковы минусы и плюсы SQL- и NoSQL-СУБД применительно к планируемой нагрузке? Как я уже написал, у SQL нет плюсов. По сравнению с NoSQL-решениями есть такая штука, как джойн, но от него отказались вполне сознательно, и он чаще вредит, чем помогает. Принципиальная разница для поставленной задачи в масштабировании: практически любое NoSQL-решение предлагает возможность горизонтального расширения из коробки, в то время как SQL этого не умеет. Перед тем, как вообще бросаться в выбор базы данных, надо понять, что от нее требуется. Кроме стандартного жестко стркутурированного типа SQL-совместимых баз данных есть четыре основных типов баз данных NoSQL: key-value (грубо говоря, доступ исключительно по первичному ключу без возможности осуществлять выборки), row-column (очень похоже на SQL, но, конечно, совершенно иная вещь под капотом), документоориентированные (т.е. без жестко заданной структуры) и графовые (предназначенные для работы со сложными связями). У вас, судя по всему, выбор стоит между документоориентированной и row-column БД: тут нельзя сказать что-то конкретное, и выбор остается за вами. Могу только сказать, что из row-column предпочтение обычно отдается Cassandra, а в документоориентированных любят монгу, но я слышал про нее довольно печальные отзывы (и в текущем проекте по ряду причин выбрали rethinkdb). Кроме всего это стоит помнить (и изучать) тот факт, что вся система теперь превращается в распределенную, и теперь у нее есть все любимые проблемы распределенных систем, в том числе разрыв сети и несинхронное обновление данных на разных узлах. Тут можно много сказать про проблемы производительности, про возможные подводные камни, но на самом деле все упирается банально в то, насколько хорошо ваша платформа масштабируется, и можете ли вы выкатить новый сервак в течение дня, потому что даже самый идеальный бэкенд имеет пропускной предел. Единственное, про что еще хотелось бы сказать - это то, что переход на распределенные системы, как правило, требует еще и смены парадигмы pull-on-demand на push-on-change.

Ответ 2



Вы можете разработать такой сервис на любой технологии, в том числе с использованием PHP. Поставив рядом несколько серверов, вы можете практически бесконечно масштабировать операции чтения за счет репликации (как на уровне СУБД, так и на уровне NoSQL-решений). Проблемы начинаются, когда у вас очень много одновременных соединений, даже если вы можете под каждый из них выделить отдельный поток-запрос. Просто их так много, что процессор переключаясь между ними начинает терять слишком много времени. Так как вы работаете для мобильных приложений, у вас может быть очень не важная связь с клиентом, а значит много медленных клиентов. Чем страшен медленный клиент для классического сервера? Пусть у вас сервер может держать 1000 одновременных запросов, подключаются к вам 500 клиентов с EDGE и начинают в течение 10 минут ждать ответа. И вот у вас уже сервер обслуживает не 1000, а 500 одновременных запросов. Так как 500 повисли в ожидании ответа от клиентов. Оставшиеся соединения начинают не справляться, отдавать запросы медленнее, и вот у вас сервер может обслуживать только 250 запросов. А через некоторое время и вообще грохается. Это может не так актуально для Web, где клиенты в массе пошустрее, но в мобильной разработке это более частое явление – покрытие везде разное, связь от него зависит здорово. Когда говорят о NodeJS или серверах Ruby – там ничего волшебного нет, просто сервера более новые и проектировались немного по-другому. Вместо того, чтобы ждать каждое соединение отдельным запросом организуется один поток, который опрашивает соединения в неблокирующем режиме. Пришел ответ от клиента – он его обрабатывает, нет - пошел к следующему соединению. Этим убивается два зайца. Вы не переключаетесь между потоками/процессами (экономите время), вы решаете проблему медленных клиентов – у вас поток не ждет, он постоянно работает. Проблема только чтобы у вас сервер-сторона не тормозила, так как опрашивающий поток один. Это Event Driven архитектура, можно найти решения и для PHP. Просто в случае NodeJS или Ruby они идут чуть не из коробки, в случае PHP придется поискать решение. Однако, какой бы вы язык не выбрали, разобраться с этим важно. Event Driven архитектура позволяет вам задействовать WebSocket-ы, постоянные соединения от клиентов – вы можете позволить их сколько угодно, так как соединение в ожидающем режиме почти не расходует никаких ресурсов (ни памяти, ни процессора). И вот мы плавно подходим к тому, как вы будете писать. Даже если вы не используете EventDriven-решения, а поставили рядом много классических fork-серверов, реплицировали данные на уровне базы данных, у вас все равно остается проблема с записью, так как она не масштабируется репликацией. Вам нужно очень быстро писать, чтобы клиент не ждал ответа от сервера, не держал соединение, а шел по своим делам. Чем быстрее вы будете их обслуживать – тем вам будет проще. Писать непосредственно в базу данных – не вариант. Классические СУБД очень медленные и неторопливые, норовят все в транзакциях выполнять, а это тоже накладные расходы. Есть два пути – быстрое NoSQL-решение полностью распложенное в оперативной памяти (redis, MongoDB). Быстро записали в память, отпустили клиента, в фоне разбираемся, записываем. Очереди – позакидали все в очередь, в фоне не спеша события из очереди обрабатываются. Было бы здорово, если часть информации преобразовывалась, агрегировалась непосредственно в оперативной памяти и лишь потом поступала в медленную базу данных "оптом". Чем хороши современные NoSQL-решения – они почти все написаны с учетом неблокирующих соединений и могут держать тысячи одновременных соединений. Чем плохи – это не классическая СУБД, там сложно организовывать связи и ассоциации, вообще нужно работать немного по другому. В них плохо с транзакциями. Однако, они расположены в оперативной памяти и отлично кластеризуются. Принимать в них данные одно удовольствие – это не сурогат из 100 таблиц с последующим слиянием в СУБД, чтобы писать данные было проще, и не геморройная кольцевая архитектура в репликации. В общем на практике обычно используется все, что вы упомянули, можно часть написать на PHP, с WebSocket-ами вам возможно будет удобнее работать через NodeJS-сервер, очереди и PubSub-решения возможно будет проще организовать через Redis или RabbitMQ, "сырые" данные положить в то же Reids или MongoDB, а в качестве долговременного резервируемого хранилища использовать классическую СУБД (хотя можно вообще без классической СУБД обойтись). Если вы доведете свое API до заявленных нагрузок, вам скорее всего придется попробовать если не все, то почти все :)

среда, 5 февраля 2020 г.

В чем разница между API и REST API?

#api #rest #терминология


На собеседовании мне задали вопрос "В чем разница между API и REST API?". 
Может кто нибудь пояснить? 
    


Ответы

Ответ 1



API - это общий термин, который означает "программный интерфейс приложения". Rest API - это конкретный API, под названием REST, описывающий протокол взаимодействия с веб-сервисом.

Чем отличается api от sdk?

#api #терминология #sdk


вроде и то и то предоставляет доступ к какому-либо функционалу
    


Ответы

Ответ 1



SDK - это набор функционала(библиотек) и утилит для разработки. Собственно SDK и предоставляет реализацию некоторого API, это оболочка API's, которая упрощает работу для разработчиков. API: набор готовых классов, процедур, функций, структур и констант, предоставляемых приложением для использования во внешних программных продуктах это интерфейс, похоже на спецификацию телефонной системы или электропроводки в вашем доме. это список того, что можно вызывать и какого ждать результата. SDK: пакет реальных инструментов внедрения. Это, как комплект, который позволяет вам подключиться к телефонной системе или электрической проводке. это библиотеки, в которых реализованы вызываемые функции + файлы необходимые для подключения этих библиотек

Ответ 2



API (Application programming interface) - это описание интерфейса чего-либо. Набор правил по которым что-то должно работать. SDK (Software development kit) - это набор инструментов для работы с чем-то. Вы можете рассматривать свой любимый язык программирования как API (набор правил синтаксиса и поведения), а свою любимую среду - как SDK (набор инструментов для написания, проверки и отладки кода).

Ответ 3



API штука очень абстрактная. Это лишь словарь для перевода обращений к сервису в действия. Указывает "куда обращаться, чтобы попросить сервис сделать <...>". "Куда обращаться" может описываться разными способами в зависимости от того, как к сервису осуществляется доступ: Для сетевых API это адреса узлов, пути в пределах узлов, спецификации для запроса, ответа, сообщений/датаграмм или всего протокола связи. Для библиотек под языки программирования или написания плагинов это доступные потребителям функции/классы/интерфейсы/и т. п. В какой-то мере можно считать, что API это спецификация/документация. Ничего более вещественного он из себя не представляет. Один API может быть описан различными способами, но при этом быть одним и тем же API. SDK же вещь гораздо более осязаемая: это комплект программного обеспечения для разработки под некий API. Посему он неминуемо содержит описание API, но во вполне конкретной форме, связанной с тем, из какой среды к API осуществляется доступ. К примеру, для HTTP-сервиса API это спецификация того, какие HTTP-запросы способен принимать сервис: какими могут быть методы + пути + заголовки + тела запросов, какие у каждого запроса будут последствия и какие на них придут ответы. А SDK для такого сервиса для некоего языка программирования может представлять собой заранее заголовленный набор классов, в котором спрятана HTTP-шная природа интерфейса, и обращение к сервису выглядит просто как вызов функции. Такой SDK практически преобразует один API в другой, более удобный для разработки с конкретными технологиями.

воскресенье, 2 февраля 2020 г.

С чего начать изучение API?

#java #android #веб_программирование #api #asp


У меня такая ситуация,  У нас есть 1  урок по ASP.Net и другой урок по разработке
Android приложений, и я хочу написать веб  приложение на ASP и его мобильное прил.
для Android. И препод  говорит что можно просто добавить API от веб приложения в мобилку
и чтоб  вся обработка шла туда. То есть  написать бэк-енд на ASP и использовать его
и в мобильном андроид  приложении(хотя бэк Андроида вроде на Java). Как это сделать
или как правильно задавать вопросы гуглу или как искать уроки по этой теме ??
    


Ответы

Ответ 1



Если вы будете самостоятельно писать бэк для своего приложения то начинать нужно с изучения всего что связано с серверной частью. Если же вам уже будут давать готовое апи для вашего приложения, то нужно начинать изучать принципы отправки запросов на сервер и обработки его ответов. Я на данный момент занимаюсь разработкой клиент-серверного приложения, и мне дают документацию по апи, где прописаны все запросы необходимые для полного замещения веб-сервиса моим приложением. Для работы с апи я использую библиотеку Retrofit, хотя есть и Volley - это уже кому какая придется по душе. Лично я бы вам советовал начать читать про сами клиент-серверные приложения. Библиотеку я вам посоветую использовать Retrofit потому-что она более удобная и гибкая, и лично мне более понятна в использовании. Ниже я привожу несколько ссылок которые облегчат выполнение ваших задач: Инструмент для создания классов-моделей, которые будут использоваться в запросах. Сервис, который показывает структуру вашего ответа с сервера. Туториал для работы с ретрофитом. Статья по библиотекам которые я указывал. Еще статья по работе с retrofit Надеюсь хоть чем-то помог, если будет что-то не понятно - не стесняйтесь и спрашивайте. Удачи, я верю что у вас все получится :)

вторник, 28 января 2020 г.

Как определенный элемент массива вставить на первое место?

#javascript #массивы #api


Есть лексикографически отсортированный массив объектов, получаемый из api, где есть
одно из свойств - имя города(Name)
Мне необходимо отсортировать, что бы сначала шел город, который мне нужен, а все
остальные по алфавиту.

const result = response.data
          result.sort( (a, b) => {
            return a.Name.localeCompare(b.Name)
          });


Можно как-нибудь задать такой порядок сортировки, что бы сначала на первом месте
был какой-то определенный город, скажем "Москва", а затем всё остальные элементы отсортировались
лексикографически?
Я пытался сделать что-то типо того:

if(a.Name === 'Москва') {
return 1;
}
if (a.Name < b.Name) {
  return -1;
  }
return 0;


Но это совсем не то...
    


Ответы

Ответ 1



Можно так const arr = [ {Name: 'НеМосква1'}, {Name: 'НеМосква2'}, {Name: 'Москва'}, {Name: 'НеМосква3'} ]; var result = arr.sort( (a, b) => { if(a.name == "Москва") return -1; if(b.name == "Москва") return 1; return a.Name.localeCompare(b.Name) }) console.log(result);

Ответ 2



const result = [ {Name: 'НеМосква1'}, {Name: 'НеМосква2'}, {Name: 'Москва'}, {Name: 'НеМосква3'} ]; // Сначала отсортируйте result.sort((a, b) => a.Name.localeCompare(b.Name)); //Потом поставьте нужный город на первое место: const [item] = result.splice(result.findIndex(a => a.Name === 'Москва'), 1); result.splice(0, 0, item) console.log(result);

пятница, 24 января 2020 г.

Как парсить вакансии с API Зарплата.ру в R

#json #парсер #api #r


Есть API джоб-сайта "Зарплата.ру"

https://api.zp.ru/v1/


Как парсить вакансии в R с пакетом jsonlite? То есть, как именно нужно обратиться?
К примеру, через API HeadHunter это корректно делается так:

  string <-"https://api.hh.ru/vacancies?text=\"'machine+learning\"&page="
for (pageNum in 0:5){ # Всего страниц
  data <- fromJSON(paste0(string,  pageNum))
  vacanciesdf <- rbind(vacanciesdf, data.frame(
    data$items$area$name, # Город
    data$items$salary$currency, # Валюта
    data$items$salary$from, # Минимальная оплата
    data$items$employer$name, # Название компании
    data$items$name,#Название должности
    data$items$snippet$requirement)) # Требуемые навыки
  print(paste0("Upload pages:", pageNum + 1))
  Sys.sleep(3)
}


Как решить аналогичную задачу через API "Зарплата.ру" чтобы была возможность задать
ключевое слово и рассортировать данные по столбцам data.frame?
    


Ответы

Ответ 1



Пример кода для выполнения запроса с использованием пакета crul. # HHTP клиент cl <- crul::HttpClient$new(url = "https://api.zp.ru") # Запрос к API resp <- cl$get(path = "v1/vacancies", query = list(scope = "public", q = "machine+learning", limit = 100L)) # Парсинг ответа ans <- jsonlite::fromJSON(resp$parse(encoding = "UTF-8")) # Количество записей в результате выдачи cat(ans$metadata$resultset$count) #> 2 # Извлекаем необходимые поля res <- ans$vacancies data.frame( header = res$header, published_at = as.Date(res$publication$published_at), salary = res$salary, education = res$education$title, experience_length = res$experience_length$title, schedule = res$schedule$title, working_type = res$working_type$title, requirements = res$requirements, url = paste0("https://www.zp.ru", res$url), company = res$company$title, address = paste(res$address$city$title, res$address$street, res$address$building) ) #> header published_at salary education #> 1 Senior, Middle Data scientist 2017-12-08 договорная высшее #> 2 Junior Data scientist 2017-12-08 договорная высшее #> experience_length schedule working_type #> 1 3-5 лет гибкий график полная занятость #> 2 без опыта гибкий график полная занятость #> requirements #> 1 Высшее образование, стаж работы 3-5 лет, полная занятость #> 2 Высшее образование, без опыта, полная занятость #> url #> 1 https://www.zp.ru/vacancy/Senior_Middle_Data_scientist?id=139080429 #> 2 https://www.zp.ru/vacancy/Junior_Data_scientist?id=139080474 #> company address #> 1 СКБ Контур Екатеринбург Малопрудная 5 #> 2 СКБ Контур Екатеринбург Малопрудная 5 Для выгрузки всех результатов в случае если их больше 100, необходимо использовать параметр offset. Описание полей возвращаемого результата приведено в документации по API. При необходимости можно извлекать только нужные поля при помощи параметра fields. Например: query = list(scope = "public", q = "machine+learning", limit = 100L, fields = "header,company.title") Пример постраничной выгрузки: #' @title Функция для выгрузки вакансий с сайта zp.ru #' @param cl HTTP клиент. Создаётся при помощи `crul::HttpClient`. #' @param query Строка, содержащая запрос. #' @param limit Целое число от 1 до 100, определяющее количество результатов. #' @return data.frame с результатами запроса fetch_vacancies <- function(cl, query) { limit <- 100L q <- list( scope = "public", q = query, limit = limit ) fetch_data <- function(query) { # Запрос к API resp <- cl$get(path = "v1/vacancies", query = query) # Проверка статуса ответа resp$raise_for_status() # Парсинг ответа jsonlite::fromJSON(resp$parse(encoding = "UTF-8")) } extract_data <- function(data) { res <- data$vacancies data.frame( header = res$header, published_at = res$publication$published_at, salary = res$salary, education = res$education$title, experience_length = res$experience_length$title, schedule = res$schedule$title, working_type = res$working_type$title, requirements = res$requirements, url = paste0("https://www.zp.ru", res$url), company = res$company$title, address = paste(res$address$city$title, res$address$street, res$address$building) ) } ans <- fetch_data(q) res <- extract_data(ans) # Орабатываем случай, если результатов больше 100 if (ans$metadata$resultset$count > limit) { e <- new.env() # Доабвляем уже полученные данные e[["0"]] <- res offset <- 101L count <- ans$metadata$resultset$count # Повторяем запросы и парсинг со смещением в 100 while (offset < count) { q$offset <- offset res <- extract_data(fetch_data(q)) e[[as.character(offset)]] <- res # Предотвращаем спам запросов Sys.sleep(0.4) # Выводим сообщение cat("\rFetch page ", (offset - 1L) / 100L) offset <- offset + 100L } # Собираем все результаты res <- do.call(rbind, as.list(e)) } # Добавляем обработанный запрос в атрибуты рзультата attr(res, "query") <- ans$metadata$query$searched_q return(res) } # HHTP клиент cl <- crul::HttpClient$new(url = "https://api.zp.ru") query <- "Аналитик" res <- fetch_vacancies(cl, query)

Преобразование JSON в URL текст

#python #json #post #api #urlencode


В поисках решения вот этой проблемы выяснил, что проблема в API, оно принимает не
JSON, а в отправляемых данных должен быть URL text. Вопрос такой, можно ли преобразовать
такой JSON:

JSONFORMDATA = {

"add": [
{
    "source_name": "WEB сайт",
    "source_uid": "a1fee7c0fc436088e64ba2e8822ba2b3",
    "created_at": "1529007000",
    "incoming_entities": {
        "leads": [
            {
                "name": "Покупка"
            }
        ],
        "contacts": [
            {
                "name": "Федя",
                "responsible_user_id": "1903006",
                "custom_fields": [
                    {
                        "id": "382707",
                        "values": [
                            {
                                "value": "+77777777777",
                                "enum": "WORK"
                            }
                        ]
                    },
                    {
                        "id": "389993",
                        "values": [
                            {
                                "value": "sfgh3gh233h3h3h3"
                            }
                        ]
                    },
                    {
                        "id": "389995",
                        "values": [
                            {
                                "value": "Обратный звонок"
                            }
                        ]
                    }
                ]
            }
        ]
    },
    "incoming_lead_info": {
        "form_id": "329248",
        "form_page": "vdtest.ru",
        "ip": "127.0.0.1",
        "service_code": "QkKwSam8"
    }
}
]

}


в такой формат:

add[0][source_name]=WEB FORM
&add[0][source_uid]=wb0123456789
&add[0][created_at]=1529105760
&add[0][incoming_entities][leads][0][name]=WEB Bay
&add[0][incoming_entities][contacts][0][name]=John
&add[0][incoming_entities][contacts][0][responsible_user_id]=2473552
&add[0][incoming_entities][contacts][0][custom_fields][0][id]=382707
&add[0][incoming_entities][contacts][0][custom_fields][0][values][0][value]=+792887776655
&add[0][incoming_entities][contacts][0][custom_fields][0][values][0][enum]=MOB
&add[0][incoming_entities][contacts][0][custom_fields][1][id]=389993
&add[0][incoming_entities][contacts][0][custom_fields][1][values][0][value]=cid123456789
&add[0][incoming_entities][contacts][0][custom_fields][2][id]=389995
&add[0][incoming_entities][contacts][0][custom_fields][2][values][0][value]=Product
&add[0][incoming_lead_info][form_id]=329248
&add[0][incoming_lead_info][form_page]=localhost
&add[0][incoming_lead_info][ip]=127.0.0.1
&add[0][incoming_lead_info][service_code]=myformmw


Это декодированные данные из URL text, которые принимает API:

add%5B0%5D%5Bsource_name%5D=WEB+FORM&add%5B0%5D%5Bsource_uid%5D=wb0123456789&add%5B0%5D%5Bcreated_at%5D=1529105760&add%5B0%5D%5Bincoming_entities%5D%5Bleads%5D%5B0%5D%5Bname%5D=WEB+Bay&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bname%5D=John&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bresponsible_user_id%5D=2473552&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B0%5D%5Bid%5D=382707&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B0%5D%5Bvalues%5D%5B0%5D%5Bvalue%5D=%2B792887776655&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B0%5D%5Bvalues%5D%5B0%5D%5Benum%5D=MOB&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B1%5D%5Bid%5D=389993&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B1%5D%5Bvalues%5D%5B0%5D%5Bvalue%5D=cid123456789&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B2%5D%5Bid%5D=389995&add%5B0%5D%5Bincoming_entities%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields%5D%5B2%5D%5Bvalues%5D%5B0%5D%5Bvalue%5D=Product&add%5B0%5D%5Bincoming_lead_info%5D%5Bform_id%5D=329248&add%5B0%5D%5Bincoming_lead_info%5D%5Bform_page%5D=localhost&add%5B0%5D%5Bincoming_lead_info%5D%5Bip%5D=127.0.0.1&add%5B0%5D%5Bincoming_lead_info%5D%5Bservice_code%5D=myformmw


Как выяснилось, в примере PHP не используют чистый JSON, а строят массив. Может в
Python тоже необходимо построить подобный массив или есть способ проще, ведь на сколько
я понимаю, как таковых массивов в Python нет.
    


Ответы

Ответ 1



Честно, рекурсию не очень люблю, но в таких задачах она себя хорошо показывает: Данные (из вопроса): JSON_FORM_DATA = { "add": [ ... } Код: def dict_to_url_params(json_data, root): def deep(node, root, items): if isinstance(node, list): for i, value in enumerate(node): node_root = root + '[{}]'.format(i) deep(value, node_root, items) elif isinstance(node, dict): for key, value in node.items(): node_root = root + '[{}]'.format(key) deep(value, node_root, items) else: root += '=' + node items.append(root) items = [] deep(json_data, root, items) return items if __name__ == '__main__': items = dict_to_url_params(JSON_FORM_DATA['add'], root='add') params = '&'.join(items) print('Params: ' + params) print() print('Params:') for x in items: print(x) Результат: Params: add[0][source_name]=WEB сайт&add[0][source_uid]=a1fee7c0fc436088e64ba2e8822ba2b3&add[0][created_at]=1529007000&add[0][incoming_entities][leads][0][name]=Покупка&add[0][incoming_entities][contacts][0][name]=Федя&add[0][incoming_entities][contacts][0][responsible_user_id]=1903006&add[0][incoming_entities][contacts][0][custom_fields][0][id]=382707&add[0][incoming_entities][contacts][0][custom_fields][0][values][0][value]=+77777777777&add[0][incoming_entities][contacts][0][custom_fields][0][values][0][enum]=WORK&add[0][incoming_entities][contacts][0][custom_fields][1][id]=389993&add[0][incoming_entities][contacts][0][custom_fields][1][values][0][value]=sfgh3gh233h3h3h3&add[0][incoming_entities][contacts][0][custom_fields][2][id]=389995&add[0][incoming_entities][contacts][0][custom_fields][2][values][0][value]=Обратный звонок&add[0][incoming_lead_info][form_id]=329248&add[0][incoming_lead_info][form_page]=vdtest.ru&add[0][incoming_lead_info][ip]=127.0.0.1&add[0][incoming_lead_info][service_code]=QkKwSam8 Params: add[0][source_name]=WEB сайт add[0][source_uid]=a1fee7c0fc436088e64ba2e8822ba2b3 add[0][created_at]=1529007000 add[0][incoming_entities][leads][0][name]=Покупка add[0][incoming_entities][contacts][0][name]=Федя add[0][incoming_entities][contacts][0][responsible_user_id]=1903006 add[0][incoming_entities][contacts][0][custom_fields][0][id]=382707 add[0][incoming_entities][contacts][0][custom_fields][0][values][0][value]=+77777777777 add[0][incoming_entities][contacts][0][custom_fields][0][values][0][enum]=WORK add[0][incoming_entities][contacts][0][custom_fields][1][id]=389993 add[0][incoming_entities][contacts][0][custom_fields][1][values][0][value]=sfgh3gh233h3h3h3 add[0][incoming_entities][contacts][0][custom_fields][2][id]=389995 add[0][incoming_entities][contacts][0][custom_fields][2][values][0][value]=Обратный звонок add[0][incoming_lead_info][form_id]=329248 add[0][incoming_lead_info][form_page]=vdtest.ru add[0][incoming_lead_info][ip]=127.0.0.1 add[0][incoming_lead_info][service_code]=QkKwSam8

среда, 22 января 2020 г.

example api vine javascript

#javascript #ajax #api


Я тут сутки не спал, поэтому помогите мне подумать.

Есть плеер Vine. У него есть апи https://dev.twitter.com/web/vine/oembed .
Можете сразу посмотреть все заголовки ответа тут https://vine.co/oembed.json?id=5xJVBXAunrD

Мне нужно с клиента получить данные
отправляю запрос



$.ajax({
  url: 'https://vine.co/oembed.json?id=5xJVBXAunrD',
  dataType: "jsonp",
  contentType: "application/json",
  success: () => {
    console.log(arguments)
  },
})





Получаю по лицу: 


  Refused to execute script from
  'https://vine.co/oembed.json?id=5xJVBXAunrD&callback=jQuery211011806981946079831_1482419611704&_=1482419611705'
  because its MIME type ('application/json') is not executable, and
  strict MIME type checking is enabled.


В общем, как я понял, из-за того самого строго соответствия мне нужно все это "Добро"
проксировать?
    


Ответы

Ответ 1



Ошибка происходит из-за того, что сервис отдает json, а dataType = jsonp. Если бы сервис разрешал обращаться к себе с различных сайтов достаточно было бы просто заменить dataType на json. Но в данном случае сервис не разрешает и браузер выдаст ошибку No 'Access-Control-Allow-Origin' header is present on the requested resource. В качестве решение можно либо делать запрос к своему серверу, и уже с него делать серверными средствами запрос, либо воспользоваться одним из сервисов, например: https://cors-anywhere.herokuapp.com/ Пример: $.ajax({ url: 'https://cors-anywhere.herokuapp.com/https://vine.co/oembed.json?id=5xJVBXAunrD', dataType: "json", contentType: "application/json", success: data => { console.log(data) }, }); .as-console-wrapper { top: 0; max-height: 100% !important; }

Ответ 2



Попробуйте заменить контент тайп на Content-Type: application/javascript

воскресенье, 12 января 2020 г.

Как правильно хранить сессию при разработке JSON API?

#json #api #сессия


Обычно мы храним id сессии в cockie браузера. Но допустим я хочу использовать свое
API в мобильных приложениях ios/android, и я собираюсь хранить куку в памяти приложения
и вставлять потом в каждый запрос с приложения. Но на сколько это правильно? Есть ли
какие-то советы по этому поводу?
    


Ответы

Ответ 1



Но на сколько это правильно? Тут просто нет другого подхода. Вам так или иначе надо идентифицировать пользователя API. По каким-либо косвенным признакам вы его идентифицировать не можете - на одном IP могут сидеть несколько потребителей, User-Agent нет в принципе, да и вообще это просто подменяемый заголовок и не более. Поэтому вам надо с каждым запросом передавать некий идентификатор, который сможет помочь вам найти пользователя. Тут есть несколько тонких моментов: Это не должен быть сам идентификатор пользователя (иначе весь механизм ломается через угон идентификатора, и с угнанным пользователем нельзя сделать ничего, кроме его убийства) Этот идентификатор (или механизм, связанный с ним) должен однозначно подтверждать корректность сессии либо передаваться исключительно по защищенному каналу (HTTPS), иначе его можно будет ровно так же угнать и представляться некорректным пользователем. Идентификаторы должны считаться расходным материалом в приложении: их всегда можно грохнуть и насоздавать новых, чтобы любой угнанный идентификатор мог быть обнулен сразу после обнаружения угона Общепринятым подходом считается передаче токена доступа в произвольном заголовке (github, google, amazon s3). Так или иначе информация для аутентификации всегда остается в заголовках (в query parameters и непосредственно теле запроса ей не место), поэтому здесь на самом деле тоже особого выбора нет. Токен хранится на устройстве, в случае паники пользователь удаляет токен (пользуясь тем же токеном доступа) Последний абзац - про подтверждение корректности сессии, передаваемое по незащищенному каналу. В случае, если есть опасения за перехват токена при его передаче - необходимо так или иначе подписывать исходящие запросы. Для этого можно использовать полновесную или легковесную challenge-response аутентификацию, смысл которой можно свести к тому, что сервер и клиент загадывают друг другу загадки, которые могут разгадать только они сами. В самом простом варианте сервер может выписывать токен, состоящий из идентификатора и секретной части (ключа шифрования), которая однократно передается клиенту при создании токена (клиент тоже может при этом переать некий секрет, но мы считаем, что серверу всегда можно доверять). После этого все сообщения от клиента к серверу либо полностью шифруются полученным ключом, либо содержат в дополнительном заголовке сформированную на основе ключа и тела запроса подпись, которую злоумышленник не сможет подделать, не своровав секретный ключ. Это, однако, не защищает само по себе от: Replay-атак (e.g. если пользователь шлет запрос "удалить последнюю запись", то злоумышленник может сгенерировать еще сотню таких запросов и полностью очистить историю пользователя). Самое простое решение - жонглирование временными метками (e.g. не принимать запросы старше 5 секунд), но при малейших проблемах с синхронизацией стреляет в ногу. Физического изъятия ключа у клиента. Обычно считается, что от этого нельзя защититься, и этот момент просто игнорируется. Перехвата ключа в момент выписывания токена (поэтому лучше все-таки обзавестись HTTPS). При открытом трафике можно воспользоваться алгоритмом Диффи-Хеллмана и аналогами, но это займет времени больше, чем написание самого приложения. Вычисления ключа при использовании некорректного алгоритма (e.g. если не происходит ротации ключа и если злоумышленник знает, что всегда используется JSON, то он, зная, что последний символ всегда является } или ], может вычислить ключ, сравнивая запросы разной длины). Опять же, проще взять HTTPS, есть альтернативные методы, но я не буду их рассказывать, чтобы ненароком не пропустить важный момент. upd. есть некий стандарт json web token, сам я с ним пока не ознакомился.

Ответ 2



Это один из правильных подходов к разработке API. Есть такое понятие при разработке REST API, как отсутствие состояния (statelessness, STATEless). Состояние клиента не хранится на сервере, ни в каком виде, этим занимается исключительно сам клиент Серверы не связаны с интерфейсами клиентов и их состояниями. На стороне сервера не сохраняется пользовательский контекст между двумя разными запросами. Каждый запрос содержит всю информацию, необходимую обработчику, а состояние сессии хранится на клиенте. Несохранение состояния улучшает производительность Web-сервиса и упрощает дизайн и реализацию серверных компонентов, поскольку отсутствие состояния на сервере устраняет необходимость синхронизации сеансовых данных с внешним приложением.

суббота, 11 января 2020 г.

Работа с исключениям при достижении лимита постов

#python #vkontakte_api #api


Есть скрипт, который отправляет в ВК посты в 2 группы. Сообщения разные и количество
их тоже разное, но наступает момент когда в одной группе лимит постов заканчивается
и скрипт прекращает работать с ошибкой 214. 

Как и что прописать чтобы скрипт продолжал работать со второй группой?

    if data1:
        api.wall.post(owner_id='-1', message=text1)
    if data2:
        api.wall.post(owner_id='-2', message=text2)


Ошибка:


  vk.exceptions.VkAPIError: 214. Access to adding post denied: you can only
  add 50 posts a day

    


Ответы

Ответ 1



Попробуйте так: def wall_post(**kwargs): try: api.wall.post(**kwargs) except vk.exceptions.VkAPIError as e: if e.code == 214: #print('Warning: {}'.format(e.error_msg)) pass else: raise if data1: wall_post(owner_id='-1', message=text1) if data2: wall_post(owner_id='-2', message=text2) PS у меня нет VK account'а, поэтому код не протестирован

четверг, 9 января 2020 г.

Сайт это тоже API?

#веб_программирование #api #терминология


Гуглил о том что такое API, и везде примеры сводятся к тому, что вот есть сайт и
к нему можно приделать API чтобы другие приложения могли взаимодействовать с сайтом.
Но никто не пишет о том, является ли API сам сайт, когда клиент из браузера отправляет
к нему запрос. Ведь по сути когда я в браузере открываю сайт example.com то, сам браузер
обращается к API сайта и получает text/html ответ т.е. получается взаимодействие двух
приложений.

Так вот вопрос: Сайты это тоже API для взаимодействия между браузером и сервером?
(не совсем корректно сформулированный вопрос, но надеюсь смысл понятен)
    


Ответы

Ответ 1



Сложность вопроса в том, что термин API весьма всеобъемлющий и не очень чёткий. Ответ сильно зависит от контекста. С чисто теоретической точки зрения да, можно считать и так. Пользователь всё-таки не может взаимодействовать с сайтом сколько-нибудь напрямую. Посему, можно считать, что сайт как множество допустимых HTTP-запросов к нему, это API между сервисом, который он предоставляет, и браузером. Причём поскольку в ответах API описываются возможные запросы, он ещё и, в некотором смысле, самодокументируемый. Это круто. В реальном коде сервис и его "браузерный API" (называемый иногда "веб-интерфейсом") могут быть практически неразделимы. Обычно это плохо. Но может быть простительно, если других интерфейсов не ожидается, и/или если сервис сам по себе является интерфейсом к чему-то ещё. Но если рассуждать так и далее, то и выполняемые вами (человеком; вы же человек?) действия с браузером происходят через API: интерфейсы ввода-вывода ОС. Тут, впрочем, API уже заканчиваются и начинается аппаратный интерфейс. Но это с вашей позиции. Существуют также программно управляемые браузеры (был специализированный PhantomJS, сейчас это штатно существует и в Chrome, есть и другие), которые для серверов очень похожи на обычных пользователей. Для управляющих ими программ множество возможных запросов к сайту является API в самом прямом смысле: программа взаимодействует через этот интерфейс с приложением. А на практике, в веб-разработке обычно считается, что браузер — пользователь. То, что за ним ещё какие-то интерфейсы, уже не в её области. И там принято разбивать HTTP-сервисы на две группы: сайты: с которыми браузеры взаимодействуют более-менее напрямую, без дополнительных механизмов API: с которыми напрямую взаимодействуют другие приложения, самостоятельные (автоматизация действий штатными способами или взаимодействие между сервисами) или SPA (запускаемые из кода, доставленного сайтом)

понедельник, 6 января 2020 г.

Wordpress API для плагина

#json #wordpress #api #link


Нужно как-то сделать в плагине, чтобы при переходе по ссылке определенного типа отдавался JSON
где token - это код сгенерированный за алгоритмом текущего дня (этот код знает сервер
и отправитель)

method - это указатель метода для плагина   

Ссылка должна получится вида

http://domain.ru/wp-api/token/method?postdata=data


Я нашел частичный ответ 
тут

Но как читается эта штука, что это такое, и как ее доработать под меня. 

 add_rewrite_rule('^wp-api/pugs/?([0-9]+)?/?','index.php?__wp-api=1&pugs=$matches[1]','top');


Готов все попробовать сделать сам, но просто объясните как сделать точку входа и
чтоб она работала.
    


Ответы

Ответ 1



Если я верно угадал в комментарии к вашему ответу, что вы на самом деле хотите сделать, то есть два варианта решения данной задачи. Первый - разрабатывать действительно свой API, как советуют по вашей ссылке, то есть точку входа для внешних источников запроса. Но это довольно громоздкий вариант, который вряд ли вам нужен. Второй - просто принимать некоторые запросы и в ответ на них слать некий ответ. В предыдущем ответе другим пользователем вам был дан в целом рабочий для такого решения совет, но, как по мне, более правильно будет поступить описанным ниже образом. Главным образом потому, что конкретно для асинхронных запросов именно от плагинов, а у вас наверняка такая задача и стоит, именно описанный ниже функционал и подходит (он находится в разделе Plugin API), в то время как предложенный другим пользователем вариант - это использование функционала редиректов, что совершенно неверно методологически. Функционал этот завязан на такой хук: wp_ajax_(action) / wp_ajax_nopriv_(action). Если коротко, то он работает "из коробки" для запросов, сформированных из админки, и с небольшим применением магии работает и для фронта сайта. Подробное описание, как это сделать, есть тут. Ключевых моментов тут три: Написать функцию, которая будет делать POST-запрос на адрес admin-ajax.php (адрес будет использоваться в т.ч. и для фронта). Написать колбек, который будет использоваться для ловли вашего запроса и ответа на него, который, в свою очередь, повесить на этот запрос с помощью упомянутого хука wp_ajax_(my_action) (часть в скобках меняется на ваше название). Если запрос нужно делать не только из админки, то повесить тот же колбек с помощью хука wp_ajax_nopriv_(action). Добавить на фронт js-объект myajax (название произвольно), в котором будете хранить адрес вашего эндпоинта, название запроса, ключ для проверки запроса на принадлежность к вашему сайту (wp_nonce, токен, но это не про ваш токен, а про токен конкретно для этого функционала) и другие нужные вам на фронте переменные.

Ответ 2



Посмотрите в сторону endpoint'ов в WordPress. Добавьте свой endpoint, например json. И через хук add_action( 'template_redirect', 'function' ); разбирайте полученный результат. get( 'json' ); if ( ! $json ) { return; } // Код для получения JSON // ..................... wp_send_json( $json ); } add_action( 'template_redirect', 'mihdan_template_redirect' ); ?>

Откуда берут информацию о кинотеатрах и показах в них?

#php #api


Собственно существуют различные сайты с информацией о кинотеатрах различных городов
и показах в них. Самые крупные известные мне - это кино афиша и кино поиск яндекса. 
Интересует откуда они берут информацию, сомневаюсь что база заполняется в ручную.
Существует ли некое свободно доступное API? Или какие есть варианты?  
    


Ответы

Ответ 1



Мой ответ (по крайней мере пока что) не будет претендовать на ответ, в нужном нам смысле слова, но оставлю некоторые заметки, чтобы не пропали, а кто-то мб пойдет в изучении дальше. Во-первых, как мы понимаем, государство регулирует рынок кинопроката. Картины допускаются к прокату с разрешения каких-то там комиссий, и соответственно отслеживается что и где показывается. Поэтому, если думаешь, что государство что-то мониторит - гугли "единая государственная система" и добавляй предмет поиска, в нашем случае "кино". Дальше переходим на сайт "Электронный кинобилет" (видимо какая то первая версия на скорую руку). Тут нам без логина и пароля делать почти нечего. Однако мы видим, красным шрифтом, что имеется Программа ручного формирования XML- файлов с информацией о продаже кинобилетов CreateXML. А также в наличие ссылка на скачивание программы. В архиве есть руководство пользователя. Из руководства видно, что программа написана в delphi (вроде 2009). Введение гласит: Предназначена программа для кинотеатров и сетей кинотеатров, не имеющих автоматизированных систем продажи билетов, небольших прокатных организаций, сельских кинотеатров, кинопередвижек и др. Отсюда 2 вывода: Есть автоматизированные системы продажи билетов, которые напрямую самостоятельно отправляют данные по продажам в ГИВЦ (главный информационно вычислительный центр) МинКультуры. Те, кто не может себе позволить такую роскошь, обязаны заполнять данные вручную с помощью указанной CreateXML. Руководство пользователя писал, очевидно, весьма одаренный человек, иначе я не могу объяснить, как вообще может в голову прийти написать следующий текст к диалогу для указания URL-загрузки файлов (и почему вообще этот URL можно настраивать тоже не ясно): В данной закладке задаются адреса, по которым осуществляется от-правка XML-файлов. Эти адреса заданы по умолчанию и не рекомендуется с ними экспериментировать. Далее, зная название системы можно заглянуть в википедию, где в копилку знаний можно добавить следующее: В России отслеживанием кассовых сборов занимается Фонд Кино. Отраслевая аналитика строится на основе данных Единой Федеральной автоматизированной информационной системы сведений о показах фильмов в кинозалах (ЕАИС). Переходим на сайт ЕАИС. Можем посмотреть ютуб-ролик. Видим, что есть приложение, следовательно, скорее всего есть API, публичный или закрытый - не ясно. Упоминаний про API на сайте не видно. Разработчики сайта скорее всего могут дать больше информации по данному вопросу. На странице Статистика-Прокат можно какие-то отчеты выгружать в Excel. Который в принципе уже поддается обработке, как и парсинг самой страницы. Следующий этап - узнать про API, тут уж либо гуглить, либо посмотреть, что там мобильное приложение и куда шлет, или сайт куда ajax-запрашивает. Но это за пределами моего сегодняшнего гугло-сеанса. Передаю эстафету.

Ответ 2



В развитии кинопроката участвуют все кому эта сфера не безразлична. Особенно она не безразлична кинодистрибьюторам. 90% этого рынка в России занимают пять компаний. Они, к примеру, по роду своей деятельности заинтересованы в расширении аудитории кинозрителей. Для этого им необходимо заключить договора как отдельно с кинотеатром, так и их сетями, при этом в максимальном количестве населенных пунктов. Кинотеатрам, в свою очередь, с ними. Постепенно накапливается автоматизированная база данных, с которой нужно грамотно работать, а именно продвигать свою продукцию (кино) через рекламу, ТВ и интернет-ресурсы. И чем шире охват аудитории, качественнее контент , к примеру у Кинопоиска Яндекса, тем эффективней происходит обмен данными между таким ресурсом и дистрибьютором вплоть до свободных мест в отдельно взятом кинотеатре какого-нибудь города в определенный момент времени.

Ответ 3



Когда сам владел сайтом с городской киноафишей - кинотеатры сами присылали каждый четверг обновлённую информацию о расписании. Сперва нужно было совершить один звонок в кинотеатр, а после этого email добавлялся в общую рассылку и я получал актуальную инфу. Может и есть какой-то сервис с открытым/закрытым API, но скорее всего база его база заполняется вручную.

вторник, 31 декабря 2019 г.

Уменьшение количества бойлерплейт кода в webapi-приложении

#c_sharp #api #архитектура #aspnet_web_api


Пишу клиент и сервер для проекта. Суть проекта: складской учет, возможно в будущем
будет расширяться с добавлением бухгалтерского. То есть склады, документы прихода,
расхода, перемещения, зарплата.

Имеется схема: C# desktop - WEB API Asp.Net Core - Database MySQL

Клиент посылает запрос на сервер, сервер обращается к БД и возвращается JSON. Через
HttpClient.

Схема MVC.
Пока протестировал на одной табличке. В контроллере прописал API(для примера):

    // GET: api/Products
    [HttpGet]
    public async Task>> GetProduct()
    {
        return await _context.Product.ToListAsync();
    }

    // GET: api/Products/5
    [HttpGet("{id}")]
    public async Task> GetProduct(int id)
    {
        var product = await _context.Product.FindAsync(id);

        if (product == null)
        {
            return NotFound();
        }

        return product;
    }



Подскажите, когда табличек будет много, и вариантов запросов будет
много, то необходимо каждый запрос вот так вот прописывать(по типу
того что в коде)?
Есть ли какое-то более оптимальное решение и все ли я делаю
правильно?

То есть в будущем я планирую для каждой таблицы создать контроллер и в нем прописывать
api запросы для сервера.

    


Ответы

Ответ 1



Вы делаете всё правильно, но для типовых операций придумали такую вещь, как скафолдинг (scaffolding). Настраивается это следующим образом. В папке C:\Users\{username}\.nuget\packages\microsoft.visualstudio.web.codegenerators.mvc\2.2.2\Templates (у вас могут быть и папки 2.2.1 и прочие) находятся шаблоны предоставляемые майкрософт по умолчанию вместе со студией, например: @inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @{string modelName = (Model.ClassName.EndsWith("Controller") ? Model.ClassName.Substring(0, Model.ClassName.Length - 10) : Model.ClassName);} namespace @Model.NamespaceName { [Route("api/[controller]")] [ApiController] public class @Model.ClassName : ControllerBase { // GET: api/@modelName [HttpGet] public IEnumerable Get() { return new string[] { "value1", "value2" }; } // GET: api/@modelName/5 [HttpGet("{id}", Name = "Get")] public string Get(int id) { return "value"; } // POST: api/@modelName [HttpPost] public void Post([FromBody] string value) { } // PUT: api/@modelName/5 [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } // DELETE: api/ApiWithActions/5 [HttpDelete("{id}")] public void Delete(int id) { } } } Рекомендую не трогать шаблоны по умолчанию, а скопировать папку Templates в ваш проект и уже там отредактировать так, как вам необходимо: Ссылки по теме: Scaffold: beginners guide Metanit: Scaffolding в Visual Studio Custom scaffold templates in ASP.NET Core

Ответ 2



Почти наверняка большинство таблиц будет требовать одинаковых алгоритмов: получать энтити по ключу, получать их список по некоторому признаку с разбиением по страницам и т.п. Значит, этот код нужно как-то абстрагировать от типа таблицы. Вы можете абстрагировать DAL (унести DbContext и получение данных из контроллера в другое место). Какие-то базовые операции вынести в обобщенный код (например, базовый репозиторий, который умеет простой CRUD для любой таблицы), специфичные части запроса делать в наследниках, строя запрос по частям с помощью IQueriable. Завести сущность фильтра и передать ее в методы репозитория, где на основе его уже будет строиться запрос. Аналогичным образом можно и интерфейсный слой (контроллеры) сделать обобщенными, с вкраплением кастомной логики.

Ответ 3



Подскажите, когда табличек будет много, и вариантов запросов будет много, то необходимо каждый запрос вот так вот прописывать(по типу того что в коде)? Смотря сколько параметров в запросе. Если их мало, проще прописывать каждый по отдельности. Если их будет много, лучше использовать DTO (Data Transfer Object) и структурировать параметры в нём. То есть в будущем я планирую для каждой таблицы создать контроллер и в нем прописывать api запросы для сервера. Если у Вас на клиенте предусмотрено изменение всех таблиц, то Вам возможно придётся поступить именно так. И то, в зависимости от ситуации можно построить общую точку редактирования нескольких таблиц. А, вообще такой подход чаще всего не оправдан. Хотя бы потому, что не все таблицы нужно редактировать. например, таблицы, которые хранят перечисления. Правда, если совсем по-хорошему, следует ориентироваться на модель предметной области и уже исходя из неё проектировать систему.

Зачем нужен API Key

#php #javascript #алгоритм #api


Те сервисы, у которых есть открытое API, обязывают регистрироваться, чтобы получить
специальный ключ... то есть API Key.. и этот ключ нужно передавать при каждом запросе:
site.ru/{api_key}/{method}/...

Не понятно, почему они так делают, если я могу, к примеру, взять у кого-нибудь этот
ключ, не регистрируясь...

так для чего того нужен этот API Key? в чем смысл?
    


Ответы

Ответ 1



API Key используется как CSRF Token - дабы посылать далеко сразу и без разговоров тех, у кого его нет. Если получил и творишь фигню - можно было бы отозвать. Т. е. средство быстрой модерации. Так же ключи могут выполнять функции Access Token - таким образом, кто-то (сервис, пользователь) может предоставить доступ до тех ресурсов, к которым доступ запрещён (личные сообщения, приватная информация и т. д.), не делая последние публичными. Брать ключ у кого-то, конечно, можно, вот только ответственность за действия будет нести именно владелец ключа (в некоторых сервисах подобное прямо оговаривают). Если действия не владельца ключа будут деструктивными, администрация удалит владельца (а так же, возможно, отзовёт какие-то оплаченные платные услуги, собранная аудитория под старым ключём). Скомпрометированный ключ иногда дают пересоздать (SE API), иногда - только с помощью тех. поддержки.

Ответ 2



Обычно API key используется для идентификации сервисом клиента. И по этому ключу сервис определяет какие данные можно отдавать определенному клиенту. Обычно еще используется авторизация и выдается токен, который нужно передоверять при каждом запросе к серверу после авторизации. Владельцы сервиса наверняка не предполагают что вам кто то будет давать свой api_key)

четверг, 26 декабря 2019 г.

Разработка json restful api на php

#php #веб_программирование #aspnet #api #rest


Как подойти к созданию JSON RESTful API на PHP, если делать все с нуля. Стоит ли
использовать реализации MVC? 

Иначе, какие фреймворки порекомендуете?


Slim .... 
Zend framework
Laravel
Silex
Phalcon
и.т.д


Стоит ли использовать
 ASP.NET Web API? 

Примерная рабочая Схема (не очень хорошая, как пишут):

1.WebServer(хостинг)

WebServer(хостинг) - index.php/users/
                           userInfo.php
                           createAccount.php
                           /..../


WebServer - на нём располагаются файлы,  распределенные по директориям, и обращаясь
по uri к какому-ту файлу, получим ответ, в формате JSON.

2.Получение данных от WebServer:

Если нам нужны какие-то данные, посылаем запрос , допустим GET в формате JSON, на
определённое uri , допустим www.site.com/users/userInfo.php?infoALL=RIKO.  В ответ,
мы получаем данные в формате JSON из MySQL СУБД. 

ВОПРОС как это правильно сделать? 
По вышеописанной схеме, конечно, все будет работать, но этот вариант слишком примитивен,
и в дальнейшем при его использовании, обязательно будут проблемы. 
    


Ответы

Ответ 1



На данный момент я пишу API для себя. FrontEnd которого состоит с обработки JSON, а BackEnd - на РНР. Для бэкенда я использую CakePHP 3.x. Пути можно проставить кастомно. А можно использовать стандартные. К примеру: У Вас есть контроллер API, в котором есть метод login. То отправить запрос на данный контроллер к методу login можно по следующему адресу : $.post('/api/login', data, function (json) { // Обработка ответа }) На счёт использования MVC - да, и только. Вы не пожалеете. Не стоит писать 100500 своих связок или писать свой фреймворк. Используйте готовые. Учите и применяйте знания на действии.

Ответ 2



PHP или ASP использовать зависит от платформы где все будет разворачиваться дабы потом не мучать веб сервер настройками поддержки того или иного языка. Если платформу можно выбрать любую, то при выборе между PHP или ASP, выберите то с чем вы лучше знакомы и умеете работать. Разницы в реализации ваших задач на PHP или ASP не будет никакой, потому как оба языка прекрасно реализуют API. Тут важно учесть что будет дальше, PHP и ASP имеют свои особенности и плюсы. Подумайте кто потом будет поддерживать это? Есть ли специфичный функционал? На каком языке больше готовых решений или примеров? MVC ? Фреймворки ? Вы серьезно ? У вас View одно и тоже - JSON данные. И вообще, если задача только вынимать и отдавать данные из SQL, нужны ли фреймворки или городить огород MVC стиля? Задумайтесь лучше над вопросом REST или SOAP. Спросите меня, если не понимаете о чем я

воскресенье, 22 декабря 2019 г.

Программно узнать вживую о забитом голе на чемпионате мира по футболу

#http #api #любой_язык #live


Слово api или developer не видать на https://www.fifa.com/worldcup/

Подсматривая сетевые запросы браузера можно найти ссылку, которая обновления получает
как json:

https://api.fifa.com/api/v1/live/football/recent/17/254645?language=ru-RU

В принципе можно с помощью периодического опроса (polling) недавнюю информацию получить
отсюда.                

В комментариях к похожему вопросу UEFA/FIFA scores API, можно найти ссылку на информацию
обо всех матчах сразу:

https://api.fifa.com/api/v1/calendar/matches?idseason=254645&idcompetition=17&count=100

Эту ссылку также можно подсмотреть в инструментах разработчика в сетевых запросах
браузера, находясь на fifa.com

Поисковики ещё подсказывают https://worldcup.sfg.io — сайт, который как json похожую
информацию возвращает соскребая её с сайта fifa. Есть и другие сайты.

Идеально хотелось бы найти API в открытом доступе, которое бы позволило подписаться
на интересующие меня события, без необходимости постоянного опроса, чтобы свежую информацию
получить. 
Пример такого API это сайт https://pubsubhubbub.appspot.com/, который позволяет подписаться
на push-уведомления. См. Как узнать что вышло новое видео на youtube-канале и как получить
его url?
    


Ответы

Ответ 1



API с push-уведомлениями не нашёл. В качестве компромисса, написал скрипт, который спит до начала матча, а затем каждые полминуты делает запрос по live ccылке, приведённой выше и сравнивает полученную информацию с уже сохранённой, чтобы узнать начался ли новый матч, забит ли новый гол. Вот код утилиты на Питоне, отсылающей телеграмму вначале матча/при забитии гола.

суббота, 21 декабря 2019 г.

Как правильно организовать REST-клиент ? Асинхронная загрузка JSON, изображений в базу и отображении в списке

#android #json #listview #api #rest


Хочу разобраться разобраться как правильно организовать REST-клиент.

Есть API, которая по запросу выдает JSON объекты. 

Нужно построить список ListView(или лучше использовать RecyclerView?) из этих JSON.

Как я это все делаю:

public class MainActivity extends AppCompatActivity {

    private static final String API = "https://api.github.com";

    ListView listView;
    UserAdapter adapter;
    DBHandler mDBhandler;


    int lastUserIDinList=0;
    //начальное кол-во пользователей в списке
    int initialAmountUsers=10;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mDBhandler = new DBHandler(this);

        listView = (ListView) findViewById(R.id.listview);

        adapter = new UserAdapter(MainActivity.this, new ArrayList());
        listView.setAdapter(adapter);

        new CountUserInDB().execute();

        listView.setOnScrollListener(new EndlessScrollListener() {
            @Override
            public boolean onLoadMore(int page, int totalItemsCount) {
                loadUsers(1);
                return true;
            }
        });


    }


    //получаем от API объекты, ждем коллбэк и запускаем AsyncTask запись в базу(в
качестве параметра передаем список моделей)
    void loadUsers(int count){
        RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(API).build();
        GitAPI git = restAdapter.create(GitAPI.class);
        //полчаем от АПИ пользователей с ид больше lastUserIDinList. количество пользователей
в ответе - count
        git.getNextUsers(lastUserIDinList, count, new Callback>() {
            @Override
            public void success(ArrayList gitHubUsers, Response response) {
                new WriteToDB().execute(gitHubUsers);
            }

            @Override
            public void failure(RetrofitError error) {
                Toast.makeText(getApplicationContext(), error.getMessage(),
                        Toast.LENGTH_LONG).show();
            }
        });
    }



    //get arralist of users and write it to db
    class WriteToDB extends AsyncTask,Void,Integer> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Integer doInBackground(ArrayList... params) {
            //заноси в таблицу всех пользователей полученных в коллбэке
            for(int i=0; i> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected ArrayList doInBackground(Integer... params) {
            //читаем из базы всех пользователей, ид которых >= params[0]
            return mDBhandler.getUserFromID(params[0]);
        }

        @Override
        protected void onPostExecute(ArrayList userList) {
            super.onPostExecute(userList);
            new DisplayUserInListView().execute(userList);
        }
    }




    //get arralist of users and add it to adapter
    class DisplayUserInListView extends AsyncTask,Void,Void> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(ArrayList... params) {
            //добавляем по очереди пользователей в адаптер
            for(int i=0; i {

        int lastUserIDinTable=-1;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }


        @Override
        protected Integer doInBackground(Void... params) {
            //получаем ИД последней записи в таблице
            lastUserIDinTable=mDBhandler.getLastUseID();
            //получаем количество пользователей в таблице
            return mDBhandler.getUserCount();
        }


        @Override
        protected void onPostExecute(Integer userCount) {
            super.onPostExecute(userCount);

            if(userCount==0){
                //база пуста, загрузим 10 пользователей для начала
                loadUsers(10);
            }

            if(userCount>=initialAmountUsers){
                //в базе больше 10 пользователей, что достаточно для начала - читаем
из базы.
                new ReadFromDB().execute(0);
            }

            if(userCount>0 && userCount


Ответы

Ответ 1



Многократно уже отписывался на эту тему, как писать REST клиента. Не поленюсь написать еще раз: Через сервис тягаем JSON объекты из сети и складываем в SQLite БД Над SQLite БД создаем ContentProvider и через CursorLoader организуем подпитку CursorAdapter, который и будет адаптером для ListView или RecyclerView Вопрос выбора ListView или RecyclerView - это вопрос не только вкуса, но и квалификации (все таки). Как показывает опыт RecyclerView сложнее в понимании, но дает гораздо большую свободы в плане управления списком. Я бы рекомендовал начинать с ListView и только потом приступать к RecyclerView Для работы с JSon лучшим инструментом является Google Gson - берите его и даже не сомневайтесь. Функция Gson очень простая, но важная: это трансляция вашего Java объекта в Json строку и обратно. По сути вместо рутинного парсинга json строки вы будете иметь дело с интеллигентными Java классами. Часто при работе с Rest требуется реализация функциональности Pull-To-Refresh - ну то есть обновление списка при вытягивании вверх/вниз. В сети есть огромное количество реализаций Pull-To-Refresh. Я пользовался этим проектом - но он сейчас мертвый. Недавно в суппорт либах появился новый виджет: SwipeRefreshLayout я лично не пользовался не знаю как с ним работать. Те из знакомых кто пользовались - пищат от восторга. У меня нет оснований не доверять им. P.S. Для RecyclerView организовать CursorAdapter напрямую не получится, нужно будет применить эту наработку. Update Создавать сервис, в котором будут загружаться данные с API(), парситься, записываться в базу и потом передавать в активити сообщение, что надо обновить список. Активити как получит такое уведомление, с помощью CursorLoader'а грузит данные из таблицы и добавляет их адаптеру списка. В качестве сервиса подойдет IntentService, который можно запускать по таймеру и/или через событие генерируемое при Pull-To-Refresh Не надо никак уведомлять Activity о том, что БД изменилось - CursorLoader сам будет автоматом подгружать обновленные данные при изменении БД через соответствующий Observer (в случае RecyclerView), а в случае CursorAdapter это уже будет "из коробки"