Как пройти аутентификацию в службе запросов Викисклада с помощью OAuth в Python?

Я пытаюсь программно использовать службу запросов Wikimedia Commons [1] с помощью Python, но у меня возникают проблемы с аутентификацией через OAuth 1.

Ниже приведен автономный пример Python, который не работает должным образом. Ожидаемое поведение заключается в том, что возвращается набор результатов, но вместо этого возвращается HTML-ответ страницы входа. Вы можете получить зависимости с помощью pip install --user sparqlwrapper oauthlib certifi. Затем сценарию должен быть предоставлен путь к текстовому файлу, содержащему вставленный вывод, полученный после подачи заявки на токен только владельца [2]. например

Consumer token
    deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
Consumer secret
    deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
Access token
    deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
Access secret
    deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef

[1] https://wcqs-beta.wmflabs.org/ ; https://diff.wikimedia.org/2020/10/29/sparql-in-the-shadow-of-structured-data-on-commons/

[2] https://www.mediawiki.org/wiki/OAuth/Owner-only_consumers

import sys
from SPARQLWrapper import JSON, SPARQLWrapper
import certifi
from SPARQLWrapper import Wrapper
from functools import partial
from oauthlib.oauth1 import Client
 
 
ENDPOINT = "https://wcqs-beta.wmflabs.org/sparql"
QUERY = """
SELECT ?file WHERE {
  ?file wdt:P180 wd:Q42 .
}
"""
 
 
def monkeypatch_sparqlwrapper():
    # Deal with old system certificates
    if not hasattr(Wrapper.urlopener, "monkeypatched"):
        Wrapper.urlopener = partial(Wrapper.urlopener, cafile=certifi.where())
        setattr(Wrapper.urlopener, "monkeypatched", True)
 
 
def oauth_client(auth_file):
    # Read credential from file
    creds = []
    for idx, line in enumerate(auth_file):
        if idx % 2 == 0:
            continue
        creds.append(line.strip())
    return Client(*creds)
 
 
class OAuth1SPARQLWrapper(SPARQLWrapper):
    # OAuth sign SPARQL requests

    def __init__(self, *args, **kwargs):
        self.client = kwargs.pop("client")
        super().__init__(*args, **kwargs)
 
    def _createRequest(self):
        request = super()._createRequest()
        uri = request.get_full_url()
        method = request.get_method()
        body = request.data
        headers = request.headers
        new_uri, new_headers, new_body = self.client.sign(uri, method, body, headers)
        request.full_url = new_uri
        request.headers = new_headers
        request.data = new_body
        print("Sending request")
        print("Url", request.full_url)
        print("Headers", request.headers)
        print("Data", request.data)
        return request
 
 
monkeypatch_sparqlwrapper()
client = oauth_client(open(sys.argv[1]))
sparql = OAuth1SPARQLWrapper(ENDPOINT, client=client)
sparql.setQuery(QUERY)
sparql.setReturnFormat(JSON)
results = sparql.query().convert()
 
print("Results")
print(results)

Я также пробовал без SPARQLWrapper, а просто использовал запросы + запросы_ouathlib. Однако у меня возникает та же проблема --- возвращается HTML для страницы входа --- так что, похоже, это может быть проблема со службой запросов Wikimedia Commons.

import sys
import requests
from requests_oauthlib import OAuth1


def oauth_client(auth_file):
    creds = []
    for idx, line in enumerate(auth_file):
        if idx % 2 == 0:
            continue
        creds.append(line.strip())
    return OAuth1(*creds)


ENDPOINT = "https://wcqs-beta.wmflabs.org/sparql"
QUERY = """
SELECT ?file WHERE {
  ?file wdt:P180 wd:Q42 .
}
"""


r = requests.get(
    ENDPOINT,
    params = {"query": QUERY},
    auth=oauth_client(open(sys.argv[1])),
    headers = {"Accept": "application/sparql-results+json"}
)


print(r.text)

Группа Викиданных в Telegram: t.me/joinchat/IeCRo0j5Uag1qR4Tk8Ftsg

Stanislav Kralin 17.12.2020 12:24
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
1
391
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Если вы запрашиваете аутентификацию MediaWiki OAuth v1

Я интерпретирую это как то, что вы ищете способ сделать OAuth только для сайта WikiMedia (используя v1), остальная часть вашего кода на самом деле не является частью вопроса? Поправьте меня если я ошибаюсь.

Вы не указываете, какое приложение вы разрабатываете, существуют разные способы аутентификации на страницах Викимедиа с использованием OAuth, для веб-приложений, использующих Flask или Django с правильной внутренней поддержкой.

Более «общий» способ — использовать библиотеку mwoauth (python-mwoauth) из любого приложения. Он по-прежнему поддерживается как в Python 3, так и в Python 2.

Я предполагаю следующее:

  • На целевом сервере установлена ​​установка MediaWiki с установленным расширением OAuth.
  • Вы хотите выполнить рукопожатие OAuth с этим сервером для аутентификации.

Использование Wikipedia.org в качестве примера целевой платформы:

$ pip install mwoauth

# Find a suitable place, depending on your app to include the authorization code:

from mwoauth import ConsumerToken, Handshaker
from six.moves import input # For compatibility between python 2 and 3

# Construct a "consumer" from the key/secret provided by the MediaWiki site
import config
consumer_token = ConsumerToken(config.consumer_key, config.consumer_secret)

# Construct handshaker with wiki URI and consumer
handshaker = Handshaker("https://en.wikipedia.org/w/index.php",
                        consumer_token)

# Step 1: Initialize -- ask MediaWiki for a temporary key/secret for user
redirect, request_token = handshaker.initiate()

# Step 2: Authorize -- send user to MediaWiki to confirm authorization
print("Point your browser to: %s" % redirect) #
response_qs = input("Response query string: ")

# Step 3: Complete -- obtain authorized key/secret for "resource owner"
access_token = handshaker.complete(request_token, response_qs)
print(str(access_token))

# Step 4: Identify -- (optional) get identifying information about the user
identity = handshaker.identify(access_token)
print("Identified as {username}.".format(**identity))

# Fill in the other stuff :)

Возможно, я неправильно истолковал ваш вопрос, если это так, пожалуйста, кричите мне через левое ухо.

Гитхаб:

Используй Источник, Люк

Вот ссылка на документы, включая пример использования Flask: WikiMedia OAuth — Python

Это связано конкретно со службой запросов Wikimedia Commons, поэтому мне нужно использовать клиент SPARQL. Он похож на конечную точку Wikidata SPARQL, но имеет другой набор доступных данных. Смотрите ссылки в вопросе. Поскольку это всего лишь сценарий для личного использования, я использую токен только для владельца, т. е. взаимодействие с пользователем не требуется, но токен может работать только с одной учетной записью. По этой причине не нужно задействовать веб-фреймворки. Похоже, что mwoauth — это довольно тонкая оболочка вокруг oauthlib. Я использую oauthlib direct, так как мне нужно интегрировать его со SPARQLWrapper,

Frankie Robertson 18.12.2020 08:40

Да я вижу. Никогда не использовал это, но это интересная задача, чтобы получить некоторый опыт. Тогда прошу прощения за то, что не углубился в вопрос. Возможно, укажите, что это обязательное требование, так вы, скорее всего, получите правильный ответ.

C. Sederqvist 18.12.2020 14:51

Опять же, если я прочитаю заголовок фактического вопроса, это будет довольно очевидно. Виноват.

C. Sederqvist 18.12.2020 14:54

Почему бы вам не попробовать и посмотреть, сможете ли вы получить ответ на запрос SPARQL «вручную», используя requests + OAuth и т. д., и тогда, если вы сможете, вы узнаете, что у нас есть ошибка в SPARQLWrapper. в отличие от проблемы в коде вашего приложения.

Код requests должен выглядеть примерно так: + материал OAuth:


r = requests.get(
    ENDPOINT,
    params = {"query": QUERY},
    auth=auth,
    headers = {"Accept": "application/sparql-results+json"}
)

Ник

Хороший совет - спасибо. Я постараюсь, как только у меня будет возможность, и вернусь с результатами.

Frankie Robertson 23.12.2020 08:25

Это не сработало, поэтому я полагаю, что это проблема со службой запросов Викисклада. Спасибо, что помогли мне сузить круг!

Frankie Robertson 23.12.2020 15:07

Я бы попробовал запустить ваш код, используя другую конечную точку. Вместо https://wcqs-beta.wmflabs.org/sparql попробуйте использовать https://query.wikidata.org/sparql. Когда я использую первую конечную точку, я также получаю HTML-ответ страницы входа, которую вы получали, однако, когда я использую вторую, я получаю правильный ответ:

from SPARQLWrapper import SPARQLWrapper, JSON

endpoint = "https://query.wikidata.org/sparql"
sparql = SPARQLWrapper(endpoint)

# Example query to return a list of movies that Christian Bale has acted in:
query = """
SELECT ?film ?filmLabel (MAX(?pubDate) as ?latest_pubdate) WHERE {
   ?film wdt:P31 wd:Q11424 .
   ?film wdt:P577 ?pubDate .
   ?film wdt:P161 wd:Q45772 .
  SERVICE wikibase:label {
    bd:serviceParam wikibase:language "en" .
  }
 }
GROUP BY ?film ?filmLabel
ORDER BY DESC(?latest_pubdate)
LIMIT 50
"""

sparql.setQuery(query)
sparql.setReturnFormat(JSON)
results = sparql.query().convert()

# Define a quick function to get json into pandas dataframe:
import pandas as pd
from pandas import json_normalize

def df_from_res(j):
    df = json_normalize(j['results']['bindings'])[['filmLabel.value','latest_pubdate.value']]
    df['latest_pubdate.value'] = pd.to_datetime(df['latest_pubdate.value']).dt.date
    return df

df_from_res(results).head(5)


#   filmLabel.value   latest_pubdate.value
# 0 Ford v Ferrari    2019-11-15
# 1 Vice              2019-02-21
# 2 Hostiles          2018-05-31
# 3 The Promise       2017-08-17
# 4 Song to Song      2017-05-25

И эта конечная точка также работает с библиотекой requests аналогичным образом:

import requests

payload = {'query': query, 'format': 'json'}

results = requests.get(endpoint, params=payload).json()

Спасибо за рекомендацию, но эта конечная точка имеет другую доступную информацию. В частности, у него нет всех доступных структурированных данных на Викискладе. См.: diff.wikimedia.org/2020/10/29/…

Frankie Robertson 23.12.2020 15:00

Ах да, я вижу, что вы собираетесь сейчас. Я быстро попробовал подключить приложение к новой учетной записи, которую я создал на en.wikipedia.beta.wmflabs.org/wiki, чтобы настроить необходимые учетные данные, но не смог найти способ сделать это. так, как я мог ранее на небета-сайте. Удалось ли вам пройти этот этап, чтобы получить свои полномочия? (ключ_потребителя, секрет_потребителя, токен_доступа, секрет_доступа).

user6386471 23.12.2020 21:10

Хорошо, я думаю, вы нашли решение. Я получил учетную запись не на той вики! Я приму ваш ответ сейчас, но, пожалуйста, включите эту информацию о том, где вам нужно зарегистрироваться. ETA: еще не тестировался, но спешу дать вам награду до истечения срока ее действия. ETA: Чтобы уточнить, я пытался использовать токены авторизации, которые я создал на не бета-версии Викисклада.

Frankie Robertson 24.12.2020 10:08

Неа. Не работает! Хотя я был уверен, что это поможет. Токен только для владельца из meta.wikimedia.beta.wmflabs.org/wiki/… у меня тоже не работает.

Frankie Robertson 24.12.2020 10:25

Ах извините за это! Я вернулся и попробовал несколько разных подходов к запуску этого запроса программно...

user6386471 24.12.2020 14:09

Когда вы запускаете запрос в браузере, он работает, но любая попытка попасть в конечную точку wcqs-beta.wmflabs.org/sparql терпит неудачу. В браузере, если вы нажмете </> Code в правом нижнем углу окна запроса, появится всплывающее окно, отображающее код для программного запуска запроса на разных языках. Больше всего информации, которую я мог получить при попытке некоторых из них, — это запуск кода Pywikibot — я получаю сообщение об ошибке, в котором говорится, что сайт не предоставляет конечную точку sparql. Так что потенциально это может быть причиной того, что все наши попытки терпят неудачу.

user6386471 24.12.2020 14:18

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

user6386471 24.12.2020 14:38

Эта кодовая кнопка - хорошая находка. Учитывая, что это дает такой же нерабочий код примера, я думаю, мы можем начать делать вывод, что проблема, вероятно, заключается в том, что OAuth не работает с конечной точкой, как указано в документации, а работает только тогда, когда файл cookie сеанса подарок. Я постараюсь разобраться с этим, чтобы зарегистрировать либо ошибку документации, либо ошибку реализации.

Frankie Robertson 25.12.2020 09:10

Да, я думаю, что это безопасный вывод, чтобы сделать. Было бы здорово услышать, каков будет результат, если вам удастся догнать! Я тоже буду следить за решением и буду держать вас в курсе, если что-то появится.

user6386471 31.12.2020 00:27
Ответ принят как подходящий

Отказ от ответственности: я являюсь одним из авторов WCQS (и автором, видимо, немного вводящей в заблуждение статьи, ссылка на которую указана в вопросе).

Этот способ аутентификации используется для приложений, аутентифицируемых с помощью Викисклада (или любого другого приложения Викимедиа), но не с WCQS, который сам по себе является приложением, аутентифицированным с Викискладом. OAuth в этом случае используется исключительно веб-приложением для аутентификации пользователей, но в настоящее время вы не можете пройти аутентификацию с помощью OAuth для ботов и других приложений. Любой вид использования потребует входа пользователя.

Это ограничение связано с нашей текущей настройкой и инфраструктурой, и мы планируем преодолеть это, когда запустим производство (сервис в настоящее время выпущен в бета-версии). К сожалению, я не могу сказать, когда это произойдет, но для нас это важно.

Если вы хотите опробовать своего бота до того, как это произойдет, вы всегда можете войти в браузер и использовать токен в своем коде, но срок его действия обязательно истечет, и в какой-то момент процесс нужно будет повторить. Простая модификация вашего второго листинга делает свое дело:

import sys
import requests

ENDPOINT = "https://wcqs-beta.wmflabs.org/sparql"
QUERY = """
SELECT ?file WHERE {
  ?file wdt:P180 wd:Q42 .
}
"""

r = requests.get(
    ENDPOINT,
    params = {"query": QUERY},
    headers = {"Accept": "application/sparql-results+json", "wcqsSession": "<token retrieved after logging in"}
)


print(r.text)

Обратите внимание, что запрос в списке рассылки, непосредственно в irc (freenode:#wikimedia-discovery) или создание билета Phabricator — лучший способ получить помощь по WCQS.

Другие вопросы по теме