get web page source in objective-c - ios

i start developing in iOS and i have the next problem:
i need to extract the page source of web page to extract some data from it.
with some urls i'v succseed and some not
here is my code that i get the page source:
(NSString *)getStringFromUrl:(NSString *)url{
NSError *err = nil;
NSString *agentString = #"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:
[NSURL URLWithString:url]];
[request setValue:agentString forHTTPHeaderField:#"User-Agent"];
NSData *data = [ NSURLConnection sendSynchronousRequest:request returningResponse: nil error: &err ];
NSString *returnData = [[NSString alloc] initWithBytes: [data bytes] length:[data length] encoding: NSUTF8StringEncoding];
if (err!=nil)
NSLog(#"error message: %#",err.description);
return returnData;
}
link that worked for me:
http://50.22.211.228:8000/played.html
link that not worked for me and return nil in returnData:
http://107.150.5.94:7070/played.html

The issue appears to be that the second request is apparently not returning valid NSUTF8StringEncoding. If you look at the NSData itself (if you log that, you'll see the hexadecimal representation of the binary payload), you are receiving data, but it's the conversion to a NSString that is failing.
If you look at that page in a web browser, it gets confused, too (the characters don't make sense). I'd wager that they are not writing a valid UTF8 string in that HTML, though it's not immediately obvious what precisely they're doing (it doesn't appear to be a string encoding that I recognize).
You can use NSASCIIStringEncoding to see the same mess in your code that you'll see in your web browser.

I use the following to get the source code of a web page as NSString:
NSError *error = nil;
NSString *urlString = [NSString stringWithFormat:#"%#", #"http://www.google.es"];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
NSString *webSource = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&error];
I hope this helps you and is what you are looking for.

Related

Twitter iOS GET statuses/home_timeline using Parse

This will be my first iPhone app and I am running into difficulties that I thought I'd be able to find a tutorial on, but alas.... nothing on the inter webs that I can find.
I'm trying to get an initial 20 or so tweets from a signed in user (signed in using Parse's Twitter authentication) and I can't seem to get it to work. I put a breakpoint in when I get an error and this is what it shows:
I'm contemplating abandoning Parse all together and doing my own authentication using https://github.com/nst/STTwitter but wanted to quickly see if there was a way to simply do what I am trying to. The code is question is at this github: https://github.com/johndangerstorey/twiz and outlined below as found in my MyLoginViewController.m file:
NSString *bodyString = [NSString stringWithFormat:#"https://api.twitter.com/1.1/statuses/home_timeline.json?screen_name=johnDANGRstorey"];
// Explicitly percent-escape the '!' character.
bodyString = [bodyString stringByReplacingOccurrencesOfString:#"!" withString:#"%21"];
NSURL *url = [NSURL URLWithString:bodyString];
NSMutableURLRequest *tweetRequest = [NSMutableURLRequest requestWithURL:url];
tweetRequest.HTTPMethod = #"GET";
tweetRequest.HTTPBody = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
[[PFTwitterUtils twitter] signRequest:tweetRequest];
NSURLResponse *response = nil;
NSError *error = nil;
// Post status synchronously.
NSData *data = [NSURLConnection sendSynchronousRequest:tweetRequest
returningResponse:&response
error:&error];
// Handle response.
if (!error) {
NSLog(#"Response: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
} else {
NSLog(#"Error: %#", error);
}
Thanks for your reply and help.
I figured it out, GET requests don't require .body or .method requests so I just removed
tweetRequest.HTTPMethod = #"GET";
tweetRequest.HTTPBody = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
and was golden.

Error Domain=NSURLErrorDomain Code=-1000 "bad URL"

Following problem:
I get every time the error -1000, but really don't know what I can change. I've tried it with GET and another sending method. But I get every time the same error.
Does someone see my mistake?
thanks
NSString *get =[[NSString alloc] initWithFormat:#"mobileNumber=%#&deviceToken=%#",myMobileNumber,myDeviceToken];
[get stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://myHomepage.net/login.php?%#",get]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *responseData = #"";
if ([response statusCode] ==200 )
{
responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"---------responseData: %#",responseData);
return responseData;
} else {
You're not saving the result of stringByAddingPercentEscapesUsingEncoding. So, instead of:
[get stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
You need:
get = [get stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
FYI, while the above fix will now successfully percent escape all characters not permitted within in a URL, there are actually some characters that are acceptable in a URL, but are not acceptable within a parameter value (e.g. a + which is interpreted as a space, or & which delineates parameters).
It is important to percent escape the values used in the parameters of the request in such a way that you escape not only those characters that are not legal in a URL, but also a few that are otherwise legal in a URL, but are not permitted within a parameter value.
Unfortunately the standard stringByAddingPercentEscapesUsingEncoding method does not do this. But the CFURL function CFURLCreateStringByAddingPercentEscapes does. You apply this function to the individual parameter values. Here is a method that performs the necessary toll-free bridging between this function and ARC:
- (NSString *)percentEscapeString:(NSString *)string
{
NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
(CFStringRef)#" ",
(CFStringRef)#":/?#!$&'()*+,;=",
kCFStringEncodingUTF8));
return [result stringByReplacingOccurrencesOfString:#" " withString:#"+"];
}
You can use this method as follows:
NSString *get =[[NSString alloc] initWithFormat:#"mobileNumber=%#&deviceToken=%#", [self percentEscapeString:myMobileNumber], [self percentEscapeString:myDeviceToken]];
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://myHomepage.net/login.php?%#",get]];
This should correctly percent escape the parameter values of the string, regardless of the presence of these reserved characters, or not.

Get link MP3 from a web

I want to get a MP3 link from a website, I don't know exactly what type of this website is, but it only has content like this (link: http://www.nhaccuatui.com/download/song/4Upyxq0QlytX)
{"error_message":"Success","data":{"stream_url":"http:\/\/download.s81.stream.nixcdn.com\/dd634cb8afcc15d7c17a8ce4c548709f\/533cc58e\/NhacCuaTui791\/KhongQuanTam-ChiDan_4cyw4_hq.mp3","is_charge":"false"},"error_code":0,"STATUS_READ_MODE":true}
What can I do if I want to get content (link mp3) from key (?) "stream_url" from this link to put it in to my iOS Applications?
If you rearrange your JSON it will look like this:
{
"error_message":"Success",
"data":{
"stream_url":"http:\/\/download.s81.stream.nixcdn.com\/dd634cb8afcc15d7c17a8ce4c548709f\/533cc58e\/NhacCuaTui791\/KhongQuanTam-ChiDan_4cyw4_hq.mp3",
"is_charge":"false"
},
"error_code":0,
"STATUS_READ_MODE":true
}
From that you can see its a dictionary.
If you want to get it via url request do something like this:
NSDictionary *dictionaryData;
NSURLRequest *requestURL = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.00];
NSHTTPURLResponse *response;
NSError *error = [[NSError alloc]init];
NSData *apiData = [NSURLConnection sendSynchronousRequest:requestURL returningResponse:&response error:&error];
dictionaryData = [NSJSONSerialization JSONObjectWithData:apiData options:kNilOptions error:&error];
Then you can get the url by doing this:
NSString *str = [[dictionaryData objectForKey:#"data"] objectForKey:#"stream_url"];
Then make another request for getting the file.
The data you presented is in JSON format. To access it, use NSJSONSerialization.

NSXMLParser with encoded Data in String

I want to program a NSXMLParser and I have the data for this lying in a NSString (encoded).
If I put the NSString to NSLog it gives me the right lines I need. But when I want to start the Parser with the data from that NSString it doesn't start.
Here is my code:
The interface:
#interface StundenplanParser () <NSXMLParserDelegate>
#end
The method that gets called from the ViewController:
-(void)parserStart
{
// Create your request string with parameter name as defined in PHP file
NSString *myRequestString = [NSString stringWithFormat:#"matr=%#&pressme=%#",#"33886",#"S T A R T"];
// Create Data from request
NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"http://www2.htw-dresden.de/~rawa/cgi-bin/auf/raiplan_app.php"]];
// set Request Type
[request setHTTPMethod: #"POST"];
// Set content-type
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
// Set Request Body
[request setHTTPBody: myRequestData];
// Now send a request and get Response
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil];
// Log Response
NSString *response = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSASCIIStringEncoding];
NSLog(#"Response:%#\n",[response substringToIndex:64]);
// If I would use NSASCIIStringEncoding in the following line, the data would be nil
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
if (!parser) NSLog(#"Error with parser");
else NSLog(#"parser successfully initialised");
[parser setDelegate:self];
[parser parse];
}
NOTE: In my response (NSString*) is the code that I want to parse. If I print it out using NSLog I see the source code of the website..
Now I want to show only the elements the NSXMLParser found with this:
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(#"found element: %#", elementName);
}
I hope it's not a problem that I left the url in the code. I just want to parse the schedule from my university.
I hope you understand what I mean and you could help me.
EDIT: My Problem is not that I don't get the XML. My Problem is that the parser won't start.. I edited the code.
EDIT2: The output from the response is:
2013-11-18 23:52:16.008 University[17210:70b] Response:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN
So it seems like it worked, but the problem is the encoding of this into a NSData instance that I need to initialize my parser.
EDIT3:
After implementing the parser:parseErrorOccurred I got the following error in the Console:
2013-11-19 08:58:26.190 University[17458:70b] ERROR while parsing: The operation couldn’t be completed. (NSXMLParserErrorDomain error 65.)
Translating with Google, the website
http://www2.htw-dresden.de/~rawa/cgi-bin/auf/raiplan_app.php
says 'Create a schedule in csv format' and entering the matriculation number from your code 33886, it returns a web page that has in its the data you're trying to get at:
<Stunde>
<titel>Englisch C1 III/SI (w.o.)</titel>
<kuerzel>Engl_C1 Üw3/SI Technik</kuerzel>
<raum>S 334</raum>
<dozent>Kilian</dozent>
<datum>16.10.2013</datum>
<anfang>7:30</anfang>
<ende>9:00</ende>
</Stunde>
If you extract this data and form a new XML string, then the XML Parser will run. The tag is where this data begins, and the last one is followed by a , so this code will extract out the elements:
NSRange startRange = [dataString rangeOfString:#"<Stunde>"];
NSString *dataAfterHtml = [dataString substringFromIndex:startRange.location];
NSRange endRange = [dataAfterHtml rangeOfString:#"<br>"];
dataAfterHtml = [dataAfterHtml substringToIndex:endRange.location];
NSString *formattedXML = [NSString
stringWithFormat:#"<data>%#</data>",
dataAfterHtml];
now give this the parser
NSData *data = [formattedXML dataUsingEncoding:NSUTF8StringEncoding];
xmlParser = [[NSXMLParser alloc] initWithData:data];
You are interpreting the data as ASCII and then encoding it as UTF8, you are going to end up with an invalid string or with a string with invalid characters. Try this:
...
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:returnData];
[parser setDelegate:self];
[parser parse];
You problem is that you are not URLEncoding the data, but feeding it draw into the HTTP body. In this:
NSMutableData *body = [NSMutableData data];
NSString *postWerte = #"matr=XXXXX&lang=1&aktkw=1&pressme=S T A R T";
[body appendData:[postWerte dataUsingEncoding:NSUTF8StringEncoding]];
you need to URLEncode "S T A R T", and should bee really URLencoding every string that follows an equal sign. (UPDATE: so it works as is - my service is way picker and I must encode. YMMV!)
EDIT:
So try changing your code a bit, and if it still fails then please post the first line or two of the returned data:
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil];
assert(returnData); // you should be suppling an error pointer just in case but this will do for now
// Log Response
{
NSString *response = [[NSString alloc] initWithBytes:[returnData bytes] length:[returnData length] encoding:NSASCIIStringEncoding];
NSLog(#"%#",[response substringToIndex:64]); // or a bit mor
}
NSXMLParser *parser = [[NSXMLParser alloc] initWithData: returnData];
assert(parser); // will be nil if anything has gone wrong, obviously in your real code put an if test
[parser setDelegate:self]; // no affect for nil object
[parser parse];// no affect for nil object
EDIT2:
BTW, I missed this, and it may not make a difference, but the parser object has no property "delegate", you technically should be using the setter:
[parser setDelegate:self];
Also, you did implement the following, right?
parserDidStartDocument: // which I assume you never see, right
parser:validationErrorOccurred:
parser:parseErrorOccurred:
parserDidEndDocument: // you never see this either?
If parser is nil, dump the whole returned data as a UTF8 string (per the log code). Also, try looking for an error:
dispatch_async(dispatch_get_main_queue(), ^
{
NSLog(#"PARSE ERROR: %#", [parser parseError]);
} );

Convert a Web service from GET to POST

I have created a web service to make a communication between an iOS application and a Joomla web site, and I used the GET method to communicate between the mobile application and the web service and also between the web service and the controller (PHP file that does the work and return the data) , but I didn't find how to convert the implementation to POST method here is the actual system :
ws.php : it's the web service (simple example )
<?php
$id = $_GET['id'] ; // get the data from the URL
// here i make testes
// then I redirect to the controller of the Joomla component that receive
// the call of the request the URL is actually the attribute "action" of
// an existing HTML Form that implement the login system, I added a
// parameter called web service to help me to modify the controller
// to make the difference between a normal call and a web service call
header("Location: index.php?option=com_comprofiler&task=login&ws=1&id=1");
?>
Controller.php : the receiver of the web service call and the web call (from browser)
<?php
// code added by me, in the existent controller of the component
// it was waiting for a form submitting, so I got to convert my data to POST here
if (isset($_GET['ws'])) // it's a web service call
{
$_POST['id'] = $_GET['id'] ;
// do the task ...
if ($correctLogin) // just an example
echo "1"
else
echo '0';
}
?>
I didn't put the real implementation, and it's just a simple example of the system, but it's the same
Call from the mobile
NSURL *url = [[NSURL alloc]initWithString:#"http://localhost/ws.php?id=1"];
NSData *dataUrl= [NSData dataWithContentsOfURL:url];
NSString *str = [[NSString alloc]initWithData:dataUrl
encoding:NSUTF8StringEncoding];
if(![str isEqualToString:#"0"])
NSLog(#"connected");
else
NSLog(#"not connected");
so I don't want to use the GET method, I just want to receive my data from the mobile using POST and also send the data to the controller using POST also, what is the best solution ?
If you want your app to send data using POST method, then I'm this code. I hope it will help.
It takes the data to be sent in dictionary object.
Ecodes the data to be sent as POST
and then returns the response (if you want the results in string format you can use [[NSString alloc] initWithData:dresponse encoding: NSASCIIStringEncoding]; when returning data)
-(NSData*) getData:(NSDictionary *) postDataDic{
NSData *dresponse = [[NSData alloc] init];
NSURL *nurl = [NSURL URLWithString:url];
NSDictionary *postDict = [[NSDictionary alloc] initWithDictionary:postDataDic];
NSData *postData = [self encodeDictionary:postDict];
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nurl];
[request setHTTPMethod:#"POST"]; // define the method type
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
// Peform the request
NSURLResponse *response;
NSError *error = nil;
dresponse = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
return dresponse;
}
This method prepares the Dictionary data for POST
- (NSData*)encodeDictionary:(NSDictionary*)dictionary {
NSMutableArray *parts = [[NSMutableArray alloc] init];
for (NSString *key in dictionary) {
NSString *encodedValue = [[dictionary objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *part = [NSString stringWithFormat: #"%#=%#", encodedKey, encodedValue];
[parts addObject:part];
}
NSString *encodedDictionary = [parts componentsJoinedByString:#"&"];
return [encodedDictionary dataUsingEncoding:NSUTF8StringEncoding];
}

Resources