ralucas / node-zillow

Node wrapper for Zillow API
https://ralucas.github.io/node-zillow
MIT License
70 stars 27 forks source link

undocumented GetResults #13

Closed konsumer closed 6 years ago

konsumer commented 8 years ago

I'd like to use the undocumented http://www.zillow.com/search/GetResults.htm which is available in this python lib. It can do fairly complicated searching for houses, based on the params you would enter on the website. I have the start of a parameter builder, and would be happy to PR for it, once it's a bit more cleaned up, if there is interest. As I look at the rest of this API, methods are basically just 1:1 maps of params, but the GetResults params are pretty ugly, so it might make more sense to add a mapping function for it.

konsumer commented 8 years ago

Here is what I am currently using to allow for nicer input params, to give an idea of the sort of thing I mean:

function binOption (defaults, options) {
  var out = defaults.toString().split('')
  options.forEach((o, i) => {
    if (o !== undefined) {
      out[i] = o ? 1 : 0
    }
  })
  return out.join('')
}

function zilloSearchParams (options) {
  options.size = options.size || [0, '']
  options.price = options.price || [0, '']
  options.baths = options.baths || [0, '']
  options.lot_size = options.lot_size || [0, '']
  options.year = options.year || [0, '']
  options.bedrooms = options.bedrooms || [0, '']
  options.days = options.days || [0, '']

  const size = [options.size[0] || 0, options.size[1] || ''].join(',')
  const coords = options.coords.map(i => Math.floor(i * 1000000)).join(',')
  const price = [options.price[0] || 0, options.price[1] || ''].join(',')
  const baths = [options.baths[0] || 0, options.baths[1] || ''].join(',')
  const lotSize = [options.lot_size[0] || 0, options.lot_size[1] || ''].join(',')
  const year = [options.year[0] || 0, options.year[1] || ''].join(',')
  const bedrooms = [options.bedrooms[0] || 0, options.bedrooms[1] || ''].join(',')
  const days = options.days || 'any'

  // "By Agent", "By Owner", "Foreclosures", "New Homes", "Open Houses Only", "Coming Soon."
  const lt = binOption(
    111101,
    [
      options.byAgent,
      options.byOwner,
      options.foreclosed,
      options.newHome,
      options.openHouse,
      options.comingSoon
    ]
  )

  // Status of home. A 6 digit binary number.
  // input of 111011 means search for all houses
  // (Set to 1 to search "For Sale"), "Make me move", "Recently Sold", (Next bit seems unused), "For Rent", (Set to 1 if you want to search for foreclosure properties)
  const status = binOption(
    111011,
    [
      options.forSale,
      options.makeMeMove,
      options.recentlySold,
      0,
      options.forRent,
      options.foreclosed
    ]
  )

  // Home Type. A 6 digit binary number. 111111 means search for
  // "Houses", "Condos", "Apartments", "Manufactured", "Lots/Land", "Townhomes"
  const ht = binOption(
    111111,
    [
      options.houses,
      options.condos,
      options.apartments,
      options.manufactured,
      options.lots,
      options.townhomes
    ]
  )

  if (options.foreclosed === undefined) {
    options.foreclosed = true
  }

  return {
    bd: bedrooms,                        // Bedrooms (number plus) eg input of "1," means 1 bedroom and up
    days: days,                          // Number of days on market. Select "any" for any number of days
    pmf: options.foreclosed ? 1 : 0,     // Search for foreclosed properties (0/1)
    parking: options.parking ? 1 : 0,    // On site-parking (rentals only) (0/1)
    laundry: options.laundry ? 1 : 0,    // In unit laundry (rentals only) (0/1)
    lt: lt,                              // List Type. A 6 digit binary number for filtering by for sale 111111 would mean search for all for sale "By Agent", "By Owner", "Foreclosures", "New Homes", "Open Houses Only", "Coming Soon."
    pf: options.pre_foreclosure ? 1 : 0, // Search for properties in pre-foreclosure (0/1)
    pets: options.pets ? 1 : 0,          // Accepts pets (rentals only) (0/1)
    status: status,                      // Status of home. A 6 digit binary number. input of 111011 means search for all houses (Set to 1 to search "For Sale"), "Make me move", "Recently Sold", (Next bit seems unused), "For Rent", (Set to 1 if you want to search for foreclosure properties)
    pr: price,                           // Price (number plus) eg input of 50000 means 50000 and up
    ba: baths,                           // Bathrooms (number plus)
    ht: ht,                              // Home Type. A 6 digit binary number. 111111 means search for "Houses", "Condos", "Apartments", "Manufactured", "Lots/Land", "Townhomes"
    sort: options.sort || 'days',        // Sort by choice of (days/featured/priced/pricea/lot/built/size/baths/beds/zest/zesta)
    lot: lotSize,                        // Lot size "<min>,<max>"
    yr: year,                            // Year build "<min>,<max>"
    rect: coords,                        // top-left bottom-right lat/long, somma-seperated
    zoom: options.zoom || 11,            // The zoom of the map.
    sf: size,                            // Square feet "<min>,<max>". If either min or max not set just leave blank but keep comma

    spt: 'homes',                        // Seems to be "homes" by default
    pnd: 0,                              // ?? 0 seems to be default
    rt: 6,                               // ?? 6 seems to be default
    ds: 'all',                           // ?? "all" seems to be default
    p: 1,                                // ?? 1 seems to be default
    zso: 0,                              // ?? 0 seems to be default
    red: 0,                              // ?? 0 seems to be default
    pho: '0,',                           // ?? 0 seems to be default
    search: 'maplist',
    mp: ','
  }
}
ralucas commented 8 years ago

@konsumer, How are you? Yes, the creation of the request url just takes a JS object (hash table) of the params and builds the query string based on those key/value pairs. What did you have in mind as alternative? Obviously the GetResults can take a lot of parameters, but It still looks like key/value associations are going to be necessary to build the url. Thoughts?

Best,

Richard

konsumer commented 8 years ago

I feel like we could just add it as-is and document all the obtuse parameters, or use the parameter builder function above and have nicer options. When I have some time, I'll PR and set it all up.

konsumer commented 6 years ago

Is there interest in adding the above code for mapping params? I forgot about this, but I am still happy to make a PR for it.

blaskovicz commented 6 years ago

@konsumer yes please ;)

blaskovicz commented 6 years ago

@konsumer notes after some trial and error on my end:

The rect query params in this format: bottomLeft.long,bottomLeft.lat,topRight.long,topRight.lat

Given two LatLong markers, I developed some code to fix them into zillow's eight-digit-no-decimal format:

  let rect: string = "";
  let tokenLength: number = 0;
  for (const char of `${bottomLeft.longitude},${bottomLeft.latitude},${
    topRight.longitude
  },${topRight.latitude}`) {
    if (char === ".") {
      continue;
    } else if (char === "-") {
      tokenLength -= 1;
    } else if (char === ",") {
      tokenLength = -1;
    } else if (tokenLength === 8) {
      continue;
    }
    rect += char;
    tokenLength += 1;
  }

https://www.carlyzach.com/functions/playground/0a31a2ffadc563a8c491ad59ecde49cc?language=javascript

konsumer commented 6 years ago

I'm not using this lib anymore, but feel free to use above code to get you started.

blaskovicz commented 6 years ago

When and if #20 gets merged I'll do that. I figured out some other options and was able to start using it on https://house-rank.carlyzach.com.

See https://github.com/blaskovicz/house-rank-api/commit/bf7f1634bf0e1b8c1dc2bbfa66f08cb137cb5006#diff-648bbbd558888fd428674ecdc24e44aeR172