Compare commits

..

4 Commits

Author SHA1 Message Date
zacharyhampton
6f3faceb27 Version bump to 0.8.14
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 14:32:59 -07:00
zacharyhampton
cab0216f29 Version bump to 0.8.13
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 12:30:46 -07:00
zacharyhampton
8ee720ce5c Version bump to 0.8.12
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 15:30:26 -07:00
zacharyhampton
8eb138ee1a Version bump to 0.8.11
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 22:42:01 -07:00
4 changed files with 13 additions and 10 deletions

View File

@@ -5,6 +5,7 @@ import requests
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry from urllib3.util.retry import Retry
import uuid import uuid
import secrets
from ...exceptions import AuthenticationError from ...exceptions import AuthenticationError
from .models import Property, ListingType, SiteName, SearchPropertyType, ReturnType from .models import Property, ListingType, SiteName, SearchPropertyType, ReturnType
import json import json
@@ -82,14 +83,15 @@ class Scraper:
Scraper.session.headers.update( Scraper.session.headers.update(
{ {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'apollographql-client-version': '26.11.0-26.11.0.0249758', 'apollographql-client-version': '26.11.1-26.11.1.1106489',
'Accept': '*/*', 'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9', 'Accept-Language': 'en-US,en;q=0.9',
'rdc-client-version': '26.11.0', 'rdc-client-version': '26.11.1',
'X-APOLLO-OPERATION-TYPE': 'query', 'X-APOLLO-OPERATION-TYPE': 'query',
'X-APOLLO-OPERATION-ID': secrets.token_hex(32),
'rdc-client-name': 'RDC_NATIVE_MOBILE-iPhone-com.move.Realtor', 'rdc-client-name': 'RDC_NATIVE_MOBILE-iPhone-com.move.Realtor',
'apollographql-client-name': 'com.move.Realtor-apollo-ios', 'apollographql-client-name': 'com.move.Realtor-apollo-ios',
'User-Agent': 'Realtor.com/26.11.0.0249758 CFNetwork/3860.200.71 Darwin/25.1.0', 'User-Agent': 'Realtor.com/26.11.1.1106489 CFNetwork/3860.200.71 Darwin/25.1.0',
} }
) )

View File

@@ -17,6 +17,7 @@ from typing import Dict, Union
from tenacity import ( from tenacity import (
retry, retry,
retry_if_exception_type, retry_if_exception_type,
retry_if_not_exception_type,
wait_exponential, wait_exponential,
stop_after_attempt, stop_after_attempt,
) )
@@ -71,7 +72,7 @@ class RealtorScraper(Scraper):
"variables": variables, "variables": variables,
} }
response = self.session.post(self.SEARCH_GQL_URL, json=payload) response = self.session.post(self.SEARCH_GQL_URL, data=json.dumps(payload, separators=(',', ':')))
if response.status_code == 403: if response.status_code == 403:
if not self.proxy: if not self.proxy:
@@ -1110,7 +1111,7 @@ class RealtorScraper(Scraper):
@retry( @retry(
retry=retry_if_exception_type((JSONDecodeError, Exception)), retry=retry_if_exception_type((JSONDecodeError, Exception)) & retry_if_not_exception_type(AuthenticationError),
wait=wait_exponential(multiplier=1, min=1, max=10), wait=wait_exponential(multiplier=1, min=1, max=10),
stop=stop_after_attempt(3), stop=stop_after_attempt(3),
) )
@@ -1125,16 +1126,16 @@ class RealtorScraper(Scraper):
property_ids = list(set(property_ids)) property_ids = list(set(property_ids))
fragments = "\n".join( fragments = "\n".join(
f'home_{property_id}: home(property_id: {property_id}) {{ ...HomeDetailsFragment }}' f'home_{property_id}: home(property_id: {property_id}) {{ ...SearchFragment }}'
for property_id in property_ids for property_id in property_ids
) )
query = f"""{HOME_FRAGMENT} query = f"""{HOME_FRAGMENT}
query GetHomeDetails {{ query GetHome {{
{fragments} {fragments}
}}""" }}"""
data = self._graphql_post(query, {}, "GetHomeDetails") data = self._graphql_post(query, {}, "GetHome")
if "data" not in data or data["data"] is None: if "data" not in data or data["data"] is None:
# If we got a 400 error with "Required parameter is missing", raise to trigger retry # If we got a 400 error with "Required parameter is missing", raise to trigger retry

View File

@@ -371,7 +371,7 @@ _SEARCH_HOMES_DATA_BASE = """{
HOME_FRAGMENT = """ HOME_FRAGMENT = """
fragment HomeDetailsFragment on Home { fragment SearchFragment on Home {
__typename __typename
pending_date pending_date
listing_id listing_id

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "homeharvest" name = "homeharvest"
version = "0.8.10" version = "0.8.14"
description = "Real estate scraping library" description = "Real estate scraping library"
authors = ["Zachary Hampton <zachary@bunsly.com>", "Cullen Watson <cullen@bunsly.com>"] authors = ["Zachary Hampton <zachary@bunsly.com>", "Cullen Watson <cullen@bunsly.com>"]
homepage = "https://github.com/ZacharyHampton/HomeHarvest" homepage = "https://github.com/ZacharyHampton/HomeHarvest"