I am trying to write a simple free translator (QT widget) using the online translation service.
The idea is to send the standard get request to an online translator, and then parse the response.
But the reply does not contain the translated text ! I guess this is because the service uses AJAX.
In the example, I am using the google translator, but I get similar results with other translators (yandex, deepl).
I know that there is a way to use the shareware API, but since the project is not commercial at the moment, I do not want to register a bank card.
Is there a browser-like way to get translation without the API and use it for free ?
I have searched for any information, but to my surprise, it was outdated and irrelevant at the moment (since Google closed the free service).
And one more question. When I tried to cast the result to a QString (QString s = reply->readAll().toString() or QString s = reply->readAll().toStdString().c_str()), I got a distorted htlm code (a lot of NUL characters at the beginning of the file). I assume that this is due to a misinterpretation of the escape sequences, but how then to cast the result correctly ? Even in the current version, there is some garbage at the beginning of the file (NUL NUL Уi).
The code I use is:
void getTranslate() {
QNetworkAccessManager manager;
QUrl url("https://translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=Hello%2C%20World%20!");
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
do {
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} while(!reply->isFinished());
QFile html("out.html");
if (html.open(QIODevice::ReadWrite)) {
QDataStream out(&html);
out << reply->readAll();
}
reply->close();
delete reply;
}
looking at Google Translate it uses AJAX Request, to get the translation. You could try to change the URL to something like this (this is where the ajax request goes to):
https://translate.google.de/translate_a/single?client=webapp&sl=auto&tl=en&hl=de&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=sos&dt=ss&dt=t&dt=gt&otf=2&ssel=0&tsel=0&xid=45662847&kc=1&tk=656516.836633&q=dies%20ist%20ein%20test
This request returns JSON data, which should be easy to parse.
I am not sure what all the parameters are for, but maybe this information is helpful for you.
I belive your problems with screen-scraping approach may be that the translate application uses Ajax to call the server-side and retrieve the translation. The page you get when downloading using QNetworkRequest is merely the JS application, it doesn't actually contain the translation. That doesn't get filled in until after a call has been made from the page to the server.
And that is why it isn't working. Perhaps you could get it working somehow, so let us know how you do it :-)
Related
I'm having quite a bit of struggle in trying to achieve a simple PUT request in QT. Frankly I am still a starter using this framework so I have much left to learn.
What I am trying to do is make a PUT request with 2 parameters this Cloudflare Workers KV API.
With that being said this is my current code snippet:
QString szUrl = "";
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart valuePart;
valuePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"value\""));
valuePart.setBody(value.toByteArray());
QHttpPart metadataPart;
metadataPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"metadata\""));
metadataPart.setBody(metadata.toByteArray());
multiPart->append(valuePart);
multiPart->append(metadataPart);
QNetworkRequest request;
request.setUrl(szUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data");
request.setRawHeader("Authorization", QString("Bearer %1").arg("...").toUtf8());
QNetworkReply* reply = m_NetworkManager->put(request, multiPart);
// delete the multiPart with the reply
multiPart->setParent(reply);
// Process reply (QNetworkAccessManager::finished)
// Process errors (QNetworkAccessManager::sslErrors)
qDebug() << reply->error();
qDebug() << reply->readAll();
qDebug() << reply->errorString();
The above returns always HTTP 400 (bad request) with zero, and I mean ZERO response. I could not retrieve the error response from the API in any way. Just an empty string ("") under debug.
If I execute the example cURL cli code from the API page, it works perfectly. I get a response and it's successful.
My question would be, what on earth am I doing wrong? I read the documentation and wrote the code accordingly, I cannot understand what's happening.
I have searched quite a bit for a possible answer online but unfortunately all issues and examples are with POST requests and file uploads (mostly).
Please advise, it would be much appreciated.
I will answer my own question because one of the reasons the above was not working it's because of my own fault.
Problem 1:
Bad formatting of metadata. Looks like Cloudflare API wants the metadata "compressed".
Problem 2:
Unable to retrieve API error response. Now this is an interesting one because I found countless forum posts and posts even here with the same problem, yet nobody posted the actual solution.
The readAll() function states this issue:
This function has no way of reporting errors; returning an empty
QByteArray can mean either that no data was currently available for
reading, or that an error occurred. This function also has no way of
indicating that more data may have been available and couldn't be
read.
The resolution is to connect QIODevice::readyRead to as per the documentation:
connect(reply, &QIODevice::readyRead, this, [=]() {
QByteArray response = reply->readAll();
qDebug() << response;
});
You now have the server reply regardless of the HTTP error code.
With that being said 2 problems solved in one go, enjoy.
rest API:
someting/post expects 'token' as bytearray body data
something/delete expects 'token' as bytearray body data
Using Qt I can prepare the data in a QByteArray and send via deleteResource (that doesn't accepts a data parameter) and I can use sendCustomRequest that accepts a data parameter, but if I use the later with DELETE I have no data.
With POST, I do have the data.
Minimal code example, python server - just to exemplify. the Qt code is below.:
#route('/something/delete', "DELETE")
def somethingDelete(url, post):
print(post) # empty
#route('/something/delete2', "POST")
def somethingDelete2(url, post):
print(post) # correct output.
and the Qt code that triggers the server calls - This code is higly shortened to simplify, but the idea is that.
QNetworkRequest req;
req.setRawHeader("OCS-APIREQUEST", "true");
req.setUrl = Utility::concatUrlPath(account()->url(), path());
QByteArray bufferData("token=" + _token);
sendCustomRequest(req, "POST", bufferData);
as soon as I change the POST to DELETE, I don't get the token, but the correct python function is executed.
The DELETE HTTP verb does not have a request body so your buffer is probably simply dropped by Qt. To use DELETE you would need to encode your token in the URL.
As of Qt 5.9.2, it seems that Qt might ignore body data when performing a DELETE operation.
In Qt code in QNetworkReplyHttpImplPrivate::postRequest(), one can see that createUploadByteDevice() is not called when the operation is QNetworkAccessManager::DeleteOperation.
However, this is only valid when the DELETE request is sent by calling QNetworkAccessManager::deleteResource(), which is the only way to create a network request with the QNetworkAccessManager::DeleteOperation operation. Also note that this function does not allow you to send any body data.
If you use QNetworkAccessManager::sendCustomRequest() to send the request, then as far as Qt is concerned the operation is QNetworkAccessManager::CustomOperation. The custom verb you pass is not processed further, and Qt will behave exactly the same whatever the value of verb is. Even if verb is a known value like POST or DELETE.
This means that Qt does not discard the body data.
So if you used QNetworkAccessManager::sendCustomRequest(), as you claim, your body data is sent to the server (confirmed by Wireshark). So the issue is not on Qt side, but on the server side.
I'm trying to send a bit of json data to a web service using Qt 5.1.1. There are many examples out there, but almost all are for Qt 4.x, which had a slightly different API. Here's what I'm trying now:
QUrl url("http://...");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QByteArray jsonInPostFormat = "json=" + QUrl::toPercentEncoding(jsonAsString);
jsonInPostFormat.replace("%20","+");
reply = net->post(request, jsonInPostFormat);
connect(reply, SIGNAL(finished()), this, SLOT(finishedAddComment()));
The above has ... no effect. If I'm doing things correctly, I seem to not get any response from the server. (I call reply->readALL() in finishedAddComment().)
The service has a test form that works correctly. I've dumped the headers that it's sending and looked at the html, which is just a standard form with post method specified.
Am I doing something obviously wrong? Is there a better way, say, using QUrlQuery?
Some way of seeing the raw HTML requests that Qt is actually sending would be super-helpful...
Thanks!
Tyler
Qt5 no longer has the QUrl::encodedQuery() method. Not sure, but from the documentation it might work using QUrl::query() method instead.
Hope it helps.
I need to create a simple web service (being the "server"). The goal is to provide some data I do read in an Qt / C++ application as JSON data. Basically a JavaScript application in the browser shall read its data from the Qt app. It is usually a single user scenario, so the user runs a Google Maps application in her browser, while additional data come from the Qt application.
So far I have found these libs:
Qxt: http://libqxt.bitbucket.org/doc/0.6/index.html but being a newbie on C++/Qt I miss some examples. Added: I have found one example here
gSoap: http://www.cs.fsu.edu/~engelen/soap.html has more examples and documentation and also seems to support JSON
KD SOAP: http://www.kdab.com/kdab-products/kd-soap/ with no example as far as I can tell, docu is here
Qt features itself, but it is more about acting as a client: http://qt-project.org/videos/watch/qt-networking-web-services
Checking SO gives me basically links to the above libs
webservice with Qt with an example I do not really get.
How to Create a webservice by Qt
So basically I do have the following questions:
Which lib would you use? I want to keep it as simple as possible and would need an example.
Is there another (easy!) way to provide the JSON data to the JavaScript Web page besides the WebService?
-- Edit, remarks: ---
Needs to be application intrinsic. No web server can be installed, no extra run time can be used. The user just runs the app. Maybe the Qt WebKit could be an approach....
-- Edit 2 --
Currently checking the tiny web servers as of SO " Qt HTTP Server? "
As of my tests, currently I am using QtWebApp: http://stefanfrings.de/qtwebapp/index-en.html This is one of the answers of Edit 2 ( Qt HTTP Server? )
Stefan's small WebServer has some well documented code, is written in "Qt C++" and easy to use, especially if you have worked with servlets already. Since it can be easily integrated in my Qt project, I'll end up with an internal WebServer.
Some demo code from my JSON tests, showing that generating the JSON content is basically creating a QString.
void WebServiceController::service(HttpRequest& request, HttpResponse& response) {
// set some headers
response.setHeader("Content-Type", "application/json; charset=ISO-8859-1");
response.setCookie(HttpCookie("wsTest","CreateDummyPerson",600));
QString dp = WebServiceController::getDummyPerson();
QByteArray ba = dp.toLocal8Bit();
const char *baChar = ba.data();
response.write(ba);
}
If someone has easy examples with other libs to share, please let me know.
QByteArray ba = dp.toLocal8Bit();
const char *baChar = ba.data();
You don't need to convert the QByteArray to char array. Response.write() can also be called with a QByteArray.
By the way: qPrintable(dp) is a shortcut to convert from QString to char array.
I have found numerous examples on uploading a file to a web server via C++ on Windows.
However I am struggling after a lot of searching (and I thought I was good with Google!) to find any examples to help me achieve the same on a Mac.
Can anyone point me towards some help on how to upload a file to a web server on a Mac OS using either C++ or objective C?
It can be via either HTTP/HTTPS or FTP/FTPS (or both).
Thanks!
You could use 'libcurl', see this article on Wikipedia.
You would want to use an NSURLConnection with a NSMutableURLRequest, something like this:
NSMutableURLRequest *theRequest=[[NSMutableURLRequest alloc] init];
[theRequest addValue:#"attachment;filename=\"file2.gif\"" forHTTPHeaderField:#"Content-disposition"];
[theRequest addValue:#"image/gif" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue:#"binary" forHTTPHeaderField:#"Content-Transfer-Encoding"];
[theRequest setHttpBody:myBodyNSDataObject];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
receivedData=[[NSMutableData data] retain];
} else {
// inform the user that the download could not be made
}
You can modify or set headers using the NSMutableURLRequest method:
- (void)addValue:(NSString *)value forHTTPHeaderField:(NSString *)field
The response will be whatever the server comes back with. You can check out Apple's documentation for the rest of the delegate methods to implement to get the body of the response back. You should have the NSData object representing the content of the file you want to upload ready. Doing the same using FTP is a bit more involved, but this will work to post the file body up. You will want to make sure your NSData object is set up like the body of an HTTP post, such that you set up the headers like:
[theRequest addValue:#"attachment;filename=\"file2.gif\"" forHTTPHeaderField:#"Content-disposition"];
[theRequest addValue:#"image/gif" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue:#"binary" forHTTPHeaderField:#"Content-Transfer-Encoding"];
And then you should append the body. On the server side, you can get the file name and the bytes composing the file.
This is not exactly the code you should use, but it should give you a good idea of how to proceed.
connection Kit might be what your looking for