I'm trying to get HTML with search results from Google. With sending GET request for example to:
https://www.google.ru/?q=1111
But if in browser all is ok, when I'm trying to use it with curl or to get source with "View source" in Google, there is only some Javascript code, no search result. Is that some type of protection? What can I do?
You now have to use the Google Search API to make your GET requests.
All other methods have been blocked.
The page from your question is the Google Search page with the input field.
The search results page is this one:
https://www.google.ru/search?q=1111
Rotate proxies and user agents, and delay similar requests to get the HTML from Google Search results pages with fewer amount of bans.
Or use SerpApi to access HTML and the extracted data from it. It has a free trial.
curl -s 'https://serpapi.com/search?q=coffee'
Output
{
// Omitted
"organic_results": [
{
"position": 1,
"title": "Coffee - Wikipedia",
"link": "https://en.wikipedia.org/wiki/Coffee",
"displayed_link": "en.wikipedia.org › wiki › Coffee",
"snippet": "Coffee is a brewed drink prepared from roasted coffee beans, the seeds of berries from certain Coffea species. When coffee berries turn from green to bright red ...",
"sitelinks": {
"expanded": [
{
"title": "History",
"link": "https://en.wikipedia.org/wiki/History_of_coffee",
"snippet": "The history of coffee dates back to the 15th century, and possibly ..."
},
{
"title": "International Coffee Day",
"link": "https://en.wikipedia.org/wiki/International_Coffee_Day",
"snippet": "International Coffee Day (1 October) is an occasion that is ..."
},
{
"title": "List of coffee drinks",
"link": "https://en.wikipedia.org/wiki/List_of_coffee_drinks",
"snippet": "Milk coffee - Nitro cold brew coffee - List of coffee dishes - ..."
},
{
"title": "Portal:Coffee",
"link": "https://en.wikipedia.org/wiki/Portal:Coffee",
"snippet": "Coffee is a brewed drink prepared from roasted coffee beans, the ..."
},
{
"title": "Coffee bean",
"link": "https://en.wikipedia.org/wiki/Coffee_bean",
"snippet": "A coffee bean is a seed of the Coffea plant and the source for ..."
},
{
"title": "Geisha",
"link": "https://en.wikipedia.org/wiki/Geisha_(coffee)",
"snippet": "Geisha coffee, sometimes referred to as Gesha coffee, is a type of ..."
}
],
"list": [
{
"date": "Color: Black, dark brown, light brown, beige"
}
]
},
"rich_snippet": {
"bottom": {
"detected_extensions": {
"introduced_th_century": 15
},
"extensions": [
"Introduced: 15th century",
"Color: Black, dark brown, light brown, beige"
]
}
},
"cached_page_link": "https://webcache.googleusercontent.com/search?q=cache:U6oJMnF-eeUJ:https://en.wikipedia.org/wiki/Coffee+&cd=2&hl=sv&ct=clnk&gl=se",
"related_pages_link": "https://www.google.se/search?gl=se&hl=sv&q=related:https://en.wikipedia.org/wiki/Coffee+coffee&sa=X&ved=2ahUKEwjJ9p2p_KXuAhVlRN8KHf22D8wQHzABegQIAhAJ"
}
},
// ...
}
Disclaimer: I work at SerpApi.
To add a bit more sauce to the answers as they are not correct and do not even respond to your problem.
First of all, it's perfectly legal to scrape Google as long as you do not harm their service through it (DoS-like).
Also the methods have not been blocked, it's just not that simple.
The speed depends on your methods, it does not have to be very slow..
You can scrape ten thousands of keyword pages in a minute if needed.
You will find a better answer to the topic here: Is it ok to scrape data from Google results?
Your problem with curl comes indeed from protection, Google does not allow automated access and it has a very sophisticated set of detection algorithms.
They go from simple user agent checks (that's what stopped you directly) up to artificial intelligence that tries to detect unusual queries or related queries.
You can load it in the browser and then scrape results via Javascript.
Or you can use Google API, but seems that it requires payment if you will request it more then 100 times per day.
Related
I'm pretty new to the DRF and serializing/deserializing. I'm slowly building a dashboard for my business during the corona virus and learning to code. I am in a little deep, but after spending more than $10k on developers on upwork and not really get much result, I figured, what do I have to lose?
Our software provider has a full API for our needs https://developer.myvr.com/api/, but absolutely no dashboard to report statistics about our clients reservation data.
The end result will be a synchronization of some of the data from their API to my database which will be hosted through AWS. I chose to do it this way due to having to do some post processing of data from the API. For example, we need to calculate occupancy rates(which is not an endpoint), expenses from our accounting connection and a few other small calculations in which the data is not already in the provided API. I originally wanted to use the data from the API solely, but I'm hesitant due to the reasons above.
That's the backstory, here are the questions:
The API response is extremely complex and nested multiple times, what is the best practise to extract a replication of the structure of the data to my own Database? Would I have to create models for each field manually?
Example response:
```{
"uri": "https://api.myvr.com/v1/properties/b6b0f2fe278f612b/",
"id": "b6b0f2fe278f612b",
"key": "b6b0f2fe278f612b",
"accessDescription": null,
"accommodates": 11,
"active": false,
"addressOne": "11496 Zermatt Dr",
"addressTwo": null,
"allowTurns": true,
"amenities": "https://api.myvr.com/v1/property-amenities/?propertyId=b6b0f2fe278f612b",
"automaticallyApprove": false,
"baseNightlyRate": "395.00",
"baseRate": {
"uri": "https://api.myvr.com/v1/rates/660c299d4785c32e/",
"id": "660c299d4785c32e",
"key": "660c299d4785c32e",
"externalId": null,
"baseRate": true,
"changeoverDay": null,
"created": "2019-01-19T08:02:36Z",
"currency": "USD",
"endDate": "2020-01-18",
"minStay": 3,
"modified": "2019-01-19T08:02:36Z",
"monthly": 0,
"name": "Base Rate",
"weekNight": 39500,
"nightly": 39500,
"position": 0,
"property": {
"name": "API Demo Property",
"uri": "https://api.myvr.com/v1/properties/b6b0f2fe278f612b/",
"id": "b6b0f2fe278f612b",
"externalId": null,
"key": "b6b0f2fe278f612b",
"slug": "api-demo-property"
},
"ratePlan": {
"uri": "https://api.myvr.com/v1/rate-plans/862caa3f5267602d/",
"key": "862caa3f5267602d",
"name": "Default Rates for Property"
},
"repeat": true,
"startDate": "2020-01-18",
"weekend": 0,
"weekendNight": 0,
"weekly": 250000
},
"bathrooms": "4.0",
"bedrooms": 4,
"bookingUrl": "https://myvr.com/reservation/redirect/booking/b6b0f2fe278f612b/",
"checkInTime": "16:00:00",
"checkOutTime": "10:00:00",
"city": "Truckee",
"commissionStructure": null,
"countryCode": "US",
"created": "2016-01-19T00:01:48Z",
"currency": "USD",
"customFields": {},
"description": "Luxurious living, scenic mountain setting, entertainment galore. Located on a quiet street in Tahoe Donner, our well equipped modern home is nestled into the wilderness. A babbling creek greets visitors approaching the front step as it collects into a small pond with a cascading waterfall. <br/>\n<br/>\nInside, over 3,000 sqft of luxurious living space divides itself between two floors. On the first floor, a beautiful kitchen with granite counters, gas stove and stainless steel appliances opens to a large great room centered around a wood burning fireplace and featuring 30' soaring ceilings. A spacious loft overlooks the great room, showcasing a large poker/card table. Upstairs features a large entertainment room, complete with wet bar, shuffleboard table, and state-of-the-art television setup with surround sound. The scenic backyard is accessible from a large deck featuring a new hot tub with seating for 7.",
"externalId": null,
"feePlan": {
"uri": "https://api.myvr.com/v1/fee-plans/4d1c44383755051b/",
"key": "4d1c44383755051b",
"name": "Default Fees for Listing"
},
"headline": "Beautiful Four Bedroom Lake Front Property",
"houseRules": null,
"instantBookingsEnabled": false,
"lat": "39.3422523000",
"level": "unit",
"localAreaDescription": "Tahoe Donner is a year round activity resort. The amenities include private beach/boat launching facilities, pools, recreation center, tennis, horseback riding, golf, downhill skiing as well as cross country skiing. Truckee is a historical mining town-having a western feel but also has museums, theaters, fine dining plus 2 large supermarkets-all less than 3 miles from the house. Our home is also located within a 15 minute drive to 4 major ski resorts. Downtown Reno is a short 40 minute drive away for those seeking a night on the town or the thrill of a Nevada casino.",
"lon": "-120.2271947000",
"lowestNightlyRate": "395.00",
"manual": "",
"modified": "2019-10-18T17:18:43Z",
"name": "API Demo Property",
"owner": null,
"postalCode": "96161",
"ratePlan": {
"uri": "https://api.myvr.com/v1/rate-plans/862caa3f5267602d/",
"key": "862caa3f5267602d",
"name": "Default Rates for Property"
},
"ratePlanLocked": false,
"region": "CA",
"shortCode": "API",
"size": 3000,
"slug": "api-demo-property",
"suitableElderly": "yes",
"suitableEvents": "unknown",
"suitableGroups": "yes",
"suitableHandicap": "no",
"suitableInfants": "unknown",
"suitableKids": "yes",
"suitablePets": "no",
"suitableSmoking": "no",
"transitDescription": null,
"type": "house",
"weekendNights": [
5,
6
]
}```
I think the best way to populate the database would be to run a custom management command to run a once off script, I've done this previously with another database, however I'm still stuck as I don't really want to write these models manually. Also a concern is if a field is missing or the structure changes.
This project is definitely above my skills and extremely ambitious, but I would appreciate any feedback or advice anyone might have.
Thanks,
Darren
So I didn't really get any interest in this question, but I ended up working it out myself.
I hope someone googles it and might find it helpful.
import requests
from rest_framework.response import Response
from django.core.management.base import BaseCommand, CommandError
from reservation.models import Reservation
import time
MYVR_URL = 'https://api.myvr.com/'
MYVR_RESERVATION = 'v1/reservations/?limit=100'
headers = {
'Authorization': 'Basic SOmeAPiCodeHeRe123=',
}
class Command(BaseCommand):
help = 'Imports new properties and saves the objects in the database'
def handle(self, *args, **options):
url = MYVR_URL + MYVR_RESERVATION
print("Populating Reservations")
def looping_api(url, headers):
while url:
r = requests.request("GET", url, headers=headers)
url = r.json().get('next')
props_data = r.json().get('results')
start_time = time.time()
for prop in props_data:
try:
created = Reservation.objects.update_or_create(
myvr_key=prop.get('key'),
adults=prop.get('adults'),
children=prop.get('children'),
checkin=prop.get('checkIn'),
checkout=prop.get('checkOut'),
checkinTime=prop.get('checkInTime'),
checkoutTime=prop.get('checkOutTime'),
guestFirstName=prop.get('firstName'),
dateCreated=prop.get('created'),
dateBooked=prop.get('dateBooked'),
dateCancelled=prop.get('dateCanceled'),
contact=prop.get('contact').get('name'),
contact_key=prop.get('contact').get('key'),
guest_type=prop.get('guestType'),
property_name=prop.get('property').get('name'),
property_key=prop.get('property').get('key'),
source_code=prop.get('source').get('code'),
source_name=prop.get('source').get('name'),
total_due=prop.get('quote').get('totalDue'),
total_refundables=prop.get(
'quote').get('totalRefundableFees'),
total_nonrefundables=prop.get(
'quote').get('totalNonrefundableFees'),
reference_id=prop.get('referenceId'),
)
print(
f"Added obj {prop.get('key')}")
except AttributeError as error:
print(f"{error} attribute is null or owner booking")
url = r.json().get('next')
print(r.json().get('next'))
print(len(props_data))
end_time = time.time()
duration = (end_time - start_time)
print(duration)
looping_api(url, headers)
WIX.com Schema Snippets Problems
I'm trying to install some schema snippets into my Wix website and I keep getting errors.
Somebody said
Your code has 4 tags in it, and you're using different Schema
types
Wix help says:
You can use one main JSON-LD, nesting is allowed.
I removed the script tags but I'm not sure how to "nest" the different Schema types. Any help would be greatly appreciated
-Jeff
The code is here: https://docs.google.com/document/d/1QWAkjjExws7IdYH25HuBbGN5gkeSDlusXlYdS56vO0Q/edit?usp=sharing
Here are the WIX JSON Guidelines:
If you get an error, check your structured data and make sure:
Your code is in the JSON-LD format.
Your code begins with the HTML tag and ends with .
Within the tag, the JSON-LD itself is wrapped in curly brackets { } .
Each element in the JSON-LD is separated from the next one with a comma. However, the last element shouldn't have a comma after it.
Your code is using straight quotes "". Curly quotes “” don't pass validation.
Your code is using only one script tag, and this script tag includes only one main schema markup type (one main JSON-LD, nesting is allowed).
All fields have the format: "key":"content". Pressing 'Enter' in either field won't pass validation, for example:
Pass validation: "text" : "this is a sentence with text."
FAIL validation: "text" : "this is a
sentence with text."
Your code contains less than 7,000 characters. Code with over 7,000 characters can not be added.
You have added multiple top level entities which is not a valid syntax. To get around it you need to define a top level entity that contains all the others. This is done by using #graph and listing the others in an array. e.g.
<script type="application/ld+json">
{
"#context": "http://schema.org",
"#graph": [
{
"#type": "TravelAgency",
"name": "ColombiaMotoAdventures",
"image": "https://static.wixstatic.com/media/331b1d_452c7e9b5e304d2ca37e16dc81141be6~mv2.png/v1/fill/w_270,h_270,al_c,usm_0.66_1.00_0.01/331b1d_452c7e9b5e304d2ca37e16dc81141be6~mv2.png",
"#id": "",
"url": "https://www.ColombiaMotoAdventures",
"telephone": "3214917060",
"priceRange": "80-100",
"address": {
"#type": "PostalAddress",
"streetAddress": "Cra. 102, Loma de Pajarito ##68-36",
"addressLocality": "Medellin",
"postalCode": "",
"addressCountry": "CO"
},
"openingHoursSpecification": {
"#type": "OpeningHoursSpecification",
"dayOfWeek": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
],
"opens": "09:00",
"closes": "18:00"
},
"sameAs": "https://www.facebook.com/ColombiaMotoAdventures"
},
{
"#type": "WebSite",
"name": "Colombia Moto Adventures",
"url": "https://www.colombiamotoadventures.com/",
"potentialAction": {
"#type": "SearchAction",
"target": "https://www.colombiamotoadventures.com/?s={search_term_string}{search_term_string}",
"query-input": "required name=search_term_string"
}
},
{
"#type": "Trip",
"name": "Colombia Motorcycle Tour",
"description": "The dozen best places and areas that should be on your list when visiting Medellin, Colombia. Our motorcycle tours and rentals are geared towards beginner and intermediate riders. We have lots of people come down and tour around who don't speak a word of Spanish and they do just fine. Our tours are also geared towards beginner and intermediate riders. We are an American owned ADV motorcycle rental company based in Medellin, Colombia. Our motorcycles are perfect for comfortable, long-distance touring but are also capable to take you mud, rocks and river-crossings if that's where you decide to go.",
"itinerary": [
{
"#type": "Country",
"name": "Colombia",
"description": "Colombia is projected as one of Latin America's main destinations in terms of adventure tourism, multiple cultural activities, the best gastronomy, so that you can make your tourism trip an unforgettable experience. It is a country that makes you vibrate with its emotions, where you can choose the destination of your preference.",
"url": "https://en.wikipedia.org/wiki/Colombia"
}
]
},
{
"#type": "TouristTrip",
"name": "Colombia",
"description": "This trip is modeled as several distinct Tourist Trips using the subTrip property.",
"subTrip": [
{
"#type": "TouristTrip",
"name": "Colombia Jungle Motorcycle Adventure",
"description": "Rent a motorcycle in Medellin and drive to the jungle. Swim in the crystal clear waters of a jungle river and drink cold beers while listening to the amazing sounds of the jungle at night. The perfect South America motorcycle route"
},
{
"#type": "TouristTrip",
"name": "Guatape Motorcycle Trip",
"description": "Ride a motorcycle to Guatape and have lunch! Leave Medellín in the morning and take a drive out to the town of Guatape."
},
{
"#type": "TouristTrip",
"name": "Suzuki DR650 Motorcycle Rental",
"description": "Rent a DR650 motorcycle in Colombia. Known as the worlds best dual-sport motorcycle take your South America motorcycle adventure with this! "
},
{
"#type": "TouristTrip",
"name": "Medellin Motorcycle Trout Fishing Day Trip",
"description": "Ride an adventure motorcycle into the remote backcountry outside of Medellin and catch trout with a local family. "
},
{
"#type": "TouristTrip",
"name": "Cerro del Padre Amaya Off-Road Motorcycle Trip",
"description": "Boquerón to Padre Amaya - This is where things get interesting, We take our motorcycles and start heading up an off-road trail stunning views and high altitude."
},
{
"#type": "TouristTrip",
"name": "Colombia Waterfalls Motobike Tour",
"description": "Want to explore beyond Guatape? This motorcycle trip is for you! San Carlos is a small pueblo near Medellín that hasn’t yet been discovered by many foreigners. But it’s a hidden gem surrounded by mountains, rivers, waterfalls, and nature."
},
{
"#type": "TouristTrip",
"name": "Medellin Motorcycle Rental",
"description": "Motorcycle Tours and Rentals in Medellin, Colombia. We have the best selection of adventure motorcycles in Colombia Suzuki DR650, Honda XRE300, Kawasaki Versys-X 300."
},
{
"#type": "TouristTrip",
"name": "5 Day Colombia Lost Emerald City Motorcycle Tour",
"description": "Off-Road Colombia - Journey to the Lost Emerald City. Ride motorcycles off-road through rugged terrain, cross mountain ranges, and streams see amazing vistas and waterfalls and finally get the chance to and interact with local emerald miners. You can even buy emeralds directly from the miners in the field!"
},
{
"#type": "TouristTrip",
"name": "Colombia Coffee Region Motorcycle Tour",
"description": "Take an amazing motorcycle trip through Colombia's Coffee Region. Swim in the hot springs in Santa Rosa and take a coffee tour in Salento as well as walking through the Wax Palms in the Valle Cocora, and as always, enjoy all the twists and turns while riding through some of the amazing scenery in the world."
},
{
"#type": "TouristTrip",
"name": "19 Day Colombia to Ecuador Motorcycle Trip",
"description": "Journey To The Center Of The Earth. Based on customer feedback, we have decided to turn our most popular South America motorcycle route in Colombia into a full motorcycle tour through the most beautiful parts of Colombia and Ecuador. Rent a motorcycle in Medellin, Colombia, and ride to Quito, Ecuador and back."
}
]
}]
}
</script>
I am trying to understand if there is an option to get the conversation logs of the discussions with some sort of a webhook.
The API.AI docs only refer to using webhook for fulfilment purposes , but for now I don't plan my server (GCP ENGINE APP) to supply fulfilment but only to log the relevant parameters from each conversation.
Anyone knows how to approach this?
Turn on the webhook feature for the intent. You will be able to get the requests and all the data associated with it. You will be able to send back to API.AI too. Here is the full circle:
{
"id": "891db09a-851c-43dc-81c6-4c6705c94f85",
"timestamp": "2017-01-03T10:31:18.676Z",
"result": {
"source": "agent",
"resolvedQuery": "yes, France",
"action": "show.news",
"actionIncomplete": false,
"parameters": {
"adjective": "",
"subject": "France"
},
"contexts": [
{
"name": "subject",
"parameters": {
"subject.original": "France",
"adjective": "",
"subject": "France",
"adjective.original": ""
},
"lifespan": 5
},
{
"name": "region",
"parameters": {
"subject.original": "France",
"adjective": "",
"subject": "France",
"adjective.original": ""
},
"lifespan": 5
}
],
"metadata": {
"intentId": "34773849-4ac2-4e28-95a5-7abfc061044e",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"intentName": "subject"
},
"fulfillment": {
"speech": "Here is the latest news\n\n According to Watson the main emotion expressed in the article is: ;( ( sadness )\n\n Son of Equatorial Guinea’s president facing trial in France\n\nPARIS — After years of investigation, France on Monday put the son of the president of Equatorial Guinea on trial for corruption, charged with spending many millions in state funds — much of it allegedly in cash — to feed an opulent lifestyle of fast cars, designer clothes, works of art and...\n\nRead more: https://www.washingtonpost.com/world/europe/son-of-equatorial-guineas-president-facing-trial-in-france/2017/01/02/b03d30d0-d0cb-11e6-9651-54a0154cf5b3_story.html",
"source": "Washington Post",
"displayText": "Here is the latest news. According to Watson the main emotion expressed in the article is: sadness",
"messages": [
{
"type": 0,
"speech": "Here is the latest news\n\n According to Watson the main emotion expressed in the article is: ;( ( sadness )\n\n Son of Equatorial Guinea’s president facing trial in France\n\nPARIS — After years of investigation, France on Monday put the son of the president of Equatorial Guinea on trial for corruption, charged with spending many millions in state funds — much of it allegedly in cash — to feed an opulent lifestyle of fast cars, designer clothes, works of art and...\n\nRead more: https://www.washingtonpost.com/world/europe/son-of-equatorial-guineas-president-facing-trial-in-france/2017/01/02/b03d30d0-d0cb-11e6-9651-54a0154cf5b3_story.html"
}
],
"data": {
"newsAgent": {
"adjective": "",
"subject": "France",
"intent": "subject",
"action": "show.news",
"news": {
"title": "Son of Equatorial Guinea’s president facing trial in France",
"source": "Washington Post",
"link": "https://www.washingtonpost.com/world/europe/son-of-equatorial-guineas-president-facing-trial-in-france/2017/01/02/b03d30d0-d0cb-11e6-9651-54a0154cf5b3_story.html",
"language": "english",
"body": "PARIS — After years of investigation, France on Monday put the son of the president of Equatorial Guinea on trial for corruption, charged with spending many millions in state funds — much of it allegedly in cash — to feed an opulent lifestyle of fast cars, designer clothes, works of art and...",
"emotion": "sadness",
"emoticon": ";("
},
"speech": "Here is the latest news",
"sessionId": "0856125a-d0bc-4cba-990d-cbcfaea536db"
}
}
},
"score": 1
},
"status": {
"code": 206,
"errorType": "partial_content",
"errorDetails": "Webhook call failed. Error message: Webhook contains contexts with empty names or names containing whitespaces. ErrorId: 131000fa-0ec1-4efb-b47c-64301ac7bb2b"
},
"sessionId": "0856125a-d0bc-4cba-990d-cbcfaea536db"
}
The result object is the request that API.AI sends you, you get the contexts objects as well.
The fulfilment object is the response my endpoint sent back to API.AI
Check the documentation
When I use the Google Feed API's find search, a lot of the time it's not returning the URLs of the feed.
As an example, a find search for the query 'CNN', points to this URL:
https://ajax.googleapis.com/ajax/services/feed/find?v=1.0&q=cnn
And returns these results (trimmed):
{
"responseData": {
"query": "cnn",
"entries": [
{
"url": "",
"title": "<b>CNN</b>.com: Breaking News, US, World, Weather, Entertainment <b>...</b>",
"contentSnippet": "Find the latest breaking news and information on the top stories, weather, <br>\nbusiness, entertainment, politics, and more. For in-depth coverage, <b>CNN</b> <br>\nprovides ...",
"link": "http://www.cnn.com/"
},
{
"url": "",
"title": "World News - International Headlines, Stories and Video - <b>CNN</b>.com",
"contentSnippet": "<b>CNN</b> brings you International News stories, video and headlines from Europe, <br>\nAsia, Africa, the Middle East, and the Americas.",
"link": "http://www.cnn.com/world"
},
{
"url": "",
"title": "U.S. News - Headlines, Stories and Video - <b>CNN</b>.com",
"contentSnippet": "<b>CNN</b>.com brings you US News, Videos and Stories from around the country.",
"link": "http://www.cnn.com/us"
},
{
"url": "",
"title": "<b>CNN</b> (#<b>CNN</b>) | Twitter",
"contentSnippet": "The latest Tweets from <b>CNN</b> (#<b>CNN</b>). It's our job to #GoThere and tell the most <br>\ndifficult stories. Come with us!",
"link": "https://twitter.com/cnn"
},
{
"url": "http://gdata.youtube.com/feeds/base/users/CNN/uploads?alt=rss&v=2&orderby=published&client=ytapi-youtube-profile",
"title": "<b>CNN</b> - YouTube",
"contentSnippet": "<b>CNN</b> operates as a division of Turner Broadcasting System, which is a subsidiary <br>\nof Time Warner. <b>CNN</b> identifies itself as -- and is widely known to be - the m...",
"link": "http://www.youtube.com/user/CNN"
}
]
},
"responseDetails": null,
"responseStatus": 200
}
The first 4 results don't have a URL attached. The 5th one does, but isn't relevant, so my initial idea to remove items with empty URLs wouldn't work.
Following the basic query example on their developer guide, their example shows the url field of the results filled in. Copy and pasting the URL they provided shows similar results, but without the url field filled out.
Unless I'm mistaken, this seems like it must be a problem with Google's results.
I've been playing with the google feed API for a podcasts I run and wanted to include a simple ng-repeat to display the title and link URL to the MP3. However the JSON google provides is nested in several different Objects and Arrays. For instance, my JSON feed looks like this:
{
"responseData": {
"feed": {
"feedUrl": "http://feeds.feedburner.com/stillgotgame",
"title": "2old2play presents Still Got Game",
"link": "http://www.2old2play.com/",
"author": "",
"description": "Still Got Game focuses on the gaming industry from the perspective of adult gamers. We look at news, reviews, and inside information in the world of video games. Each episode touches on the community, the industry, and the games that keep us coming back.",
"type": "rss20",
"entries": [
{
"mediaGroups": [
{
"contents": [
{
"url": "http://traffic.libsyn.com/dsmooth/Still_Got_Game_Episode_33__Coast_to_Coast.mp3",
"fileSize": "35346436",
"type": "audio/mpeg"
}
]
}
],
"title": "Still Got Game Ep. 33: Coast to Coast",
"link": "http://2old2play.com/media/still-got-game-ep-33-coast-coast/",
"author": "podcast#2old2play.com",
"publishedDate": "Tue, 06 May 2014 22:05:01 -0700",
"contentSnippet": "DSmooth finally has his Rocket Bro back. After a multi-week hiatus for Doodirock's move to the West Coast, they boys were back ...",
"content": "DSmooth finally has his Rocket Bro back. After a multi-week hiatus for Doodirock's move to the West Coast, they boys were back in force this week. The duo talk gaming news and the new releases, cover a bunch of viewer feedback, and talk a bit about what may be the worst moving company ever. They'll have you LMFAOing! You can always call the boys at (773) 527-2961 and weigh in yourself, or tune in live Monday nights at 9:00 EDT at http://twitch.tv/2old2play ...",
"categories": [
"Audio"
]
}
]
}
},
"responseDetails": null,
"responseStatus": 200
}
As you can see, in order to get to the items URL to the MP3 I have to go through entries, mediaGroups, and Contents before I even reach the Array I need! I start off inside the entries with this factory I've created:
.factory('audioFEED', function($resource){
return $resource('http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=100&q=http://feeds.feedburner.com/stillgotgame',{},
{
query:{
method:'JSONP',
params: {callback: 'JSON_CALLBACK'},
isArray:false,
headers:{
'Access-Control-Allow-Origin': '*'
}
},
});
});
Thats easy enough with just setting up the data on the controller here:
'use strict';
angular.module('twitchappApp')
.controller('audioCtrl', function($scope, audioFEED) {
audioFEED.query(function(data){
$scope.audios = data.responseData.feed.entries;
console.log($scope.audios);
});
});
However, In order to get to that data I'm having to set up multiple ng-repeats with on inside of the next. I would really like to find a better way to handle this data within the controller and access the URL inside one ng-repeat. It seems this way is adding more over head and probably not the best over all method. Is there a best practice for this? My current end result looks like this:
<h1>Audio</h1>
<div ng-repeat="audio in audios">
<h3>{{ audio.title }}</h3>
<p>{{audio.contentSnippet}}</p>
<div ng-repeat="play in audio.mediaGroups">
<div ng-repeat="playurl in play.contents">
PLAY
</div>
</div>
</div>
Yuk...
Check out this JSFiddle. Uses underscore to flatten your data down to an easier to work with array. http://jsfiddle.net/ahchurch/sKeY9/3/
Template
<div ng-controller="MyCtrl">
<div ng-repeat="playurl in contents">
PLAY
</div>
</div>
JavaScript
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
var responseData = {
"responseData": {
"feed": {
"feedUrl": "http://feeds.feedburner.com/stillgotgame",
"title": "2old2play presents Still Got Game",
"link": "http://www.2old2play.com/",
"author": "",
"description": "Still Got Game focuses on the gaming industry from the perspective of adult gamers. We look at news, reviews, and inside information in the world of video games. Each episode touches on the community, the industry, and the games that keep us coming back.",
"type": "rss20",
"entries": [
{
"mediaGroups": [
{
"contents": [
{
"url": "http://traffic.libsyn.com/dsmooth/Still_Got_Game_Episode_33__Coast_to_Coast.mp3",
"fileSize": "35346436",
"type": "audio/mpeg"
}
]
}
],
"title": "Still Got Game Ep. 33: Coast to Coast",
"link": "http://2old2play.com/media/still-got-game-ep-33-coast-coast/",
"author": "podcast#2old2play.com",
"publishedDate": "Tue, 06 May 2014 22:05:01 -0700",
"contentSnippet": "DSmooth finally has his Rocket Bro back. After a multi-week hiatus for Doodirock's move to the West Coast, they boys were back ...",
"content": "DSmooth finally has his Rocket Bro back. After a multi-week hiatus for Doodirock's move to the West Coast, they boys were back in force this week. The duo talk gaming news and the new releases, cover a bunch of viewer feedback, and talk a bit about what may be the worst moving company ever. They'll have you LMFAOing! You can always call the boys at (773) 527-2961 and weigh in yourself, or tune in live Monday nights at 9:00 EDT at http://twitch.tv/2old2play ...",
"categories": [
"Audio"
]
}
]
}
},
"responseDetails": null,
"responseStatus": 200
};
//Underscore:
$scope.contents = _.flatten(_.map(responseData.responseData.feed.entries, function(entry){
return _.map(entry.mediaGroups, function(mediaGroup){
return mediaGroup.contents;
});
}));
$scope.name = 'Superhero';
}