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

#python #oauth #sparql #wikidata #rdflib

#python #oauth #sparql #rdflib #wikidata-query-service

Вопрос:

Я пытаюсь использовать службу запросов Викисклада [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, но просто используя requests requests_ouathlib. Однако я сталкиваюсь с той же проблемой — возвращается HTML для страницы входа в систему — так что, похоже, это действительно может быть проблема со службой запросов Викисклада.

 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)
 

Комментарии:

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

Ответ №1:

Отказ от ответственности: Я один из авторов 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.

Ответ №2:

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

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

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

Ник

Комментарии:

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

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

Ответ №3:

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

Я интерпретирую это как то, что вы ищете способ выполнить OAuth только для сайта ВикиМедиа (используя 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 :)

 

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

GitHub:

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

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

Комментарии:

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

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

3. Опять же, если я прочитаю фактический заголовок вопроса, это довольно очевидно. Мой плохой.

Ответ №4:

Я бы попробовал запустить ваш код, используя другую конечную точку. Вместо 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()
 

Комментарии:

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

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

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

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

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