Я пытаюсь программно использовать службу запросов 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
[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)
Я интерпретирую это как то, что вы ищете способ сделать OAuth только для сайта WikiMedia (используя v1), остальная часть вашего кода на самом деле не является частью вопроса? Поправьте меня если я ошибаюсь.
Вы не указываете, какое приложение вы разрабатываете, существуют разные способы аутентификации на страницах Викимедиа с использованием OAuth, для веб-приложений, использующих Flask или Django с правильной внутренней поддержкой.
Более «общий» способ — использовать библиотеку mwoauth
(python-mwoauth) из любого приложения. Он по-прежнему поддерживается как в Python 3, так и в Python 2.
Я предполагаю следующее:
Использование 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,
Да я вижу. Никогда не использовал это, но это интересная задача, чтобы получить некоторый опыт. Тогда прошу прощения за то, что не углубился в вопрос. Возможно, укажите, что это обязательное требование, так вы, скорее всего, получите правильный ответ.
Опять же, если я прочитаю заголовок фактического вопроса, это будет довольно очевидно. Виноват.
Почему бы вам не попробовать и посмотреть, сможете ли вы получить ответ на запрос SPARQL «вручную», используя requests
+ OAuth и т. д., и тогда, если вы сможете, вы узнаете, что у нас есть ошибка в SPARQLWrapper. в отличие от проблемы в коде вашего приложения.
Код requests
должен выглядеть примерно так: + материал OAuth:
r = requests.get(
ENDPOINT,
params = {"query": QUERY},
auth=auth,
headers = {"Accept": "application/sparql-results+json"}
)
Ник
Хороший совет - спасибо. Я постараюсь, как только у меня будет возможность, и вернусь с результатами.
Это не сработало, поэтому я полагаю, что это проблема со службой запросов Викисклада. Спасибо, что помогли мне сузить круг!
Я бы попробовал запустить ваш код, используя другую конечную точку. Вместо 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/…
Ах да, я вижу, что вы собираетесь сейчас. Я быстро попробовал подключить приложение к новой учетной записи, которую я создал на en.wikipedia.beta.wmflabs.org/wiki, чтобы настроить необходимые учетные данные, но не смог найти способ сделать это. так, как я мог ранее на небета-сайте. Удалось ли вам пройти этот этап, чтобы получить свои полномочия? (ключ_потребителя, секрет_потребителя, токен_доступа, секрет_доступа).
Хорошо, я думаю, вы нашли решение. Я получил учетную запись не на той вики! Я приму ваш ответ сейчас, но, пожалуйста, включите эту информацию о том, где вам нужно зарегистрироваться. ETA: еще не тестировался, но спешу дать вам награду до истечения срока ее действия. ETA: Чтобы уточнить, я пытался использовать токены авторизации, которые я создал на не бета-версии Викисклада.
Неа. Не работает! Хотя я был уверен, что это поможет. Токен только для владельца из meta.wikimedia.beta.wmflabs.org/wiki/… у меня тоже не работает.
Ах извините за это! Я вернулся и попробовал несколько разных подходов к запуску этого запроса программно...
Когда вы запускаете запрос в браузере, он работает, но любая попытка попасть в конечную точку wcqs-beta.wmflabs.org/sparql терпит неудачу. В браузере, если вы нажмете </> Code
в правом нижнем углу окна запроса, появится всплывающее окно, отображающее код для программного запуска запроса на разных языках. Больше всего информации, которую я мог получить при попытке некоторых из них, — это запуск кода Pywikibot — я получаю сообщение об ошибке, в котором говорится, что сайт не предоставляет конечную точку sparql. Так что потенциально это может быть причиной того, что все наши попытки терпят неудачу.
Это кажется странным, поскольку конечную точку можно использовать из браузера... Я буду продолжать изучать ее.
Эта кодовая кнопка - хорошая находка. Учитывая, что это дает такой же нерабочий код примера, я думаю, мы можем начать делать вывод, что проблема, вероятно, заключается в том, что OAuth не работает с конечной точкой, как указано в документации, а работает только тогда, когда файл cookie сеанса подарок. Я постараюсь разобраться с этим, чтобы зарегистрировать либо ошибку документации, либо ошибку реализации.
Да, я думаю, что это безопасный вывод, чтобы сделать. Было бы здорово услышать, каков будет результат, если вам удастся догнать! Я тоже буду следить за решением и буду держать вас в курсе, если что-то появится.
Отказ от ответственности: я являюсь одним из авторов 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.
Группа Викиданных в Telegram: t.me/joinchat/IeCRo0j5Uag1qR4Tk8Ftsg