@ -0,0 +1,247 @@ |
|||||||
|
<# |
||||||
|
.Synopsis |
||||||
|
Activate a Python virtual environment for the current PowerShell session. |
||||||
|
|
||||||
|
.Description |
||||||
|
Pushes the python executable for a virtual environment to the front of the |
||||||
|
$Env:PATH environment variable and sets the prompt to signify that you are |
||||||
|
in a Python virtual environment. Makes use of the command line switches as |
||||||
|
well as the `pyvenv.cfg` file values present in the virtual environment. |
||||||
|
|
||||||
|
.Parameter VenvDir |
||||||
|
Path to the directory that contains the virtual environment to activate. The |
||||||
|
default value for this is the parent of the directory that the Activate.ps1 |
||||||
|
script is located within. |
||||||
|
|
||||||
|
.Parameter Prompt |
||||||
|
The prompt prefix to display when this virtual environment is activated. By |
||||||
|
default, this prompt is the name of the virtual environment folder (VenvDir) |
||||||
|
surrounded by parentheses and followed by a single space (ie. '(.venv) '). |
||||||
|
|
||||||
|
.Example |
||||||
|
Activate.ps1 |
||||||
|
Activates the Python virtual environment that contains the Activate.ps1 script. |
||||||
|
|
||||||
|
.Example |
||||||
|
Activate.ps1 -Verbose |
||||||
|
Activates the Python virtual environment that contains the Activate.ps1 script, |
||||||
|
and shows extra information about the activation as it executes. |
||||||
|
|
||||||
|
.Example |
||||||
|
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv |
||||||
|
Activates the Python virtual environment located in the specified location. |
||||||
|
|
||||||
|
.Example |
||||||
|
Activate.ps1 -Prompt "MyPython" |
||||||
|
Activates the Python virtual environment that contains the Activate.ps1 script, |
||||||
|
and prefixes the current prompt with the specified string (surrounded in |
||||||
|
parentheses) while the virtual environment is active. |
||||||
|
|
||||||
|
.Notes |
||||||
|
On Windows, it may be required to enable this Activate.ps1 script by setting the |
||||||
|
execution policy for the user. You can do this by issuing the following PowerShell |
||||||
|
command: |
||||||
|
|
||||||
|
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser |
||||||
|
|
||||||
|
For more information on Execution Policies: |
||||||
|
https://go.microsoft.com/fwlink/?LinkID=135170 |
||||||
|
|
||||||
|
#> |
||||||
|
Param( |
||||||
|
[Parameter(Mandatory = $false)] |
||||||
|
[String] |
||||||
|
$VenvDir, |
||||||
|
[Parameter(Mandatory = $false)] |
||||||
|
[String] |
||||||
|
$Prompt |
||||||
|
) |
||||||
|
|
||||||
|
<# Function declarations --------------------------------------------------- #> |
||||||
|
|
||||||
|
<# |
||||||
|
.Synopsis |
||||||
|
Remove all shell session elements added by the Activate script, including the |
||||||
|
addition of the virtual environment's Python executable from the beginning of |
||||||
|
the PATH variable. |
||||||
|
|
||||||
|
.Parameter NonDestructive |
||||||
|
If present, do not remove this function from the global namespace for the |
||||||
|
session. |
||||||
|
|
||||||
|
#> |
||||||
|
function global:deactivate ([switch]$NonDestructive) { |
||||||
|
# Revert to original values |
||||||
|
|
||||||
|
# The prior prompt: |
||||||
|
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { |
||||||
|
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt |
||||||
|
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT |
||||||
|
} |
||||||
|
|
||||||
|
# The prior PYTHONHOME: |
||||||
|
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { |
||||||
|
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME |
||||||
|
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME |
||||||
|
} |
||||||
|
|
||||||
|
# The prior PATH: |
||||||
|
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { |
||||||
|
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH |
||||||
|
Remove-Item -Path Env:_OLD_VIRTUAL_PATH |
||||||
|
} |
||||||
|
|
||||||
|
# Just remove the VIRTUAL_ENV altogether: |
||||||
|
if (Test-Path -Path Env:VIRTUAL_ENV) { |
||||||
|
Remove-Item -Path env:VIRTUAL_ENV |
||||||
|
} |
||||||
|
|
||||||
|
# Just remove VIRTUAL_ENV_PROMPT altogether. |
||||||
|
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { |
||||||
|
Remove-Item -Path env:VIRTUAL_ENV_PROMPT |
||||||
|
} |
||||||
|
|
||||||
|
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: |
||||||
|
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { |
||||||
|
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force |
||||||
|
} |
||||||
|
|
||||||
|
# Leave deactivate function in the global namespace if requested: |
||||||
|
if (-not $NonDestructive) { |
||||||
|
Remove-Item -Path function:deactivate |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
<# |
||||||
|
.Description |
||||||
|
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the |
||||||
|
given folder, and returns them in a map. |
||||||
|
|
||||||
|
For each line in the pyvenv.cfg file, if that line can be parsed into exactly |
||||||
|
two strings separated by `=` (with any amount of whitespace surrounding the =) |
||||||
|
then it is considered a `key = value` line. The left hand string is the key, |
||||||
|
the right hand is the value. |
||||||
|
|
||||||
|
If the value starts with a `'` or a `"` then the first and last character is |
||||||
|
stripped from the value before being captured. |
||||||
|
|
||||||
|
.Parameter ConfigDir |
||||||
|
Path to the directory that contains the `pyvenv.cfg` file. |
||||||
|
#> |
||||||
|
function Get-PyVenvConfig( |
||||||
|
[String] |
||||||
|
$ConfigDir |
||||||
|
) { |
||||||
|
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" |
||||||
|
|
||||||
|
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). |
||||||
|
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue |
||||||
|
|
||||||
|
# An empty map will be returned if no config file is found. |
||||||
|
$pyvenvConfig = @{ } |
||||||
|
|
||||||
|
if ($pyvenvConfigPath) { |
||||||
|
|
||||||
|
Write-Verbose "File exists, parse `key = value` lines" |
||||||
|
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath |
||||||
|
|
||||||
|
$pyvenvConfigContent | ForEach-Object { |
||||||
|
$keyval = $PSItem -split "\s*=\s*", 2 |
||||||
|
if ($keyval[0] -and $keyval[1]) { |
||||||
|
$val = $keyval[1] |
||||||
|
|
||||||
|
# Remove extraneous quotations around a string value. |
||||||
|
if ("'""".Contains($val.Substring(0, 1))) { |
||||||
|
$val = $val.Substring(1, $val.Length - 2) |
||||||
|
} |
||||||
|
|
||||||
|
$pyvenvConfig[$keyval[0]] = $val |
||||||
|
Write-Verbose "Adding Key: '$($keyval[0])'='$val'" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return $pyvenvConfig |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
<# Begin Activate script --------------------------------------------------- #> |
||||||
|
|
||||||
|
# Determine the containing directory of this script |
||||||
|
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition |
||||||
|
$VenvExecDir = Get-Item -Path $VenvExecPath |
||||||
|
|
||||||
|
Write-Verbose "Activation script is located in path: '$VenvExecPath'" |
||||||
|
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" |
||||||
|
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" |
||||||
|
|
||||||
|
# Set values required in priority: CmdLine, ConfigFile, Default |
||||||
|
# First, get the location of the virtual environment, it might not be |
||||||
|
# VenvExecDir if specified on the command line. |
||||||
|
if ($VenvDir) { |
||||||
|
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" |
||||||
|
} |
||||||
|
else { |
||||||
|
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." |
||||||
|
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") |
||||||
|
Write-Verbose "VenvDir=$VenvDir" |
||||||
|
} |
||||||
|
|
||||||
|
# Next, read the `pyvenv.cfg` file to determine any required value such |
||||||
|
# as `prompt`. |
||||||
|
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir |
||||||
|
|
||||||
|
# Next, set the prompt from the command line, or the config file, or |
||||||
|
# just use the name of the virtual environment folder. |
||||||
|
if ($Prompt) { |
||||||
|
Write-Verbose "Prompt specified as argument, using '$Prompt'" |
||||||
|
} |
||||||
|
else { |
||||||
|
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" |
||||||
|
if ($pyvenvCfg -and $pyvenvCfg['prompt']) { |
||||||
|
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" |
||||||
|
$Prompt = $pyvenvCfg['prompt']; |
||||||
|
} |
||||||
|
else { |
||||||
|
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" |
||||||
|
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" |
||||||
|
$Prompt = Split-Path -Path $venvDir -Leaf |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Write-Verbose "Prompt = '$Prompt'" |
||||||
|
Write-Verbose "VenvDir='$VenvDir'" |
||||||
|
|
||||||
|
# Deactivate any currently active virtual environment, but leave the |
||||||
|
# deactivate function in place. |
||||||
|
deactivate -nondestructive |
||||||
|
|
||||||
|
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine |
||||||
|
# that there is an activated venv. |
||||||
|
$env:VIRTUAL_ENV = $VenvDir |
||||||
|
|
||||||
|
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { |
||||||
|
|
||||||
|
Write-Verbose "Setting prompt to '$Prompt'" |
||||||
|
|
||||||
|
# Set the prompt to include the env name |
||||||
|
# Make sure _OLD_VIRTUAL_PROMPT is global |
||||||
|
function global:_OLD_VIRTUAL_PROMPT { "" } |
||||||
|
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT |
||||||
|
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt |
||||||
|
|
||||||
|
function global:prompt { |
||||||
|
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " |
||||||
|
_OLD_VIRTUAL_PROMPT |
||||||
|
} |
||||||
|
$env:VIRTUAL_ENV_PROMPT = $Prompt |
||||||
|
} |
||||||
|
|
||||||
|
# Clear PYTHONHOME |
||||||
|
if (Test-Path -Path Env:PYTHONHOME) { |
||||||
|
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME |
||||||
|
Remove-Item -Path Env:PYTHONHOME |
||||||
|
} |
||||||
|
|
||||||
|
# Add the venv to the PATH |
||||||
|
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH |
||||||
|
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
# This file must be used with "source bin/activate" *from bash* |
||||||
|
# You cannot run it directly |
||||||
|
|
||||||
|
deactivate () { |
||||||
|
# reset old environment variables |
||||||
|
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then |
||||||
|
PATH="${_OLD_VIRTUAL_PATH:-}" |
||||||
|
export PATH |
||||||
|
unset _OLD_VIRTUAL_PATH |
||||||
|
fi |
||||||
|
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then |
||||||
|
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" |
||||||
|
export PYTHONHOME |
||||||
|
unset _OLD_VIRTUAL_PYTHONHOME |
||||||
|
fi |
||||||
|
|
||||||
|
# Call hash to forget past commands. Without forgetting |
||||||
|
# past commands the $PATH changes we made may not be respected |
||||||
|
hash -r 2> /dev/null |
||||||
|
|
||||||
|
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then |
||||||
|
PS1="${_OLD_VIRTUAL_PS1:-}" |
||||||
|
export PS1 |
||||||
|
unset _OLD_VIRTUAL_PS1 |
||||||
|
fi |
||||||
|
|
||||||
|
unset VIRTUAL_ENV |
||||||
|
unset VIRTUAL_ENV_PROMPT |
||||||
|
if [ ! "${1:-}" = "nondestructive" ] ; then |
||||||
|
# Self destruct! |
||||||
|
unset -f deactivate |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
# unset irrelevant variables |
||||||
|
deactivate nondestructive |
||||||
|
|
||||||
|
# on Windows, a path can contain colons and backslashes and has to be converted: |
||||||
|
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then |
||||||
|
# transform D:\path\to\venv to /d/path/to/venv on MSYS |
||||||
|
# and to /cygdrive/d/path/to/venv on Cygwin |
||||||
|
export VIRTUAL_ENV=$(cygpath /home/f2256342/forge/webscraper/.venv) |
||||||
|
else |
||||||
|
# use the path as-is |
||||||
|
export VIRTUAL_ENV=/home/f2256342/forge/webscraper/.venv |
||||||
|
fi |
||||||
|
|
||||||
|
_OLD_VIRTUAL_PATH="$PATH" |
||||||
|
PATH="$VIRTUAL_ENV/"bin":$PATH" |
||||||
|
export PATH |
||||||
|
|
||||||
|
# unset PYTHONHOME if set |
||||||
|
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) |
||||||
|
# could use `if (set -u; : $PYTHONHOME) ;` in bash |
||||||
|
if [ -n "${PYTHONHOME:-}" ] ; then |
||||||
|
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" |
||||||
|
unset PYTHONHOME |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then |
||||||
|
_OLD_VIRTUAL_PS1="${PS1:-}" |
||||||
|
PS1='(.venv) '"${PS1:-}" |
||||||
|
export PS1 |
||||||
|
VIRTUAL_ENV_PROMPT='(.venv) ' |
||||||
|
export VIRTUAL_ENV_PROMPT |
||||||
|
fi |
||||||
|
|
||||||
|
# Call hash to forget past commands. Without forgetting |
||||||
|
# past commands the $PATH changes we made may not be respected |
||||||
|
hash -r 2> /dev/null |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
# This file must be used with "source bin/activate.csh" *from csh*. |
||||||
|
# You cannot run it directly. |
||||||
|
|
||||||
|
# Created by Davide Di Blasi <davidedb@gmail.com>. |
||||||
|
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com> |
||||||
|
|
||||||
|
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' |
||||||
|
|
||||||
|
# Unset irrelevant variables. |
||||||
|
deactivate nondestructive |
||||||
|
|
||||||
|
setenv VIRTUAL_ENV /home/f2256342/forge/webscraper/.venv |
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PATH="$PATH" |
||||||
|
setenv PATH "$VIRTUAL_ENV/"bin":$PATH" |
||||||
|
|
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PROMPT="$prompt" |
||||||
|
|
||||||
|
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then |
||||||
|
set prompt = '(.venv) '"$prompt" |
||||||
|
setenv VIRTUAL_ENV_PROMPT '(.venv) ' |
||||||
|
endif |
||||||
|
|
||||||
|
alias pydoc python -m pydoc |
||||||
|
|
||||||
|
rehash |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
# This file must be used with "source <venv>/bin/activate.fish" *from fish* |
||||||
|
# (https://fishshell.com/). You cannot run it directly. |
||||||
|
|
||||||
|
function deactivate -d "Exit virtual environment and return to normal shell environment" |
||||||
|
# reset old environment variables |
||||||
|
if test -n "$_OLD_VIRTUAL_PATH" |
||||||
|
set -gx PATH $_OLD_VIRTUAL_PATH |
||||||
|
set -e _OLD_VIRTUAL_PATH |
||||||
|
end |
||||||
|
if test -n "$_OLD_VIRTUAL_PYTHONHOME" |
||||||
|
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME |
||||||
|
set -e _OLD_VIRTUAL_PYTHONHOME |
||||||
|
end |
||||||
|
|
||||||
|
if test -n "$_OLD_FISH_PROMPT_OVERRIDE" |
||||||
|
set -e _OLD_FISH_PROMPT_OVERRIDE |
||||||
|
# prevents error when using nested fish instances (Issue #93858) |
||||||
|
if functions -q _old_fish_prompt |
||||||
|
functions -e fish_prompt |
||||||
|
functions -c _old_fish_prompt fish_prompt |
||||||
|
functions -e _old_fish_prompt |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
set -e VIRTUAL_ENV |
||||||
|
set -e VIRTUAL_ENV_PROMPT |
||||||
|
if test "$argv[1]" != "nondestructive" |
||||||
|
# Self-destruct! |
||||||
|
functions -e deactivate |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
# Unset irrelevant variables. |
||||||
|
deactivate nondestructive |
||||||
|
|
||||||
|
set -gx VIRTUAL_ENV /home/f2256342/forge/webscraper/.venv |
||||||
|
|
||||||
|
set -gx _OLD_VIRTUAL_PATH $PATH |
||||||
|
set -gx PATH "$VIRTUAL_ENV/"bin $PATH |
||||||
|
|
||||||
|
# Unset PYTHONHOME if set. |
||||||
|
if set -q PYTHONHOME |
||||||
|
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME |
||||||
|
set -e PYTHONHOME |
||||||
|
end |
||||||
|
|
||||||
|
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" |
||||||
|
# fish uses a function instead of an env var to generate the prompt. |
||||||
|
|
||||||
|
# Save the current fish_prompt function as the function _old_fish_prompt. |
||||||
|
functions -c fish_prompt _old_fish_prompt |
||||||
|
|
||||||
|
# With the original prompt function renamed, we can override with our own. |
||||||
|
function fish_prompt |
||||||
|
# Save the return status of the last command. |
||||||
|
set -l old_status $status |
||||||
|
|
||||||
|
# Output the venv prompt; color taken from the blue of the Python logo. |
||||||
|
printf "%s%s%s" (set_color 4B8BBE) '(.venv) ' (set_color normal) |
||||||
|
|
||||||
|
# Restore the return status of the previous command. |
||||||
|
echo "exit $old_status" | . |
||||||
|
# Output the original/"old" prompt. |
||||||
|
_old_fish_prompt |
||||||
|
end |
||||||
|
|
||||||
|
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" |
||||||
|
set -gx VIRTUAL_ENV_PROMPT '(.venv) ' |
||||||
|
end |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
#!/home/f2256342/forge/webscraper/.venv/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import re |
||||||
|
import sys |
||||||
|
from httpx import main |
||||||
|
if __name__ == '__main__': |
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
#!/home/f2256342/forge/webscraper/.venv/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import re |
||||||
|
import sys |
||||||
|
from idna.cli import main |
||||||
|
if __name__ == '__main__': |
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
#!/home/f2256342/forge/webscraper/.venv/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import re |
||||||
|
import sys |
||||||
|
from pip._internal.cli.main import main |
||||||
|
if __name__ == '__main__': |
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
#!/home/f2256342/forge/webscraper/.venv/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import re |
||||||
|
import sys |
||||||
|
from pip._internal.cli.main import main |
||||||
|
if __name__ == '__main__': |
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
#!/home/f2256342/forge/webscraper/.venv/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import re |
||||||
|
import sys |
||||||
|
from pip._internal.cli.main import main |
||||||
|
if __name__ == '__main__': |
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
#!/home/f2256342/forge/webscraper/.venv/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import re |
||||||
|
import sys |
||||||
|
from playwright.__main__ import main |
||||||
|
if __name__ == '__main__': |
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,164 @@ |
|||||||
|
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ |
||||||
|
|
||||||
|
/* Greenlet object interface */ |
||||||
|
|
||||||
|
#ifndef Py_GREENLETOBJECT_H |
||||||
|
#define Py_GREENLETOBJECT_H |
||||||
|
|
||||||
|
|
||||||
|
#include <Python.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* This is deprecated and undocumented. It does not change. */ |
||||||
|
#define GREENLET_VERSION "1.0.0" |
||||||
|
|
||||||
|
#ifndef GREENLET_MODULE |
||||||
|
#define implementation_ptr_t void* |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct _greenlet { |
||||||
|
PyObject_HEAD |
||||||
|
PyObject* weakreflist; |
||||||
|
PyObject* dict; |
||||||
|
implementation_ptr_t pimpl; |
||||||
|
} PyGreenlet; |
||||||
|
|
||||||
|
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) |
||||||
|
|
||||||
|
|
||||||
|
/* C API functions */ |
||||||
|
|
||||||
|
/* Total number of symbols that are exported */ |
||||||
|
#define PyGreenlet_API_pointers 12 |
||||||
|
|
||||||
|
#define PyGreenlet_Type_NUM 0 |
||||||
|
#define PyExc_GreenletError_NUM 1 |
||||||
|
#define PyExc_GreenletExit_NUM 2 |
||||||
|
|
||||||
|
#define PyGreenlet_New_NUM 3 |
||||||
|
#define PyGreenlet_GetCurrent_NUM 4 |
||||||
|
#define PyGreenlet_Throw_NUM 5 |
||||||
|
#define PyGreenlet_Switch_NUM 6 |
||||||
|
#define PyGreenlet_SetParent_NUM 7 |
||||||
|
|
||||||
|
#define PyGreenlet_MAIN_NUM 8 |
||||||
|
#define PyGreenlet_STARTED_NUM 9 |
||||||
|
#define PyGreenlet_ACTIVE_NUM 10 |
||||||
|
#define PyGreenlet_GET_PARENT_NUM 11 |
||||||
|
|
||||||
|
#ifndef GREENLET_MODULE |
||||||
|
/* This section is used by modules that uses the greenlet C API */ |
||||||
|
static void** _PyGreenlet_API = NULL; |
||||||
|
|
||||||
|
# define PyGreenlet_Type \ |
||||||
|
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) |
||||||
|
|
||||||
|
# define PyExc_GreenletError \ |
||||||
|
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) |
||||||
|
|
||||||
|
# define PyExc_GreenletExit \ |
||||||
|
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_New(PyObject *args) |
||||||
|
* |
||||||
|
* greenlet.greenlet(run, parent=None) |
||||||
|
*/ |
||||||
|
# define PyGreenlet_New \ |
||||||
|
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_New_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_GetCurrent(void) |
||||||
|
* |
||||||
|
* greenlet.getcurrent() |
||||||
|
*/ |
||||||
|
# define PyGreenlet_GetCurrent \ |
||||||
|
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_Throw( |
||||||
|
* PyGreenlet *greenlet, |
||||||
|
* PyObject *typ, |
||||||
|
* PyObject *val, |
||||||
|
* PyObject *tb) |
||||||
|
* |
||||||
|
* g.throw(...) |
||||||
|
*/ |
||||||
|
# define PyGreenlet_Throw \ |
||||||
|
(*(PyObject * (*)(PyGreenlet * self, \
|
||||||
|
PyObject * typ, \
|
||||||
|
PyObject * val, \
|
||||||
|
PyObject * tb)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_Throw_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) |
||||||
|
* |
||||||
|
* g.switch(*args, **kwargs) |
||||||
|
*/ |
||||||
|
# define PyGreenlet_Switch \ |
||||||
|
(*(PyObject * \
|
||||||
|
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_Switch_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) |
||||||
|
* |
||||||
|
* g.parent = new_parent |
||||||
|
*/ |
||||||
|
# define PyGreenlet_SetParent \ |
||||||
|
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_SetParent_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_GetParent(PyObject* greenlet) |
||||||
|
* |
||||||
|
* return greenlet.parent; |
||||||
|
* |
||||||
|
* This could return NULL even if there is no exception active. |
||||||
|
* If it does not return NULL, you are responsible for decrementing the |
||||||
|
* reference count. |
||||||
|
*/ |
||||||
|
# define PyGreenlet_GetParent \ |
||||||
|
(*(PyGreenlet* (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) |
||||||
|
|
||||||
|
/*
|
||||||
|
* deprecated, undocumented alias. |
||||||
|
*/ |
||||||
|
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent |
||||||
|
|
||||||
|
# define PyGreenlet_MAIN \ |
||||||
|
(*(int (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_MAIN_NUM]) |
||||||
|
|
||||||
|
# define PyGreenlet_STARTED \ |
||||||
|
(*(int (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_STARTED_NUM]) |
||||||
|
|
||||||
|
# define PyGreenlet_ACTIVE \ |
||||||
|
(*(int (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Macro that imports greenlet and initializes C API */ |
||||||
|
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
|
||||||
|
keep the older definition to be sure older code that might have a copy of |
||||||
|
the header still works. */ |
||||||
|
# define PyGreenlet_Import() \ |
||||||
|
{ \
|
||||||
|
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
|
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GREENLET_MODULE */ |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
#endif /* !Py_GREENLETOBJECT_H */ |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
home = /usr/bin |
||||||
|
include-system-site-packages = false |
||||||
|
version = 3.12.3 |
||||||
|
executable = /usr/bin/python3.12 |
||||||
|
command = /usr/bin/python3 -m venv /home/f2256342/forge/webscraper/.venv |
||||||
@ -0,0 +1,191 @@ |
|||||||
|
# AGENTS.md — Busca de Imóveis DF |
||||||
|
|
||||||
|
## Finalidade |
||||||
|
|
||||||
|
Este repositório apoia a coleta, organização e análise histórica de anúncios imobiliários do Distrito Federal para uma decisão residencial de longo prazo. |
||||||
|
|
||||||
|
Antes de trabalhar no projeto, leia integralmente: |
||||||
|
|
||||||
|
- `docs/PROJECT_CONTEXT.md` |
||||||
|
- `docs/PROPERTY_CRITERIA.md` |
||||||
|
- `docs/DATA_MODEL.md` |
||||||
|
- `docs/ANALYSIS_RULES.md` |
||||||
|
|
||||||
|
Esses documentos são a referência operacional do projeto. Quando houver conflito: |
||||||
|
|
||||||
|
1. uma instrução explícita e recente do usuário prevalece; |
||||||
|
2. depois, prevalecem estes documentos; |
||||||
|
3. por último, hipóteses inferidas a partir dos dados. |
||||||
|
|
||||||
|
Nunca trate uma hipótese como preferência confirmada. |
||||||
|
|
||||||
|
Para auxiliar no entendimento e contexto, as plantas das casas dos Jardins Mangueiral estão em: |
||||||
|
|
||||||
|
- `resources/plans` |
||||||
|
|
||||||
|
Para auxiliar no entendimento e contexto, foram feitas capturas dos mapas dos Jardins Mangueiral, que foram salvas na em: |
||||||
|
|
||||||
|
- `resources/maps` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Banco de dados |
||||||
|
|
||||||
|
O banco principal deve ser tratado como **estritamente somente leitura**. |
||||||
|
|
||||||
|
### Regras obrigatórias |
||||||
|
|
||||||
|
- Nunca execute `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `REPLACE`, `VACUUM`, `REINDEX`, `ATTACH` ou comandos equivalentes contra o banco original. |
||||||
|
- Não substitua, mova, compacte, migre ou renomeie o arquivo original. |
||||||
|
- Não remova arquivos `-wal` ou `-shm` sem uma instrução explícita. |
||||||
|
- Não execute scripts de scraping contra o banco durante uma análise sem autorização. |
||||||
|
- Use `LIMIT` em consultas exploratórias. |
||||||
|
- Não imprima tabelas inteiras no terminal. |
||||||
|
- Prefira agregações, amostras pequenas e exportações derivadas. |
||||||
|
- Salve resultados em `reports/`, `exports/` ou `data/derived/`. |
||||||
|
- Nunca grave resultados derivados dentro do banco original. |
||||||
|
- Antes de qualquer análise, confirme o caminho do banco e abra-o em modo somente leitura. |
||||||
|
|
||||||
|
### Abertura recomendada em Python |
||||||
|
|
||||||
|
```python |
||||||
|
import sqlite3 |
||||||
|
|
||||||
|
DB_PATH = "data/imoveis.sqlite" |
||||||
|
|
||||||
|
connection = sqlite3.connect( |
||||||
|
f"file:{DB_PATH}?mode=ro&immutable=1", |
||||||
|
uri=True, |
||||||
|
) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
``` |
||||||
|
|
||||||
|
Se o banco estiver sendo atualizado por outro processo, não use `immutable=1`; mantenha `mode=ro` e confirme a consistência do snapshot antes de analisar. |
||||||
|
|
||||||
|
### Cliente de linha de comando |
||||||
|
|
||||||
|
```bash |
||||||
|
sqlite3 -readonly data/imoveis.sqlite |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Fluxo inicial obrigatório |
||||||
|
|
||||||
|
Ao iniciar uma nova tarefa de análise: |
||||||
|
|
||||||
|
1. Identifique o arquivo SQLite correto e seu tamanho. |
||||||
|
2. Leia `sqlite_master` para listar tabelas, views, índices e triggers. |
||||||
|
3. Examine o esquema real antes de escrever consultas analíticas. |
||||||
|
4. Conte registros por tabela. |
||||||
|
5. Identifique campos de data, preço, área, quartos, localização, URL, anunciante e identificadores. |
||||||
|
6. Verifique valores nulos, duplicidades, formatos inconsistentes e possíveis quebras de coleta. |
||||||
|
7. Descubra se os dados representam: |
||||||
|
- anúncios; |
||||||
|
- imóveis físicos; |
||||||
|
- observações históricas do mesmo anúncio; |
||||||
|
- execuções do scraper. |
||||||
|
8. Documente qualquer ambiguidade antes de produzir conclusões. |
||||||
|
|
||||||
|
Não presuma que o modelo lógico descrito em `docs/DATA_MODEL.md` já existe fisicamente. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Princípios de análise |
||||||
|
|
||||||
|
- Diferencie sempre **anúncio**, **imóvel físico** e **observação histórica**. |
||||||
|
- Um mesmo imóvel pode aparecer em vários anúncios, portais, corretores ou datas. |
||||||
|
- Um mesmo anúncio pode mudar de preço, descrição, área informada ou status. |
||||||
|
- Preço anunciado não é preço efetivamente negociado. |
||||||
|
- Ausência de anúncio não prova venda. |
||||||
|
- Remoção de anúncio não prova fechamento de negócio. |
||||||
|
- Campo preenchido pelo anunciante não deve ser considerado confiável sem validação. |
||||||
|
- Não calcule preço por metro quadrado quando a área não for comparável ou confiável. |
||||||
|
- Não misture área privativa, útil, construída e total sem separar os conceitos. |
||||||
|
- Não compare períodos sem verificar mudanças no universo coletado. |
||||||
|
- Não compare regiões sem controlar tipologia, quartos, área, garagem, estado e padrão do imóvel. |
||||||
|
- Sempre informe tamanho da amostra, cobertura temporal, filtros e limitações. |
||||||
|
- Prefira mediana, percentis e intervalos interquartis a médias isoladas. |
||||||
|
- Mostre a sensibilidade das conclusões a outliers, duplicidades e dados ausentes. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Segurança e desempenho |
||||||
|
|
||||||
|
- Nunca exponha credenciais, cookies, tokens, chaves, sessões ou dados pessoais em relatórios. |
||||||
|
- Não envie o banco ou grandes amostras para serviços externos sem autorização explícita. |
||||||
|
- Não tente contornar mecanismos de proteção de sites. |
||||||
|
- Respeite limites de requisição, termos aplicáveis e regras definidas para o scraper. |
||||||
|
- Evite consultas que façam varreduras desnecessárias repetidas em tabelas grandes. |
||||||
|
- Use `EXPLAIN QUERY PLAN` em consultas lentas. |
||||||
|
- Sugira índices apenas como recomendação; não os crie no banco original. |
||||||
|
- Para transformações pesadas, crie um banco derivado ou arquivos Parquet/CSV separados. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Código |
||||||
|
|
||||||
|
- Escreva scripts reproduzíveis, parametrizados e idempotentes. |
||||||
|
- Use caminhos relativos ao repositório. |
||||||
|
- Evite constantes escondidas. |
||||||
|
- Registre os filtros e premissas usados. |
||||||
|
- Inclua tratamento de erros. |
||||||
|
- Use consultas SQL parametrizadas. |
||||||
|
- Adicione testes para regras de normalização, deduplicação e cálculo. |
||||||
|
- Não altere arquivos sem relação com a tarefa. |
||||||
|
- Não faça refatorações amplas sem necessidade. |
||||||
|
- Não invente significado para colunas pouco claras; documente a dúvida. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Saídas esperadas |
||||||
|
|
||||||
|
Relatórios analíticos devem, sempre que aplicável, conter: |
||||||
|
|
||||||
|
1. pergunta respondida; |
||||||
|
2. período e universo analisados; |
||||||
|
3. filtros e exclusões; |
||||||
|
4. tamanho da amostra; |
||||||
|
5. método de deduplicação; |
||||||
|
6. qualidade dos dados; |
||||||
|
7. resultados; |
||||||
|
8. limitações; |
||||||
|
9. implicações para a decisão de compra; |
||||||
|
10. consultas ou scripts usados para reproduzir o resultado. |
||||||
|
|
||||||
|
Use gráficos apenas quando eles melhorarem a interpretação. Todo gráfico deve ter título, unidade, período, tamanho da amostra e definição da métrica. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Contexto imobiliário prioritário |
||||||
|
|
||||||
|
As regiões prioritárias são: |
||||||
|
|
||||||
|
1. Cruzeiro, especialmente Cruzeiro Novo; |
||||||
|
2. Jardins Mangueiral. |
||||||
|
|
||||||
|
Não exclua outras regiões de uma análise exploratória quando forem úteis como referência, mas não desvie o projeto de seu objetivo principal sem instrução. |
||||||
|
|
||||||
|
Para Jardins Mangueiral: |
||||||
|
|
||||||
|
- considerar somente casas com planta original de três quartos; |
||||||
|
- não penalizar automaticamente o fechamento do quintal; |
||||||
|
- avaliar a funcionalidade real da conversão; |
||||||
|
- considerar as preferências de QC descritas em `docs/PROPERTY_CRITERIA.md`; |
||||||
|
- tratar relatos de moradores como evidência anedótica, não como fato conclusivo. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Atualização do contexto |
||||||
|
|
||||||
|
Não altere estes documentos apenas porque encontrou um padrão no banco. |
||||||
|
|
||||||
|
Atualize-os somente quando: |
||||||
|
|
||||||
|
- o usuário corrigir uma premissa; |
||||||
|
- uma preferência for explicitamente confirmada; |
||||||
|
- um valor financeiro for atualizado; |
||||||
|
- uma regra metodológica for aprovada; |
||||||
|
- o esquema real do projeto for consolidado. |
||||||
|
|
||||||
|
Ao atualizar uma premissa financeira, registre a data de referência e preserve o histórico em Git. |
||||||
@ -0,0 +1,216 @@ |
|||||||
|
# Busca de Imóveis DF |
||||||
|
|
||||||
|
Projeto pessoal para coletar, organizar e analisar anúncios imobiliários do Distrito Federal, com foco em uma decisão residencial de longo prazo. |
||||||
|
|
||||||
|
As regiões prioritárias são: |
||||||
|
|
||||||
|
1. Cruzeiro, especialmente Cruzeiro Novo; |
||||||
|
2. Jardins Mangueiral. |
||||||
|
|
||||||
|
O objetivo não é identificar simplesmente o menor preço anunciado. A análise considera funcionalidade, estado do imóvel, risco de reforma, localização, liquidez, custos de aquisição e manutenção e adequação à vida familiar. |
||||||
|
|
||||||
|
## Estado atual |
||||||
|
|
||||||
|
O repositório contém: |
||||||
|
|
||||||
|
- scraper autorizado para o portal DF Imóveis; |
||||||
|
- banco SQLite com anúncios, snapshots de conteúdo, execuções de coleta e fotos; |
||||||
|
- HTMLs brutos para reextração e auditoria; |
||||||
|
- plantas e mapas de apoio do Jardins Mangueiral; |
||||||
|
- relatórios reproduzíveis para priorização de visitas. |
||||||
|
|
||||||
|
Relatórios principais: |
||||||
|
|
||||||
|
- [Top 10 — Cruzeiro Novo](reports/cruzeiro_top10/README.md) |
||||||
|
- [Top 10 — Jardins Mangueiral](reports/mangueiral_top10/README.md) |
||||||
|
- [Cruzeiro Novo x Jardins Mangueiral](reports/cruzeiro_vs_mangueiral/README.md) |
||||||
|
|
||||||
|
Os rankings são prioridades de investigação, não recomendações automáticas de compra nem estimativas de preço negociado. |
||||||
|
|
||||||
|
## Princípios metodológicos |
||||||
|
|
||||||
|
- Diferenciar anúncio, snapshot histórico e imóvel físico provável. |
||||||
|
- Tratar preço como valor pedido, não como preço de transação. |
||||||
|
- Não interpretar retirada de anúncio como prova de venda. |
||||||
|
- Deduplicar republicações e anúncios do mesmo imóvel de forma reversível. |
||||||
|
- Não calcular preço por metro quadrado quando o conceito de área não for comparável. |
||||||
|
- Considerar fotos, descrições e HTML bruto, sem assumir que confirmam estrutura ou documentação. |
||||||
|
- Excluir do ranking de visitas anúncios cuja primeira foto indique “vendido” ou “negócio fechado”, registrando-os em quarentena visual. |
||||||
|
- Para o Jardins Mangueiral, considerar somente casas com planta original provável de três quartos. |
||||||
|
- Não penalizar automaticamente quintais fechados; avaliar sua funcionalidade, ventilação e qualidade construtiva. |
||||||
|
|
||||||
|
As regras completas estão em [docs/ANALYSIS_RULES.md](docs/ANALYSIS_RULES.md) e [docs/PROPERTY_CRITERIA.md](docs/PROPERTY_CRITERIA.md). |
||||||
|
|
||||||
|
## Estrutura do repositório |
||||||
|
|
||||||
|
```text |
||||||
|
. |
||||||
|
├── dfimoveis_scraper.py # coletor principal |
||||||
|
├── search_list.txt # buscas executadas pelo scraper |
||||||
|
├── requirements-dfimoveis.txt # dependências Python |
||||||
|
├── dfimoveis_data/ # banco, fotos, HTML bruto e perfil do navegador |
||||||
|
├── docs/ # contexto, critérios, modelo e regras |
||||||
|
├── reports/ # relatórios e artefatos reproduzíveis |
||||||
|
└── resources/ |
||||||
|
├── maps/ # capturas de mapas do Mangueiral |
||||||
|
└── plans/ # plantas de referência de casas de dois e três quartos |
||||||
|
``` |
||||||
|
|
||||||
|
Documentação essencial: |
||||||
|
|
||||||
|
- [PROJECT_CONTEXT.md](docs/PROJECT_CONTEXT.md): objetivo, contexto residencial e premissas financeiras; |
||||||
|
- [PROPERTY_CRITERIA.md](docs/PROPERTY_CRITERIA.md): critérios funcionais e de visita; |
||||||
|
- [DATA_MODEL.md](docs/DATA_MODEL.md): modelo conceitual desejado; |
||||||
|
- [ACTUAL_SCHEMA.md](docs/ACTUAL_SCHEMA.md): esquema físico observado; |
||||||
|
- [ANALYSIS_RULES.md](docs/ANALYSIS_RULES.md): regras metodológicas; |
||||||
|
- [AGENTS.md](AGENTS.md): instruções operacionais e de segurança. |
||||||
|
|
||||||
|
## Requisitos |
||||||
|
|
||||||
|
- Python 3.12 ou posterior; |
||||||
|
- Google Chrome ou Microsoft Edge para a coleta assistida por navegador; |
||||||
|
- PowerShell para os exemplos abaixo. |
||||||
|
|
||||||
|
Crie um ambiente virtual e instale as dependências: |
||||||
|
|
||||||
|
```powershell |
||||||
|
python -m venv .venv-scraper |
||||||
|
.\.venv-scraper\Scripts\python.exe -m pip install -r requirements-dfimoveis.txt |
||||||
|
``` |
||||||
|
|
||||||
|
As ferramentas de similaridade fotográfica usam Pillow, incluído no arquivo de requisitos. |
||||||
|
|
||||||
|
## Configuração das buscas |
||||||
|
|
||||||
|
O arquivo [search_list.txt](search_list.txt) contém uma URL completa por linha. Atualmente ele cobre: |
||||||
|
|
||||||
|
- apartamentos de três quartos no Cruzeiro Novo; |
||||||
|
- casas anunciadas com três ou quatro quartos no Jardins Mangueiral. |
||||||
|
|
||||||
|
A busca do Mangueiral inclui quatro quartos para ampliar a coleta, mas esses imóveis são excluídos da análise regional. Mesmo três quartos anunciados não comprovam a planta original. |
||||||
|
|
||||||
|
## Executando o scraper |
||||||
|
|
||||||
|
Use apenas em sites e condições para os quais você tenha autorização. Respeite termos aplicáveis, limites de requisição e mecanismos de proteção do portal. |
||||||
|
|
||||||
|
### Navegador iniciado pelo scraper |
||||||
|
|
||||||
|
```powershell |
||||||
|
.\.venv-scraper\Scripts\python.exe .\dfimoveis_scraper.py ` |
||||||
|
--search-file .\search_list.txt ` |
||||||
|
--output .\dfimoveis_data |
||||||
|
``` |
||||||
|
|
||||||
|
### Chrome iniciado manualmente |
||||||
|
|
||||||
|
Quando necessário, inicie uma instância separada do Chrome com depuração remota. Não use o perfil cotidiano do navegador. |
||||||
|
|
||||||
|
```powershell |
||||||
|
& "C:\Program Files\Google\Chrome\Application\chrome.exe" ` |
||||||
|
--remote-debugging-port=9222 ` |
||||||
|
--user-data-dir="E:\forge\python\house-quest\dfimoveis_data\browser-profile" |
||||||
|
``` |
||||||
|
|
||||||
|
Em outro terminal: |
||||||
|
|
||||||
|
```powershell |
||||||
|
.\.venv-scraper\Scripts\python.exe .\dfimoveis_scraper.py ` |
||||||
|
--search-file .\search_list.txt ` |
||||||
|
--output .\dfimoveis_data ` |
||||||
|
--attach-chrome |
||||||
|
``` |
||||||
|
|
||||||
|
Consulte todas as opções com: |
||||||
|
|
||||||
|
```powershell |
||||||
|
.\.venv-scraper\Scripts\python.exe .\dfimoveis_scraper.py --help |
||||||
|
``` |
||||||
|
|
||||||
|
Não execute o scraper enquanto uma análise espera um snapshot estável do banco. |
||||||
|
|
||||||
|
## Banco de dados |
||||||
|
|
||||||
|
O banco principal fica em: |
||||||
|
|
||||||
|
```text |
||||||
|
dfimoveis_data/dfimoveis.sqlite3 |
||||||
|
``` |
||||||
|
|
||||||
|
Ele deve ser tratado como estritamente somente leitura durante análises. Nunca grave resultados derivados dentro dele. |
||||||
|
|
||||||
|
Quando não houver processo de coleta e não existir WAL relevante: |
||||||
|
|
||||||
|
```python |
||||||
|
import sqlite3 |
||||||
|
|
||||||
|
connection = sqlite3.connect( |
||||||
|
"file:dfimoveis_data/dfimoveis.sqlite3?mode=ro&immutable=1", |
||||||
|
uri=True, |
||||||
|
) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
``` |
||||||
|
|
||||||
|
Quando existir `dfimoveis.sqlite3-wal` ou houver possibilidade de atualização concorrente, não use `immutable=1`: |
||||||
|
|
||||||
|
```python |
||||||
|
connection = sqlite3.connect( |
||||||
|
"file:dfimoveis_data/dfimoveis.sqlite3?mode=ro", |
||||||
|
uri=True, |
||||||
|
) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
connection.execute("BEGIN") |
||||||
|
``` |
||||||
|
|
||||||
|
Não remova arquivos `-wal` ou `-shm`. Consulte [ACTUAL_SCHEMA.md](docs/ACTUAL_SCHEMA.md) antes de escrever consultas analíticas. |
||||||
|
|
||||||
|
## Reproduzindo os relatórios |
||||||
|
|
||||||
|
Cada relatório regional contém: |
||||||
|
|
||||||
|
```text |
||||||
|
reports/<relatorio>/ |
||||||
|
├── README.md |
||||||
|
├── analysis.py |
||||||
|
├── photo_similarity.py |
||||||
|
├── query.sql |
||||||
|
├── parameters.json |
||||||
|
└── results.csv |
||||||
|
``` |
||||||
|
|
||||||
|
Extração tabular somente leitura: |
||||||
|
|
||||||
|
```powershell |
||||||
|
.\.venv-scraper\Scripts\python.exe .\reports\cruzeiro_top10\analysis.py |
||||||
|
.\.venv-scraper\Scripts\python.exe .\reports\mangueiral_top10\analysis.py |
||||||
|
``` |
||||||
|
|
||||||
|
Triagem de possíveis duplicatas por similaridade visual: |
||||||
|
|
||||||
|
```powershell |
||||||
|
.\.venv-scraper\Scripts\python.exe .\reports\cruzeiro_top10\photo_similarity.py |
||||||
|
.\.venv-scraper\Scripts\python.exe .\reports\mangueiral_top10\photo_similarity.py |
||||||
|
``` |
||||||
|
|
||||||
|
Similaridade de imagem é apenas um sinal para revisão humana, nunca prova automática de que dois anúncios representam o mesmo imóvel. |
||||||
|
|
||||||
|
## Limitações |
||||||
|
|
||||||
|
- A cobertura atual usa um único portal e poucas datas de coleta. |
||||||
|
- O banco representa anúncios e mudanças de conteúdo, não transações imobiliárias. |
||||||
|
- Localização e campos de anunciante apresentam contaminação de extração documentada. |
||||||
|
- A área estruturada não informa de forma segura se é útil, privativa, construída, total ou de terreno. |
||||||
|
- Fotos podem conter material genérico, itens de outros anúncios e selos de venda. |
||||||
|
- Fotos não confirmam documentação, sistemas elétricos e hidráulicos, impermeabilização, ruído ou conforto térmico. |
||||||
|
- A situação financeira registrada deve ser atualizada antes de proposta, financiamento ou definição de preço máximo. |
||||||
|
|
||||||
|
## Segurança e privacidade |
||||||
|
|
||||||
|
- Não publique credenciais, cookies, tokens, sessões ou perfis do navegador. |
||||||
|
- Não envie o banco ou grandes amostras a serviços externos sem autorização. |
||||||
|
- Não tente contornar mecanismos de proteção de sites. |
||||||
|
- O diretório `dfimoveis_data/` é ignorado pelo Git e deve permanecer local. |
||||||
|
- Revise qualquer artefato derivado antes de versioná-lo para evitar dados pessoais. |
||||||
|
|
||||||
|
## Finalidade |
||||||
|
|
||||||
|
Este projeto apoia uma decisão pessoal de compra residencial. Ele não constitui avaliação imobiliária profissional, parecer jurídico, inspeção de engenharia ou recomendação financeira. |
||||||
@ -0,0 +1,238 @@ |
|||||||
|
# Esquema físico do banco `dfimoveis.sqlite3` |
||||||
|
|
||||||
|
## 1. Escopo e identificação do snapshot |
||||||
|
|
||||||
|
Este documento descreve o esquema observado em 13 de julho de 2026. Ele não altera o modelo conceitual de `docs/DATA_MODEL.md` e não atribui significado não comprovado às colunas. |
||||||
|
|
||||||
|
| Item | Valor | |
||||||
|
|---|---| |
||||||
|
| Caminho | `dfimoveis_data/dfimoveis.sqlite3` | |
||||||
|
| Tamanho | 5.017.600 bytes | |
||||||
|
| SHA-256 | `1ce3128b488c50c993d7328ac6e24b5905f083a7e04e3f636407eea1edd02b9d` | |
||||||
|
| Modificação do arquivo | `2026-07-13 14:20:34.601285975 -03:00` | |
||||||
|
| SQLite | 3.45.1 | |
||||||
|
| Codificação | UTF-8 | |
||||||
|
| `journal_mode` persistido | WAL | |
||||||
|
| Páginas | 1.225 páginas de 4.096 bytes; `freelist_count = 0` | |
||||||
|
| `user_version` / `application_id` | 0 / 0 | |
||||||
|
| Método de abertura | `sqlite3 -readonly` e URI `mode=ro&immutable=1`, sempre com `PRAGMA query_only = ON` | |
||||||
|
|
||||||
|
Não havia arquivo `-wal` ou `-shm` nem processo com o banco aberto na verificação inicial. Isso permitiu tratar o conteúdo como snapshot imutável. `PRAGMA quick_check` retornou `ok` e `PRAGMA foreign_key_check` não retornou violações. Na validação final, o cliente SQLite havia criado os sidecars de runtime `dfimoveis.sqlite3-shm` (32.768 bytes) e `dfimoveis.sqlite3-wal` (vazio), comportamento possível ao abrir em leitura um banco configurado em WAL. Eles não foram removidos. O arquivo principal manteve tamanho, `mtime` e SHA-256 idênticos. |
||||||
|
|
||||||
|
## 2. Visão geral |
||||||
|
|
||||||
|
O banco possui cinco tabelas da aplicação e a tabela interna `sqlite_sequence`. Não há views nem triggers. Os quatro índices existentes são autoíndices criados por chaves primárias ou restrições `UNIQUE`. |
||||||
|
|
||||||
|
```mermaid |
||||||
|
erDiagram |
||||||
|
listings ||--|{ listing_history : "listing_id" |
||||||
|
listings ||--|{ listing_searches : "listing_id" |
||||||
|
listings ||--|{ photos : "listing_id" |
||||||
|
crawl_runs { |
||||||
|
INTEGER id PK |
||||||
|
TEXT started_at |
||||||
|
TEXT finished_at |
||||||
|
TEXT status |
||||||
|
TEXT stats_json |
||||||
|
} |
||||||
|
listings { |
||||||
|
TEXT listing_id PK |
||||||
|
TEXT url |
||||||
|
REAL price_brl |
||||||
|
REAL area_m2 |
||||||
|
TEXT first_seen_at |
||||||
|
TEXT last_seen_at |
||||||
|
TEXT content_hash |
||||||
|
} |
||||||
|
listing_history { |
||||||
|
INTEGER id PK |
||||||
|
TEXT listing_id FK |
||||||
|
TEXT captured_at |
||||||
|
TEXT content_hash |
||||||
|
TEXT data_json |
||||||
|
} |
||||||
|
listing_searches { |
||||||
|
TEXT listing_id PK,FK |
||||||
|
TEXT search_url PK |
||||||
|
TEXT first_seen_at |
||||||
|
TEXT last_seen_at |
||||||
|
} |
||||||
|
photos { |
||||||
|
TEXT listing_id PK,FK |
||||||
|
TEXT source_url PK |
||||||
|
INTEGER ordinal |
||||||
|
TEXT sha256 |
||||||
|
TEXT local_path |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
`crawl_runs` não possui relacionamento físico com anúncios ou snapshots. Portanto, a execução que gerou uma linha só pode ser inferida pelos horários, não recuperada por uma chave. |
||||||
|
|
||||||
|
### Contagens e cardinalidades observadas |
||||||
|
|
||||||
|
| Tabela | Linhas | Interpretação física | |
||||||
|
|---|---:|---| |
||||||
|
| `crawl_runs` | 4 | Execuções do coletor | |
||||||
|
| `listings` | 156 | Identidades de anúncios e seu estado corrente | |
||||||
|
| `listing_history` | 156 | Estados distintos dos anúncios por hash | |
||||||
|
| `listing_searches` | 156 | Vínculos entre anúncio e URL de busca | |
||||||
|
| `photos` | 5.386 | Mídias associadas diretamente aos anúncios | |
||||||
|
| `sqlite_sequence` | 2 | Sequências internas de tabelas `AUTOINCREMENT` | |
||||||
|
|
||||||
|
No snapshot avaliado: |
||||||
|
|
||||||
|
- cada anúncio possui exatamente um registro em `listing_history`; |
||||||
|
- cada anúncio está ligado a exatamente uma URL de busca; |
||||||
|
- cada anúncio possui de 5 a 61 linhas em `photos`; |
||||||
|
- não há órfãos nas três relações com `listings`; |
||||||
|
- os 156 hashes de `listing_history` coincidem com o hash corrente em `listings`; |
||||||
|
- `captured_at` coincide com `first_seen_at` nos 156 anúncios. |
||||||
|
|
||||||
|
## 3. Dicionário de tabelas |
||||||
|
|
||||||
|
### 3.1 `crawl_runs` |
||||||
|
|
||||||
|
Representa execuções técnicas do coletor. Não registra fonte, versão do scraper, configuração, texto de erro ou relacionamento direto com os itens coletados. |
||||||
|
|
||||||
|
| Coluna | Tipo | Nulo? | Chave/default | Interpretação | |
||||||
|
|---|---|---:|---|---| |
||||||
|
| `id` | INTEGER | Não | PK, `AUTOINCREMENT` | Identificador da execução; a PK inteira atribui um valor quando omitido | |
||||||
|
| `started_at` | TEXT | Não | — | Início em ISO 8601, observado em UTC | |
||||||
|
| `finished_at` | TEXT | Sim | — | Término em ISO 8601 | |
||||||
|
| `status` | TEXT | Não | — | Estado textual da execução; somente `ok` na amostra | |
||||||
|
| `stats_json` | TEXT | Não | `'{}'` | Contadores técnicos em JSON | |
||||||
|
|
||||||
|
As quatro estruturas JSON são válidas. As chaves presentes são `search_pages`, `listing_urls`, `details_ok`, `details_failed`, `changed`, `photos_ok`, `photos_failed` e `inactive`, todas inteiras. |
||||||
|
|
||||||
|
### 3.2 `listings` |
||||||
|
|
||||||
|
Representa a identidade do anúncio no portal e uma cópia mutável de seu estado mais recente. Não representa um imóvel físico deduplicado. |
||||||
|
|
||||||
|
| Coluna | Tipo | Nulo? | Chave/default | Interpretação e ressalvas | |
||||||
|
|---|---|---:|---|---| |
||||||
|
| `listing_id` | TEXT | Sim no DDL; não observado | PK | Identificador do anúncio no portal; todos os valores atuais têm 6–7 dígitos. Por peculiaridade do SQLite, PK textual sem `NOT NULL` explícito merece validação na aplicação | |
||||||
|
| `url` | TEXT | Não | — | URL do anúncio | |
||||||
|
| `title` | TEXT | Sim | — | Título extraído | |
||||||
|
| `description` | TEXT | Sim | — | Descrição curta extraída; não é o HTML bruto completo | |
||||||
|
| `transaction_type` | TEXT | Sim | — | Tipo da transação; somente `venda` no snapshot | |
||||||
|
| `property_type` | TEXT | Sim | — | Tipo anunciado; `apartamento` ou `casa` | |
||||||
|
| `price_brl` | REAL | Sim | — | Preço anunciado em reais; sujeito a erros de escala | |
||||||
|
| `condominium_brl` | REAL | Sim | — | Condomínio anunciado; periodicidade não está formalizada | |
||||||
|
| `iptu_brl` | REAL | Sim | — | IPTU anunciado; periodicidade não está formalizada | |
||||||
|
| `area_m2` | REAL | Sim | — | Uma única área em m², sem indicar se é útil, privativa, construída, total ou terreno | |
||||||
|
| `bedrooms` | INTEGER | Sim | — | Quartos anunciados, não a planta original comprovada | |
||||||
|
| `suites` | INTEGER | Sim | — | Suítes anunciadas | |
||||||
|
| `parking_spaces` | INTEGER | Sim | — | Vagas anunciadas | |
||||||
|
| `address` | TEXT | Sim | — | Texto de endereço; fortemente contaminado pela página na coleta atual | |
||||||
|
| `neighborhood` | TEXT | Sim | — | Bairro; fortemente contaminado pela página na coleta atual | |
||||||
|
| `city` | TEXT | Sim | — | Cidade; 100% nula atualmente | |
||||||
|
| `state` | TEXT | Sim | — | UF; 100% nula atualmente | |
||||||
|
| `advertiser_name` | TEXT | Sim | — | Nome do anunciante; 100% nulo atualmente | |
||||||
|
| `advertiser_code` | TEXT | Sim | — | Código do anunciante; parte relevante dos valores está contaminada | |
||||||
|
| `creci` | TEXT | Sim | — | Registro do anunciante; parte relevante dos valores está contaminada | |
||||||
|
| `latitude` | REAL | Sim | — | Latitude; 100% nula atualmente | |
||||||
|
| `longitude` | REAL | Sim | — | Longitude; 100% nula atualmente | |
||||||
|
| `published_at` | TEXT | Sim | — | Data publicada pelo portal; 100% nula atualmente | |
||||||
|
| `first_seen_at` | TEXT | Não | — | Primeira observação do anúncio | |
||||||
|
| `last_seen_at` | TEXT | Não | — | Última observação do anúncio | |
||||||
|
| `inactive_at` | TEXT | Sim | — | Momento de inativação inferida pelo coletor; 100% nulo atualmente | |
||||||
|
| `content_hash` | TEXT | Sim | — | SHA-256 do conteúdo normalizado usado para detectar mudança | |
||||||
|
| `raw_html_path` | TEXT | Sim | — | Caminho relativo para o HTML bruto do anúncio | |
||||||
|
| `extra_json` | TEXT | Não | `'{}'` | JSON adicional; contém apenas `json_ld`, um array vazio nos 156 anúncios | |
||||||
|
|
||||||
|
Exemplo não sensível de identidade: o anúncio `1026522` aponta para uma URL sob `www.dfimoveis.com.br` e para um HTML em `raw/listing/1026522/...html.gz`. |
||||||
|
|
||||||
|
### 3.3 `listing_history` |
||||||
|
|
||||||
|
Representa estados distintos de um anúncio, não cada tentativa ou observação do coletor. |
||||||
|
|
||||||
|
| Coluna | Tipo | Nulo? | Chave/default | Interpretação | |
||||||
|
|---|---|---:|---|---| |
||||||
|
| `id` | INTEGER | Não | PK, `AUTOINCREMENT` | Identificador do estado histórico; a PK inteira atribui um valor quando omitido | |
||||||
|
| `listing_id` | TEXT | Não | FK | Anúncio ao qual o estado pertence | |
||||||
|
| `captured_at` | TEXT | Não | — | Momento em que o estado foi capturado | |
||||||
|
| `content_hash` | TEXT | Não | `UNIQUE` com `listing_id` | Hash que impede repetir um estado idêntico para o anúncio | |
||||||
|
| `data_json` | TEXT | Não | — | Snapshot JSON dos principais campos extraídos | |
||||||
|
|
||||||
|
`data_json` é JSON válido em todas as 156 linhas. Ele contém os campos correntes de conteúdo, mas não contém `first_seen_at`, `last_seen_at`, `inactive_at`, `raw_html_path` ou uma referência à execução. Como a restrição é `UNIQUE(listing_id, content_hash)`, observações repetidas sem mudança não geram novos snapshots. |
||||||
|
|
||||||
|
### 3.4 `listing_searches` |
||||||
|
|
||||||
|
Tabela de associação entre anúncio e consulta de busca que o encontrou. |
||||||
|
|
||||||
|
| Coluna | Tipo | Nulo? | Chave/default | Interpretação | |
||||||
|
|---|---|---:|---|---| |
||||||
|
| `listing_id` | TEXT | Não | PK parcial, FK | Anúncio | |
||||||
|
| `search_url` | TEXT | Não | PK parcial | URL/configuração da busca | |
||||||
|
| `first_seen_at` | TEXT | Não | — | Primeira observação nessa busca | |
||||||
|
| `last_seen_at` | TEXT | Não | — | Última observação nessa busca | |
||||||
|
|
||||||
|
A chave primária composta é `(listing_id, search_url)`. Há duas URLs distintas no banco, uma para apartamentos de três quartos no Cruzeiro Novo e outra para casas anunciadas com três ou quatro quartos no Jardins Mangueiral. |
||||||
|
|
||||||
|
### 3.5 `photos` |
||||||
|
|
||||||
|
Representa arquivos de mídia associados diretamente ao anúncio. A tabela não preserva associação por snapshot. |
||||||
|
|
||||||
|
| Coluna | Tipo | Nulo? | Chave/default | Interpretação | |
||||||
|
|---|---|---:|---|---| |
||||||
|
| `listing_id` | TEXT | Não | PK parcial, FK | Anúncio | |
||||||
|
| `ordinal` | INTEGER | Não | — | Ordem da imagem no anúncio | |
||||||
|
| `source_url` | TEXT | Não | PK parcial | URL de origem da imagem | |
||||||
|
| `sha256` | TEXT | Sim | — | Hash do arquivo baixado | |
||||||
|
| `mime_type` | TEXT | Sim | — | Tipo MIME detectado | |
||||||
|
| `bytes` | INTEGER | Sim | — | Tamanho do arquivo | |
||||||
|
| `local_path` | TEXT | Sim | — | Caminho relativo sob `dfimoveis_data/` | |
||||||
|
| `first_seen_at` | TEXT | Não | — | Primeira observação da mídia | |
||||||
|
| `last_seen_at` | TEXT | Não | — | Última observação da mídia | |
||||||
|
|
||||||
|
A chave primária é `(listing_id, source_url)`, e não `(listing_id, ordinal)`. Não há ordinais repetidos dentro de um anúncio na amostra atual. Exemplo de caminho: `photos/1026522/001_9d7f11205f0a4a3f.jpg`. |
||||||
|
|
||||||
|
### 3.6 `sqlite_sequence` |
||||||
|
|
||||||
|
Tabela interna do SQLite que mantém as sequências `AUTOINCREMENT` de `crawl_runs` e `listing_history`. Não é entidade do domínio. |
||||||
|
|
||||||
|
## 4. Chaves, índices e integridade referencial |
||||||
|
|
||||||
|
| Índice | Tabela | Origem | Colunas lógicas | |
||||||
|
|---|---|---|---| |
||||||
|
| `sqlite_autoindex_listings_1` | `listings` | PK | `listing_id` | |
||||||
|
| `sqlite_autoindex_listing_history_1` | `listing_history` | `UNIQUE` | `listing_id`, `content_hash` | |
||||||
|
| `sqlite_autoindex_listing_searches_1` | `listing_searches` | PK | `listing_id`, `search_url` | |
||||||
|
| `sqlite_autoindex_photos_1` | `photos` | PK | `listing_id`, `source_url` | |
||||||
|
|
||||||
|
As FKs de `listing_history`, `listing_searches` e `photos` apontam para `listings(listing_id)`, com `NO ACTION` para atualização e exclusão. Não há índices explícitos por data, preço, região ou hash de foto. Com o volume atual isso não é um problema operacional; se a base crescer, índices analíticos devem ser criados apenas em uma camada derivada ou recomendados para uma migração aprovada, nunca adicionados ao banco original durante análise. |
||||||
|
|
||||||
|
## 5. Correspondência com o modelo conceitual |
||||||
|
|
||||||
|
| Conceito desejado | Implementação física | Avaliação | |
||||||
|
|---|---|---| |
||||||
|
| Fonte | Ausente; DF Imóveis é implícito nas URLs | Não permite múltiplas fontes com identidade segura | |
||||||
|
| Execução de coleta | `crawl_runs` | Parcial; faltam fonte, configuração, versão e relação com itens | |
||||||
|
| Anúncio | `listings` | Presente; estado corrente e identidade estão na mesma linha | |
||||||
|
| Observação histórica | `listing_history` | Parcial; guarda somente conteúdo alterado, sem `run_id` | |
||||||
|
| Imóvel físico candidato | Ausente | Nenhuma deduplicação física é persistida | |
||||||
|
| Localização normalizada | Colunas em `listings` | Insuficiente e atualmente contaminada | |
||||||
|
| Mídia | `photos` | Parcial; vinculada ao anúncio, não ao snapshot | |
||||||
|
| Origem por consulta | `listing_searches` | Presente como relação anúncio–URL de busca | |
||||||
|
|
||||||
|
## 6. Decisões de interpretação e ambiguidades |
||||||
|
|
||||||
|
1. `listing_id` identifica um **anúncio no portal**, não um imóvel físico. |
||||||
|
2. Uma linha de `listing_history` é um **estado de conteúdo distinto**, não prova de que o anúncio foi observado somente uma vez. Observações idênticas são condensadas em `last_seen_at`. |
||||||
|
3. O banco ainda não contém histórico suficiente para preços ou permanência: há um snapshot por anúncio e todos os anúncios têm `first_seen_at = last_seen_at`. |
||||||
|
4. Anúncios diferentes com preço, área ou fotos semelhantes permanecem anúncios distintos. Não há chave de endereço/unidade nem tabela de candidatos físicos. |
||||||
|
5. `area_m2` não deve ser usada para preço por m² até que o conceito de área seja validado por tipologia e fonte. |
||||||
|
6. `bedrooms` é a quantidade anunciada. Ela não comprova a planta original de três quartos exigida para Jardins Mangueiral. |
||||||
|
7. Datas com sufixo `+00:00` são tratadas como UTC. A cobertura observada é de um único dia. |
||||||
|
8. Os campos textuais contaminados devem ser reextraídos do HTML bruto em uma camada derivada; os valores atuais não devem ser silenciosamente normalizados como se fossem corretos. |
||||||
|
|
||||||
|
## 7. Problemas conhecidos do snapshot |
||||||
|
|
||||||
|
- 146 de 156 endereços têm mais de 300 caracteres; 154 de 156 bairros têm mais de 100 caracteres. Os dois bairros curtos também não formam valores confiáveis. |
||||||
|
- `city`, `state`, `latitude`, `longitude`, `published_at` e `advertiser_name` estão integralmente ausentes. |
||||||
|
- 98 códigos de anunciante e 153 valores de CRECI são longos demais para o significado esperado, indicando extração contaminada. |
||||||
|
- Há um preço de R$ 570.000.000 e uma área de 0,11 m², ambos candidatos fortes a erro de extração/escala. |
||||||
|
- O JSON-LD reservado em `extra_json` é um array vazio em todos os anúncios e não oferece uma fonte estruturada alternativa no snapshot. |
||||||
|
- Três downloads de foto falharam e deixaram linhas sem hash, MIME, tamanho e caminho local. |
||||||
|
- Hashes de foto idênticos aparecem em muitos anúncios, inclusive um hash presente nos 156; isso demonstra a presença de ativos genéricos e impede usar igualdade de hash como prova isolada de imóvel duplicado. |
||||||
|
- Não há como ligar formalmente anúncio/snapshot a `crawl_runs`, nem distinguir por chave a fonte se uma segunda fonte for incorporada. |
||||||
@ -0,0 +1,542 @@ |
|||||||
|
# Regras de análise |
||||||
|
|
||||||
|
## 1. Objetivo metodológico |
||||||
|
|
||||||
|
Produzir análises reproduzíveis e úteis para decisão de compra, sem transformar dados de anúncios em uma falsa precisão de mercado. |
||||||
|
|
||||||
|
Toda conclusão deve distinguir: |
||||||
|
|
||||||
|
- o que os dados mostram; |
||||||
|
- o que foi inferido; |
||||||
|
- o que permanece desconhecido; |
||||||
|
- o que depende de visita, documentação ou negociação. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 2. Unidade de análise |
||||||
|
|
||||||
|
Definir antes de calcular: |
||||||
|
|
||||||
|
- anúncio único; |
||||||
|
- snapshot de anúncio; |
||||||
|
- imóvel físico provável; |
||||||
|
- imóvel físico confirmado; |
||||||
|
- região; |
||||||
|
- tipologia; |
||||||
|
- período. |
||||||
|
|
||||||
|
Não misturar unidades. |
||||||
|
|
||||||
|
Exemplo de erro: |
||||||
|
|
||||||
|
> Contar cada snapshot mensal como um imóvel diferente. |
||||||
|
|
||||||
|
Exemplo correto: |
||||||
|
|
||||||
|
> Para estoque histórico, usar um anúncio ativo por data; para oferta única, deduplicar anúncios e imóveis prováveis. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 3. Cobertura e consistência temporal |
||||||
|
|
||||||
|
Antes de comparar meses: |
||||||
|
|
||||||
|
- verificar dias coletados; |
||||||
|
- verificar fontes ativas; |
||||||
|
- verificar falhas; |
||||||
|
- verificar mudanças do scraper; |
||||||
|
- verificar novos filtros; |
||||||
|
- verificar alterações no site; |
||||||
|
- verificar interrupções; |
||||||
|
- verificar mudanças de campos. |
||||||
|
|
||||||
|
Uma queda no número de anúncios pode ser falha de coleta. |
||||||
|
|
||||||
|
Relatórios temporais devem exibir cobertura. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 4. Limpeza |
||||||
|
|
||||||
|
### 4.1 Preços inválidos |
||||||
|
|
||||||
|
Identificar: |
||||||
|
|
||||||
|
- zero; |
||||||
|
- preço mensal; |
||||||
|
- preço de aluguel em anúncio de venda; |
||||||
|
- valor parcial; |
||||||
|
- erro de escala; |
||||||
|
- preço simbólico; |
||||||
|
- preço por fração; |
||||||
|
- moeda incorreta; |
||||||
|
- preço inconsistente com descrição. |
||||||
|
|
||||||
|
Não remover silenciosamente. Registrar regra e contagem. |
||||||
|
|
||||||
|
### 4.2 Áreas inválidas |
||||||
|
|
||||||
|
Identificar: |
||||||
|
|
||||||
|
- zero; |
||||||
|
- área total usada como privativa; |
||||||
|
- área do terreno; |
||||||
|
- área multiplicada; |
||||||
|
- vírgula decimal interpretada incorretamente; |
||||||
|
- área do condomínio; |
||||||
|
- área incompatível com a tipologia. |
||||||
|
|
||||||
|
### 4.3 Quartos e tipologia |
||||||
|
|
||||||
|
Verificar divergências entre: |
||||||
|
|
||||||
|
- título; |
||||||
|
- campos estruturados; |
||||||
|
- descrição; |
||||||
|
- planta; |
||||||
|
- fotografias; |
||||||
|
- tipologia original. |
||||||
|
|
||||||
|
Para Jardins Mangueiral, não aceitar tipologia anunciada como prova da planta original. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 5. Preço por metro quadrado |
||||||
|
|
||||||
|
Calcular somente quando: |
||||||
|
|
||||||
|
- o preço é válido; |
||||||
|
- a área é válida; |
||||||
|
- a área representa conceito comparável; |
||||||
|
- o imóvel pertence a uma tipologia comparável. |
||||||
|
|
||||||
|
Registrar a área usada. |
||||||
|
|
||||||
|
Não comparar diretamente: |
||||||
|
|
||||||
|
- área privativa com área total; |
||||||
|
- casa com apartamento; |
||||||
|
- imóvel reformado com imóvel para reforma sem controle; |
||||||
|
- dois quartos com três quartos apenas pela média regional; |
||||||
|
- cobertura com unidade padrão; |
||||||
|
- casa ampliada com planta original sem ajustar a área. |
||||||
|
|
||||||
|
Estatísticas mínimas: |
||||||
|
|
||||||
|
- número de observações; |
||||||
|
- mediana; |
||||||
|
- percentil 25; |
||||||
|
- percentil 75; |
||||||
|
- mínimo e máximo após validação; |
||||||
|
- proporção excluída. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 6. Outliers |
||||||
|
|
||||||
|
Não excluir outliers apenas porque são extremos. |
||||||
|
|
||||||
|
Investigar se representam: |
||||||
|
|
||||||
|
- erro; |
||||||
|
- imóvel premium; |
||||||
|
- reforma; |
||||||
|
- venda urgente; |
||||||
|
- leilão; |
||||||
|
- área incorreta; |
||||||
|
- anúncio duplicado; |
||||||
|
- preço desatualizado; |
||||||
|
- unidade atípica. |
||||||
|
|
||||||
|
Apresentar resultados com e sem outliers quando a conclusão mudar. |
||||||
|
|
||||||
|
Métodos permitidos: |
||||||
|
|
||||||
|
- IQR; |
||||||
|
- MAD; |
||||||
|
- limites específicos por tipologia; |
||||||
|
- inspeção manual; |
||||||
|
- modelos robustos. |
||||||
|
|
||||||
|
Não usar corte automático sem explicar. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 7. Duplicidades |
||||||
|
|
||||||
|
Relatar: |
||||||
|
|
||||||
|
- duplicatas dentro da mesma fonte; |
||||||
|
- duplicatas entre fontes; |
||||||
|
- snapshots repetidos; |
||||||
|
- imóveis anunciados por vários corretores; |
||||||
|
- anúncios republicados com novo identificador. |
||||||
|
|
||||||
|
Para preço de oferta atual, escolher uma regra explícita, por exemplo: |
||||||
|
|
||||||
|
- menor preço vigente por imóvel provável; |
||||||
|
- anúncio mais recente; |
||||||
|
- anúncio primário; |
||||||
|
- mediana entre anúncios do mesmo imóvel. |
||||||
|
|
||||||
|
Testar sensibilidade da conclusão à regra. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 8. Histórico de anúncios |
||||||
|
|
||||||
|
Para cada anúncio ou imóvel provável, tentar identificar: |
||||||
|
|
||||||
|
- primeira aparição; |
||||||
|
- última aparição; |
||||||
|
- tempo observado; |
||||||
|
- mudanças de preço; |
||||||
|
- períodos de ausência; |
||||||
|
- republicação; |
||||||
|
- alteração de corretor; |
||||||
|
- alteração de descrição; |
||||||
|
- alteração de área ou quartos. |
||||||
|
|
||||||
|
Não concluir “vendido” apenas porque desapareceu. |
||||||
|
|
||||||
|
Estados possíveis: |
||||||
|
|
||||||
|
- ativo; |
||||||
|
- ausente na coleta; |
||||||
|
- removido; |
||||||
|
- republicado; |
||||||
|
- provável venda; |
||||||
|
- status desconhecido. |
||||||
|
|
||||||
|
“Provável venda” exige sinais adicionais. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 9. Comparáveis |
||||||
|
|
||||||
|
Um conjunto de comparáveis deve controlar, quando possível: |
||||||
|
|
||||||
|
- região; |
||||||
|
- quadra, bloco ou QC; |
||||||
|
- tipo de imóvel; |
||||||
|
- planta original; |
||||||
|
- quartos; |
||||||
|
- área; |
||||||
|
- garagem; |
||||||
|
- elevador; |
||||||
|
- andar; |
||||||
|
- posição; |
||||||
|
- reforma; |
||||||
|
- conservação; |
||||||
|
- data; |
||||||
|
- condomínio; |
||||||
|
- situação de ocupação. |
||||||
|
|
||||||
|
Quando não for possível controlar, declarar a limitação. |
||||||
|
|
||||||
|
Nunca usar uma média ampla do bairro como avaliação definitiva de uma unidade específica. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 10. Tendências |
||||||
|
|
||||||
|
Para avaliar tendência de preço: |
||||||
|
|
||||||
|
- usar imóveis comparáveis; |
||||||
|
- controlar mudanças no mix; |
||||||
|
- ponderar cobertura; |
||||||
|
- evitar média simples; |
||||||
|
- usar mediana e regressões robustas quando necessário; |
||||||
|
- separar preço de entrada, preço atual e último preço; |
||||||
|
- mostrar intervalo de confiança ou dispersão; |
||||||
|
- evitar extrapolação longa. |
||||||
|
|
||||||
|
Uma alta no preço mediano pode ocorrer porque imóveis menores desapareceram da amostra. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 11. Estoque e liquidez |
||||||
|
|
||||||
|
Métricas úteis: |
||||||
|
|
||||||
|
- anúncios ativos; |
||||||
|
- imóveis prováveis ativos; |
||||||
|
- entradas; |
||||||
|
- saídas; |
||||||
|
- republicações; |
||||||
|
- tempo observado; |
||||||
|
- reduções de preço; |
||||||
|
- percentual com múltiplas reduções; |
||||||
|
- concentração por corretor; |
||||||
|
- dispersão de preços; |
||||||
|
- taxa de reposição do estoque. |
||||||
|
|
||||||
|
Limitações: |
||||||
|
|
||||||
|
- tempo observado não é tempo real no mercado quando a coleta começou depois; |
||||||
|
- retirada não confirma venda; |
||||||
|
- republicação pode reiniciar o relógio artificialmente. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 12. Análise financeira |
||||||
|
|
||||||
|
### 12.1 Premissas obrigatórias |
||||||
|
|
||||||
|
Toda simulação deve declarar: |
||||||
|
|
||||||
|
- preço; |
||||||
|
- entrada em dinheiro; |
||||||
|
- FGTS; |
||||||
|
- reserva preservada; |
||||||
|
- custos de aquisição; |
||||||
|
- reforma; |
||||||
|
- valor financiado; |
||||||
|
- taxa efetiva; |
||||||
|
- CET, se disponível; |
||||||
|
- sistema; |
||||||
|
- prazo; |
||||||
|
- seguros e tarifas; |
||||||
|
- condomínio; |
||||||
|
- IPTU; |
||||||
|
- manutenção; |
||||||
|
- reajuste do aluguel; |
||||||
|
- retorno líquido dos investimentos; |
||||||
|
- inflação; |
||||||
|
- valorização; |
||||||
|
- horizonte; |
||||||
|
- custos de venda; |
||||||
|
- estratégia de amortização. |
||||||
|
|
||||||
|
### 12.2 Liquidez |
||||||
|
|
||||||
|
Separar: |
||||||
|
|
||||||
|
- dinheiro; |
||||||
|
- investimentos com liquidez; |
||||||
|
- FGTS; |
||||||
|
- patrimônio imobiliário; |
||||||
|
- reserva mínima. |
||||||
|
|
||||||
|
Não tratar FGTS como reserva de emergência. |
||||||
|
|
||||||
|
### 12.3 Compra versus aluguel |
||||||
|
|
||||||
|
Comparar: |
||||||
|
|
||||||
|
- fluxo mensal; |
||||||
|
- patrimônio líquido; |
||||||
|
- custo de oportunidade; |
||||||
|
- risco; |
||||||
|
- liquidez; |
||||||
|
- flexibilidade; |
||||||
|
- estabilidade; |
||||||
|
- manutenção; |
||||||
|
- impostos; |
||||||
|
- custos de transação. |
||||||
|
|
||||||
|
Mostrar cenários: |
||||||
|
|
||||||
|
- conservador; |
||||||
|
- central; |
||||||
|
- otimista. |
||||||
|
|
||||||
|
Não decidir apenas pelo patrimônio final médio. |
||||||
|
|
||||||
|
### 12.4 Estresse |
||||||
|
|
||||||
|
Testar: |
||||||
|
|
||||||
|
- juros maiores; |
||||||
|
- renda menor; |
||||||
|
- despesas extraordinárias; |
||||||
|
- reforma acima do orçamento; |
||||||
|
- vacância ou dificuldade de revenda; |
||||||
|
- valorização baixa; |
||||||
|
- investimento com retorno menor; |
||||||
|
- manutenção elevada; |
||||||
|
- redução da capacidade de poupança. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 13. Consórcio |
||||||
|
|
||||||
|
Para analisar um grupo: |
||||||
|
|
||||||
|
- usar histórico do próprio grupo; |
||||||
|
- registrar assembleias; |
||||||
|
- lances vencedores; |
||||||
|
- lance livre; |
||||||
|
- lance fixo; |
||||||
|
- lance embutido; |
||||||
|
- número de contemplações; |
||||||
|
- saldo; |
||||||
|
- reajuste da carta; |
||||||
|
- taxa de administração; |
||||||
|
- fundo de reserva; |
||||||
|
- seguros; |
||||||
|
- regras de uso; |
||||||
|
- prazo; |
||||||
|
- valor total pago. |
||||||
|
|
||||||
|
Não inferir probabilidade de contemplação usando apenas média histórica. |
||||||
|
|
||||||
|
Analisar: |
||||||
|
|
||||||
|
- distribuição; |
||||||
|
- percentis; |
||||||
|
- sazonalidade; |
||||||
|
- número de concorrentes; |
||||||
|
- mudanças de regra; |
||||||
|
- eventos de renda; |
||||||
|
- concentração de lances; |
||||||
|
- sensibilidade ao valor ofertado. |
||||||
|
|
||||||
|
Relatos de gerente são hipóteses a testar. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 14. Relatórios por região |
||||||
|
|
||||||
|
### Cruzeiro |
||||||
|
|
||||||
|
Separar por: |
||||||
|
|
||||||
|
- Cruzeiro Novo e Velho; |
||||||
|
- quadra; |
||||||
|
- bloco; |
||||||
|
- tipologia; |
||||||
|
- área; |
||||||
|
- elevador; |
||||||
|
- garagem; |
||||||
|
- andar; |
||||||
|
- conservação; |
||||||
|
- reforma. |
||||||
|
|
||||||
|
### Jardins Mangueiral |
||||||
|
|
||||||
|
Separar por: |
||||||
|
|
||||||
|
- QC; |
||||||
|
- planta original de três quartos; |
||||||
|
- área construída; |
||||||
|
- ampliação; |
||||||
|
- qualidade da área fechada; |
||||||
|
- condomínio; |
||||||
|
- posição; |
||||||
|
- ruído; |
||||||
|
- proximidade de serviços; |
||||||
|
- padrão de reforma. |
||||||
|
|
||||||
|
Excluir casas originalmente de dois quartos. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 15. Relatório padrão |
||||||
|
|
||||||
|
### Resumo executivo |
||||||
|
|
||||||
|
- conclusão principal; |
||||||
|
- confiança; |
||||||
|
- implicação prática; |
||||||
|
- maior risco. |
||||||
|
|
||||||
|
### Dados |
||||||
|
|
||||||
|
- fontes; |
||||||
|
- período; |
||||||
|
- cobertura; |
||||||
|
- tamanho da amostra; |
||||||
|
- exclusões; |
||||||
|
- deduplicação. |
||||||
|
|
||||||
|
### Resultados |
||||||
|
|
||||||
|
- tabelas; |
||||||
|
- distribuições; |
||||||
|
- comparáveis; |
||||||
|
- tendências; |
||||||
|
- sensibilidade. |
||||||
|
|
||||||
|
### Interpretação |
||||||
|
|
||||||
|
- o que é sustentado; |
||||||
|
- o que é apenas sugestivo; |
||||||
|
- o que requer visita; |
||||||
|
- o que requer confirmação financeira ou documental. |
||||||
|
|
||||||
|
### Próxima ação |
||||||
|
|
||||||
|
Classificar como: |
||||||
|
|
||||||
|
- ignorar; |
||||||
|
- monitorar; |
||||||
|
- aprofundar; |
||||||
|
- visitar; |
||||||
|
- negociar; |
||||||
|
- candidato forte; |
||||||
|
- decisão insuficiente. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 16. Reprodutibilidade |
||||||
|
|
||||||
|
Para cada relatório, salvar: |
||||||
|
|
||||||
|
```text |
||||||
|
reports/<nome>/ |
||||||
|
├── README.md |
||||||
|
├── query.sql |
||||||
|
├── analysis.py |
||||||
|
├── parameters.json |
||||||
|
├── results.csv |
||||||
|
└── figures/ |
||||||
|
``` |
||||||
|
|
||||||
|
Registrar: |
||||||
|
|
||||||
|
- data; |
||||||
|
- hash do código; |
||||||
|
- identificação do banco; |
||||||
|
- filtros; |
||||||
|
- versão; |
||||||
|
- ambiente; |
||||||
|
- contagens. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 17. Linguagem das conclusões |
||||||
|
|
||||||
|
Preferir: |
||||||
|
|
||||||
|
- “os anúncios observados sugerem”; |
||||||
|
- “na amostra coletada”; |
||||||
|
- “após deduplicação”; |
||||||
|
- “não há evidência suficiente”; |
||||||
|
- “a conclusão é sensível a”; |
||||||
|
- “o dado não permite afirmar”. |
||||||
|
|
||||||
|
Evitar: |
||||||
|
|
||||||
|
- “o mercado vale”; |
||||||
|
- “foi vendido” sem prova; |
||||||
|
- “esta é a melhor quadra” sem critério; |
||||||
|
- “o preço justo é” sem intervalo e comparáveis; |
||||||
|
- “vai valorizar” como certeza; |
||||||
|
- “o lance será contemplado” como previsão. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 18. Critério final |
||||||
|
|
||||||
|
Uma análise é boa quando ajuda a evitar: |
||||||
|
|
||||||
|
- comprar cedo demais; |
||||||
|
- pagar por uma reforma disfarçada; |
||||||
|
- confundir anúncio com transação; |
||||||
|
- superestimar valorização; |
||||||
|
- subestimar custos; |
||||||
|
- comprometer liquidez; |
||||||
|
- aceitar uma planta inadequada; |
||||||
|
- tomar decisão por ansiedade; |
||||||
|
- descartar um bom imóvel por uma métrica superficial. |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
# Chat handoff |
||||||
|
|
||||||
|
## Operational transcript from the previous session |
||||||
|
|
||||||
|
This file consolidates the prior working session into a handoff format so the project can continue without redoing the same analysis from scratch. |
||||||
@ -0,0 +1,481 @@ |
|||||||
|
# Modelo de dados — referência conceitual |
||||||
|
|
||||||
|
## 1. Aviso |
||||||
|
|
||||||
|
Este documento descreve o modelo lógico desejável para análise. |
||||||
|
|
||||||
|
Ele **não afirma** que o banco SQLite atual possui essas tabelas ou colunas. O esquema real deve ser inspecionado antes de qualquer consulta. |
||||||
|
|
||||||
|
Não migre o banco original para este modelo sem autorização. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 2. Entidades fundamentais |
||||||
|
|
||||||
|
O projeto deve distinguir cinco conceitos: |
||||||
|
|
||||||
|
1. **fonte**: site, portal, corretor ou canal; |
||||||
|
2. **anúncio**: publicação identificável em uma fonte; |
||||||
|
3. **observação**: estado do anúncio em uma data de coleta; |
||||||
|
4. **imóvel físico**: unidade residencial que pode aparecer em vários anúncios; |
||||||
|
5. **execução de coleta**: processo técnico que obteve os dados. |
||||||
|
|
||||||
|
Essa separação é essencial para séries históricas e deduplicação. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 3. Fonte |
||||||
|
|
||||||
|
Entidade conceitual: `source` |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `source_id` |
||||||
|
- `name` |
||||||
|
- `base_url` |
||||||
|
- `source_type` |
||||||
|
- `active` |
||||||
|
- `notes` |
||||||
|
- `created_at` |
||||||
|
|
||||||
|
Não usar domínio ou nome de corretor como identificador único sem normalização. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 4. Execução de coleta |
||||||
|
|
||||||
|
Entidade conceitual: `scrape_run` |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `run_id` |
||||||
|
- `source_id` |
||||||
|
- `started_at` |
||||||
|
- `finished_at` |
||||||
|
- `status` |
||||||
|
- `pages_requested` |
||||||
|
- `pages_succeeded` |
||||||
|
- `pages_failed` |
||||||
|
- `items_seen` |
||||||
|
- `items_inserted` |
||||||
|
- `items_updated` |
||||||
|
- `http_status_summary` |
||||||
|
- `scraper_version` |
||||||
|
- `configuration_hash` |
||||||
|
- `error_summary` |
||||||
|
|
||||||
|
Serve para identificar: |
||||||
|
|
||||||
|
- períodos sem coleta; |
||||||
|
- mudanças de cobertura; |
||||||
|
- falhas; |
||||||
|
- alterações de código; |
||||||
|
- diferenças entre ausência real e ausência causada por erro. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 5. Anúncio |
||||||
|
|
||||||
|
Entidade conceitual: `listing` |
||||||
|
|
||||||
|
Representa a identidade do anúncio, não cada coleta. |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `listing_id` |
||||||
|
- `source_id` |
||||||
|
- `source_listing_id` |
||||||
|
- `canonical_url` |
||||||
|
- `first_seen_at` |
||||||
|
- `last_seen_at` |
||||||
|
- `last_successful_seen_at` |
||||||
|
- `status` |
||||||
|
- `seller_type` |
||||||
|
- `seller_name_normalized` |
||||||
|
- `broker_registration` |
||||||
|
- `property_candidate_id` |
||||||
|
- `created_at` |
||||||
|
- `updated_at` |
||||||
|
|
||||||
|
Chave natural potencial: |
||||||
|
|
||||||
|
```text |
||||||
|
(source_id, source_listing_id) |
||||||
|
``` |
||||||
|
|
||||||
|
A URL não deve ser a única chave, pois pode mudar. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 6. Observação histórica |
||||||
|
|
||||||
|
Entidade conceitual: `listing_snapshot` |
||||||
|
|
||||||
|
Representa o conteúdo observado em uma execução. |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `snapshot_id` |
||||||
|
- `listing_id` |
||||||
|
- `run_id` |
||||||
|
- `observed_at` |
||||||
|
- `title` |
||||||
|
- `description` |
||||||
|
- `price` |
||||||
|
- `condominium_fee` |
||||||
|
- `property_tax` |
||||||
|
- `area_private` |
||||||
|
- `area_useful` |
||||||
|
- `area_built` |
||||||
|
- `area_total` |
||||||
|
- `bedrooms` |
||||||
|
- `suites` |
||||||
|
- `bathrooms` |
||||||
|
- `parking_spaces` |
||||||
|
- `floor` |
||||||
|
- `elevator` |
||||||
|
- `furnished` |
||||||
|
- `property_type` |
||||||
|
- `address_raw` |
||||||
|
- `neighborhood_raw` |
||||||
|
- `latitude` |
||||||
|
- `longitude` |
||||||
|
- `image_count` |
||||||
|
- `status_raw` |
||||||
|
- `payload_hash` |
||||||
|
- `raw_payload_reference` |
||||||
|
|
||||||
|
Não sobrescrever o histórico quando um anúncio muda. |
||||||
|
|
||||||
|
Se os dados atuais usam uma única linha mutável por anúncio, documentar essa limitação. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 7. Imóvel físico candidato |
||||||
|
|
||||||
|
Entidade conceitual: `property_candidate` |
||||||
|
|
||||||
|
Representa uma hipótese de que anúncios diferentes descrevem o mesmo imóvel. |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `property_candidate_id` |
||||||
|
- `canonical_region` |
||||||
|
- `canonical_address` |
||||||
|
- `canonical_unit` |
||||||
|
- `property_type` |
||||||
|
- `original_floor_plan` |
||||||
|
- `dedup_confidence` |
||||||
|
- `dedup_method` |
||||||
|
- `review_status` |
||||||
|
- `created_at` |
||||||
|
- `updated_at` |
||||||
|
|
||||||
|
Não tratar correspondência probabilística como certeza. |
||||||
|
|
||||||
|
Estados sugeridos: |
||||||
|
|
||||||
|
- `unreviewed` |
||||||
|
- `probable` |
||||||
|
- `confirmed` |
||||||
|
- `rejected` |
||||||
|
- `ambiguous` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 8. Endereço e localização |
||||||
|
|
||||||
|
Entidade conceitual: `location` |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `location_id` |
||||||
|
- `region` |
||||||
|
- `administrative_region` |
||||||
|
- `neighborhood` |
||||||
|
- `superquadra` |
||||||
|
- `quadra` |
||||||
|
- `block` |
||||||
|
- `qc` |
||||||
|
- `street` |
||||||
|
- `lot` |
||||||
|
- `unit` |
||||||
|
- `postal_code` |
||||||
|
- `latitude` |
||||||
|
- `longitude` |
||||||
|
- `geocode_precision` |
||||||
|
- `geocode_source` |
||||||
|
|
||||||
|
No Distrito Federal, o endereço deve permitir estruturas como: |
||||||
|
|
||||||
|
- SHCES; |
||||||
|
- quadra; |
||||||
|
- bloco; |
||||||
|
- superquadra; |
||||||
|
- conjunto; |
||||||
|
- lote; |
||||||
|
- QC; |
||||||
|
- condomínio; |
||||||
|
- unidade. |
||||||
|
|
||||||
|
Não forçar todos os endereços a um modelo de rua e número. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 9. Mídia |
||||||
|
|
||||||
|
Entidade conceitual: `listing_media` |
||||||
|
|
||||||
|
Campos desejáveis: |
||||||
|
|
||||||
|
- `media_id` |
||||||
|
- `snapshot_id` |
||||||
|
- `media_url` |
||||||
|
- `media_type` |
||||||
|
- `position` |
||||||
|
- `content_hash` |
||||||
|
- `perceptual_hash` |
||||||
|
- `width` |
||||||
|
- `height` |
||||||
|
- `download_status` |
||||||
|
|
||||||
|
Hashes perceptuais de imagens podem ajudar na deduplicação, mas não devem decidir sozinhos. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 10. Histórico de preço |
||||||
|
|
||||||
|
O histórico pode ser derivado de `listing_snapshot`. |
||||||
|
|
||||||
|
View conceitual: |
||||||
|
|
||||||
|
```sql |
||||||
|
SELECT |
||||||
|
listing_id, |
||||||
|
observed_at, |
||||||
|
price, |
||||||
|
price - LAG(price) OVER ( |
||||||
|
PARTITION BY listing_id |
||||||
|
ORDER BY observed_at |
||||||
|
) AS absolute_change |
||||||
|
FROM listing_snapshot; |
||||||
|
``` |
||||||
|
|
||||||
|
Regras: |
||||||
|
|
||||||
|
- ignorar repetições idênticas; |
||||||
|
- registrar aumentos e reduções; |
||||||
|
- não interpretar ausência como venda; |
||||||
|
- identificar alterações simultâneas de área ou tipologia; |
||||||
|
- detectar preços promocionais artificiais; |
||||||
|
- separar moeda e unidades. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 11. Deduplicação |
||||||
|
|
||||||
|
### 11.1 Sinais fortes |
||||||
|
|
||||||
|
- mesmo identificador na fonte; |
||||||
|
- mesmo endereço e unidade; |
||||||
|
- mesmo conjunto de imagens; |
||||||
|
- mesmo telefone ou corretor com descrição muito semelhante; |
||||||
|
- mesma planta, metragem e características; |
||||||
|
- coordenadas muito próximas; |
||||||
|
- texto quase idêntico. |
||||||
|
|
||||||
|
### 11.2 Sinais moderados |
||||||
|
|
||||||
|
- mesma quadra e bloco; |
||||||
|
- mesmo preço; |
||||||
|
- mesma área; |
||||||
|
- mesmos quartos e vagas; |
||||||
|
- datas próximas; |
||||||
|
- imagens parcialmente coincidentes. |
||||||
|
|
||||||
|
### 11.3 Sinais fracos |
||||||
|
|
||||||
|
- mesmo bairro; |
||||||
|
- título genérico; |
||||||
|
- preço arredondado; |
||||||
|
- número de quartos isolado. |
||||||
|
|
||||||
|
### 11.4 Regras |
||||||
|
|
||||||
|
- manter o anúncio original; |
||||||
|
- criar relacionamento de duplicidade; |
||||||
|
- registrar método e confiança; |
||||||
|
- permitir revisão manual; |
||||||
|
- não fundir registros de forma irreversível; |
||||||
|
- não usar apenas texto normalizado; |
||||||
|
- evitar que atualizações do mesmo anúncio sejam contadas como imóveis novos. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 12. Normalização |
||||||
|
|
||||||
|
### Preço |
||||||
|
|
||||||
|
- armazenar como número inteiro em centavos ou valor monetário consistente; |
||||||
|
- preservar valor bruto; |
||||||
|
- registrar moeda; |
||||||
|
- detectar preço por mês confundido com preço total; |
||||||
|
- detectar valores incompletos. |
||||||
|
|
||||||
|
### Área |
||||||
|
|
||||||
|
Manter campos separados: |
||||||
|
|
||||||
|
- privativa; |
||||||
|
- útil; |
||||||
|
- construída; |
||||||
|
- total; |
||||||
|
- terreno. |
||||||
|
|
||||||
|
Nunca preencher uma área com outra apenas para completar dados. |
||||||
|
|
||||||
|
### Quartos |
||||||
|
|
||||||
|
Separar: |
||||||
|
|
||||||
|
- quartos; |
||||||
|
- suítes; |
||||||
|
- dependência; |
||||||
|
- escritório; |
||||||
|
- quarto reversível; |
||||||
|
- tipologia original; |
||||||
|
- tipologia anunciada. |
||||||
|
|
||||||
|
### Localização |
||||||
|
|
||||||
|
Preservar: |
||||||
|
|
||||||
|
- texto bruto; |
||||||
|
- versão normalizada; |
||||||
|
- componentes extraídos; |
||||||
|
- confiança da extração. |
||||||
|
|
||||||
|
### Datas |
||||||
|
|
||||||
|
Usar ISO 8601 e timezone explícito quando relevante. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 13. Qualidade dos dados |
||||||
|
|
||||||
|
Criar métricas por fonte, período e região: |
||||||
|
|
||||||
|
- percentual sem preço; |
||||||
|
- percentual sem área; |
||||||
|
- percentual sem localização; |
||||||
|
- percentual com preço por m² calculável; |
||||||
|
- duplicidade estimada; |
||||||
|
- taxa de mudança de identificador; |
||||||
|
- cobertura diária; |
||||||
|
- falhas de coleta; |
||||||
|
- distribuições implausíveis; |
||||||
|
- inconsistência entre título e campos; |
||||||
|
- divergência entre snapshots. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 14. Inspeção inicial do SQLite |
||||||
|
|
||||||
|
Consultas somente leitura úteis: |
||||||
|
|
||||||
|
### Tabelas e views |
||||||
|
|
||||||
|
```sql |
||||||
|
SELECT |
||||||
|
type, |
||||||
|
name, |
||||||
|
tbl_name, |
||||||
|
sql |
||||||
|
FROM sqlite_master |
||||||
|
ORDER BY type, name; |
||||||
|
``` |
||||||
|
|
||||||
|
### Contagem aproximada de tabelas |
||||||
|
|
||||||
|
Gerar consultas após listar os nomes. Não concatenar nomes externos sem validação. |
||||||
|
|
||||||
|
### Colunas |
||||||
|
|
||||||
|
```sql |
||||||
|
PRAGMA table_info('nome_da_tabela'); |
||||||
|
``` |
||||||
|
|
||||||
|
### Chaves estrangeiras |
||||||
|
|
||||||
|
```sql |
||||||
|
PRAGMA foreign_key_list('nome_da_tabela'); |
||||||
|
``` |
||||||
|
|
||||||
|
### Índices |
||||||
|
|
||||||
|
```sql |
||||||
|
PRAGMA index_list('nome_da_tabela'); |
||||||
|
``` |
||||||
|
|
||||||
|
### Integridade |
||||||
|
|
||||||
|
```sql |
||||||
|
PRAGMA quick_check; |
||||||
|
``` |
||||||
|
|
||||||
|
`quick_check` é somente leitura, mas pode ser custoso em bancos grandes. Executar quando apropriado. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 15. Camada derivada recomendada |
||||||
|
|
||||||
|
Sem alterar o banco original, criar: |
||||||
|
|
||||||
|
```text |
||||||
|
data/derived/ |
||||||
|
├── listings_clean.parquet |
||||||
|
├── snapshots_clean.parquet |
||||||
|
├── property_candidates.parquet |
||||||
|
├── price_history.parquet |
||||||
|
├── locations_normalized.parquet |
||||||
|
└── quality_metrics.parquet |
||||||
|
``` |
||||||
|
|
||||||
|
Ou um SQLite derivado: |
||||||
|
|
||||||
|
```text |
||||||
|
data/derived/analytics.sqlite |
||||||
|
``` |
||||||
|
|
||||||
|
Toda derivação deve registrar: |
||||||
|
|
||||||
|
- data; |
||||||
|
- versão do código; |
||||||
|
- fonte; |
||||||
|
- filtros; |
||||||
|
- hash ou identificação do banco de origem; |
||||||
|
- contagem de entrada e saída. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 16. Dicionário do esquema real |
||||||
|
|
||||||
|
Após inspecionar o banco, produzir: |
||||||
|
|
||||||
|
```text |
||||||
|
docs/ACTUAL_SCHEMA.md |
||||||
|
``` |
||||||
|
|
||||||
|
Esse arquivo deve conter: |
||||||
|
|
||||||
|
- diagrama das tabelas; |
||||||
|
- descrição de cada coluna; |
||||||
|
- chaves; |
||||||
|
- índices; |
||||||
|
- cardinalidades; |
||||||
|
- exemplos pequenos; |
||||||
|
- problemas conhecidos; |
||||||
|
- correspondência com este modelo conceitual; |
||||||
|
- decisões de interpretação. |
||||||
|
|
||||||
|
Não alterar `DATA_MODEL.md` para simplesmente espelhar um esquema ruim. Manter separação entre modelo conceitual e implementação real. |
||||||
@ -0,0 +1,265 @@ |
|||||||
|
# Contexto do projeto — Busca de Imóveis DF |
||||||
|
|
||||||
|
## 1. Objetivo |
||||||
|
|
||||||
|
Construir uma base histórica e uma metodologia confiável para apoiar a compra de um imóvel residencial no Distrito Federal. |
||||||
|
|
||||||
|
O objetivo não é apenas encontrar o menor preço. A decisão deve equilibrar: |
||||||
|
|
||||||
|
- segurança patrimonial de longo prazo; |
||||||
|
- adequação funcional do imóvel; |
||||||
|
- qualidade de vida; |
||||||
|
- liquidez; |
||||||
|
- custo total de aquisição e manutenção; |
||||||
|
- risco de reformas; |
||||||
|
- impacto financeiro mensal; |
||||||
|
- fatores psicológicos associados a possuir uma residência estável. |
||||||
|
|
||||||
|
A análise deve ser lenta, comparativa e baseada em evidências. Não existe urgência artificial para comprar. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 2. Filosofia de decisão |
||||||
|
|
||||||
|
### Princípios confirmados |
||||||
|
|
||||||
|
- Evitar comprar apenas por medo de valorização futura. |
||||||
|
- Evitar comprometer a reserva de emergência. |
||||||
|
- Não aceitar grandes concessões de planta, conservação ou funcionalidade apenas para alcançar um preço nominal. |
||||||
|
- Considerar reforma como custo financeiro, operacional e psicológico. |
||||||
|
- Preferir imóvel pronto ou com intervenções pequenas e previsíveis. |
||||||
|
- Comparar comprar com continuar alugando. |
||||||
|
- Considerar que a compra será provavelmente em uma região diferente da moradia atual. |
||||||
|
- Separar segurança emocional de justificativas financeiras. |
||||||
|
- Reconhecer o valor psicológico de possuir um imóvel físico de longo prazo, sem transformar isso em justificativa para uma compra ruim. |
||||||
|
- Tratar preço anunciado como ponto inicial de negociação, não como valor de mercado definitivo. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 3. Situação residencial atual |
||||||
|
|
||||||
|
Últimos valores consolidados no histórico do projeto: |
||||||
|
|
||||||
|
- localização atual: Asa Sul, Brasília; |
||||||
|
- imóvel alugado: aproximadamente 66 m²; |
||||||
|
- aluguel: R$ 3.200 por mês; |
||||||
|
- condomínio e IPTU não incluídos nesse valor; |
||||||
|
- reajuste anual usado nas simulações anteriores: 4,5%. |
||||||
|
|
||||||
|
Esses números devem ser confirmados antes de novas simulações, pois podem mudar. |
||||||
|
|
||||||
|
A comparação aluguel versus compra deve considerar que o padrão, a localização e a tipologia dos imóveis candidatos podem ser diferentes do imóvel alugado atual. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 4. Regiões prioritárias |
||||||
|
|
||||||
|
### 4.1 Cruzeiro |
||||||
|
|
||||||
|
O Cruzeiro, especialmente o Cruzeiro Novo, é um dos dois principais candidatos. |
||||||
|
|
||||||
|
Aspectos relevantes: |
||||||
|
|
||||||
|
- localização central; |
||||||
|
- acesso ao Plano Piloto; |
||||||
|
- blocos e quadras com diferenças importantes; |
||||||
|
- estoque antigo, com variação de conservação; |
||||||
|
- risco de imóveis que exigem reforma; |
||||||
|
- diferenças de estacionamento, garagem, elevador, ruído e posição do bloco; |
||||||
|
- apartamentos de dois e três quartos podem ter áreas totais semelhantes; |
||||||
|
- preço deve ser analisado em conjunto com planta, estado e custos de adequação. |
||||||
|
|
||||||
|
### 4.2 Jardins Mangueiral |
||||||
|
|
||||||
|
Jardins Mangueiral é o outro principal candidato. |
||||||
|
|
||||||
|
Aspectos relevantes: |
||||||
|
|
||||||
|
- casas mais novas em comparação com parte do estoque do Cruzeiro; |
||||||
|
- sensação de segurança, silêncio e residência de longo prazo; |
||||||
|
- proximidade de São Sebastião, onde vivem a namorada do usuário e familiares dela; |
||||||
|
- potencial de desenvolvimento da região em horizonte de 15 a 20 anos; |
||||||
|
- impacto de trânsito parcialmente mitigado por flexibilidade de horário e trabalho remoto; |
||||||
|
- logística compatível com o horário escolar da filha em parte dos dias; |
||||||
|
- necessidade de avaliar ruído de vizinhança e qualidade das adaptações internas. |
||||||
|
|
||||||
|
Somente casas de planta original de três quartos fazem parte do conjunto de candidatos. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 5. Preferências funcionais |
||||||
|
|
||||||
|
O imóvel ideal deve: |
||||||
|
|
||||||
|
- funcionar como residência de longo prazo; |
||||||
|
- exigir pouca ou nenhuma reforma relevante; |
||||||
|
- ter planta coerente; |
||||||
|
- evitar circulação desperdiçada; |
||||||
|
- ter boa iluminação e ventilação; |
||||||
|
- oferecer espaço social útil; |
||||||
|
- evitar duplicações de cozinha ou área gourmet sem função clara; |
||||||
|
- ter conservação compatível com o preço; |
||||||
|
- minimizar custos escondidos; |
||||||
|
- preservar flexibilidade para mudanças futuras da família. |
||||||
|
|
||||||
|
No Jardins Mangueiral, o fechamento do quintal não é automaticamente negativo. A análise deve avaliar se a área foi convertida em espaço realmente útil, ventilado e integrado. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 6. Referência financeira |
||||||
|
|
||||||
|
### Último retrato consolidado |
||||||
|
|
||||||
|
Valores informados no histórico, sujeitos a atualização: |
||||||
|
|
||||||
|
- recursos líquidos: R$ 222.000; |
||||||
|
- saldo de FGTS: R$ 130.000; |
||||||
|
- reserva mínima desejada após a compra: R$ 50.000; |
||||||
|
- capacidade de poupança: R$ 15.000 a cada seis meses; |
||||||
|
- faixa de preço mais compatível com os imóveis desejados: aproximadamente R$ 650.000 a R$ 750.000; |
||||||
|
- faixa anterior de referência: R$ 600.000. |
||||||
|
|
||||||
|
Não use esses valores como atuais sem registrar a data da análise e pedir confirmação quando a conclusão depender deles. |
||||||
|
|
||||||
|
### Regras financeiras |
||||||
|
|
||||||
|
- FGTS não deve ser tratado como liquidez disponível para emergências. |
||||||
|
- Custos de compra devem ser adicionados ao preço: |
||||||
|
- ITBI; |
||||||
|
- registro; |
||||||
|
- escritura quando aplicável; |
||||||
|
- avaliação; |
||||||
|
- tarifas; |
||||||
|
- mudança; |
||||||
|
- reparos; |
||||||
|
- mobiliário; |
||||||
|
- eventual reforma; |
||||||
|
- custos de oportunidade. |
||||||
|
- A reserva mínima de R$ 50.000 não deve ser consumida pela entrada, custos ou reforma. |
||||||
|
- A prestação não deve ser analisada isoladamente. |
||||||
|
- Comparar: |
||||||
|
- aluguel; |
||||||
|
- condomínio; |
||||||
|
- IPTU; |
||||||
|
- seguros; |
||||||
|
- manutenção; |
||||||
|
- juros; |
||||||
|
- amortização; |
||||||
|
- custo de oportunidade da entrada; |
||||||
|
- valorização; |
||||||
|
- reajuste do aluguel; |
||||||
|
- liquidez do imóvel. |
||||||
|
- Distinguir patrimônio acumulado de fluxo de caixa mensal. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 7. Financiamento e consórcio |
||||||
|
|
||||||
|
### Financiamento |
||||||
|
|
||||||
|
Simulações anteriores consideraram diferentes cenários de: |
||||||
|
|
||||||
|
- SAC; |
||||||
|
- prazos de 20, 30 e até 35 anos; |
||||||
|
- juros próximos de 9% ao ano em alguns cenários; |
||||||
|
- amortizações periódicas com FGTS; |
||||||
|
- amortizações extraordinárias em dinheiro. |
||||||
|
|
||||||
|
Toda nova simulação deve explicitar: |
||||||
|
|
||||||
|
- taxa nominal e efetiva; |
||||||
|
- indexador; |
||||||
|
- CET; |
||||||
|
- sistema de amortização; |
||||||
|
- prazo; |
||||||
|
- entrada; |
||||||
|
- custos acessórios; |
||||||
|
- uso de FGTS; |
||||||
|
- estratégia de amortização; |
||||||
|
- inflação; |
||||||
|
- retorno líquido alternativo; |
||||||
|
- horizonte de permanência. |
||||||
|
|
||||||
|
### Pró-cotista |
||||||
|
|
||||||
|
A estratégia não deve assumir que o Pró-cotista estará disponível. O usuário já percebeu que provavelmente não receberá essa linha. |
||||||
|
|
||||||
|
### Consórcio |
||||||
|
|
||||||
|
O consórcio do Banco do Brasil foi considerado como alternativa. |
||||||
|
|
||||||
|
Informações recebidas do gerente, tratadas como relatos e não como garantias: |
||||||
|
|
||||||
|
- grupos com desconto podem atrair participantes dispostos a oferecer lances elevados; |
||||||
|
- agosto e setembro podem ser meses relativamente melhores para ofertar lance; |
||||||
|
- outubro pode ficar mais competitivo por coincidir com pagamento de PLR de funcionários do BB; |
||||||
|
- muitos participantes dos consórcios do BB seriam funcionários do próprio banco. |
||||||
|
|
||||||
|
Essas afirmações precisam ser verificadas com dados do grupo sempre que forem usadas. Não extrapolar um grupo para todos os grupos. |
||||||
|
|
||||||
|
A estratégia atual é não agir com pressa e observar os percentuais de lances ao longo do tempo. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 8. Fatores familiares e logísticos |
||||||
|
|
||||||
|
- O usuário tem uma filha em idade escolar. |
||||||
|
- A namorada mora em São Sebastião e tem um filho. |
||||||
|
- Jardins Mangueiral pode reduzir a distância para a namorada e a família dela. |
||||||
|
- O usuário tem flexibilidade de trabalho e de horário em parte da semana. |
||||||
|
- A logística familiar deve ser considerada, mas não deve justificar um imóvel inadequado. |
||||||
|
- A compra deve continuar válida mesmo se circunstâncias relacionais mudarem. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 9. Horizonte |
||||||
|
|
||||||
|
A decisão deve ser avaliada como residência e patrimônio de longo prazo. |
||||||
|
|
||||||
|
Questões centrais: |
||||||
|
|
||||||
|
- O imóvel continuará funcional em 10, 15 ou 20 anos? |
||||||
|
- A localização atende a rotina sem depender de uma única circunstância atual? |
||||||
|
- O custo mensal permanece confortável em cenários adversos? |
||||||
|
- Existe reserva para manutenção, saúde, carro e emergências? |
||||||
|
- A planta permite adaptação? |
||||||
|
- A revenda é razoável? |
||||||
|
- A compra reduz ou aumenta a fragilidade financeira? |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 10. Questões em aberto |
||||||
|
|
||||||
|
Manter estas questões explícitas até que os dados permitam respondê-las: |
||||||
|
|
||||||
|
- valor atual disponível em dinheiro e FGTS; |
||||||
|
- limite confortável de prestação; |
||||||
|
- prazo máximo desejável; |
||||||
|
- custo total aceitável de aquisição; |
||||||
|
- peso relativo entre Cruzeiro e Jardins Mangueiral; |
||||||
|
- blocos e quadras prioritários no Cruzeiro; |
||||||
|
- QCs prioritárias no Mangueiral após visitas; |
||||||
|
- tolerância a imóveis sem elevador ou garagem; |
||||||
|
- impacto real do trânsito; |
||||||
|
- custo de manutenção de casas versus apartamentos; |
||||||
|
- tamanho mínimo funcional; |
||||||
|
- horizonte provável de permanência; |
||||||
|
- estratégia final entre financiamento, consórcio e espera. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 11. Fonte de verdade |
||||||
|
|
||||||
|
Este documento consolida preferências e premissas, mas não substitui uma confirmação recente quando a decisão depender de: |
||||||
|
|
||||||
|
- saldo financeiro; |
||||||
|
- renda; |
||||||
|
- taxas bancárias; |
||||||
|
- regras de FGTS; |
||||||
|
- custos cartorários; |
||||||
|
- disponibilidade de crédito; |
||||||
|
- preços atuais; |
||||||
|
- condições de consórcio; |
||||||
|
- situação familiar ou logística. |
||||||
|
|
||||||
|
Toda informação temporal deve carregar data de referência. |
||||||
@ -0,0 +1,438 @@ |
|||||||
|
# Critérios de avaliação dos imóveis |
||||||
|
|
||||||
|
## 1. Estrutura da avaliação |
||||||
|
|
||||||
|
Classificar cada critério como: |
||||||
|
|
||||||
|
- **eliminatório**: imóvel deve ser excluído; |
||||||
|
- **forte**: pode inviabilizar a compra mesmo com bom preço; |
||||||
|
- **moderado**: influencia comparação e negociação; |
||||||
|
- **informativo**: deve ser registrado, mas não decide sozinho. |
||||||
|
|
||||||
|
Não criar pesos numéricos arbitrários sem aprovação. Quando usar pontuação, mostrar os pesos, a justificativa e o efeito de cada critério. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 2. Critérios eliminatórios gerais |
||||||
|
|
||||||
|
Excluir ou colocar em quarentena analítica quando houver: |
||||||
|
|
||||||
|
- documentação incompatível ou não esclarecida; |
||||||
|
- área, tipologia ou localização materialmente divergentes; |
||||||
|
- planta que não atenda às necessidades de longo prazo; |
||||||
|
- preço total incompatível com a preservação da reserva; |
||||||
|
- problemas graves de umidade, infiltração ou estrutura; |
||||||
|
- ruído intolerável identificado em visita; |
||||||
|
- custo oculto que destrua a vantagem aparente; |
||||||
|
- anúncio sem informação mínima para comparação; |
||||||
|
- indícios relevantes de fraude ou inconsistência. |
||||||
|
|
||||||
|
Um imóvel não deve ser eliminado apenas por falta de dados no anúncio. Nesse caso, classifique-o como pendente de verificação. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 3. Critérios gerais de qualidade |
||||||
|
|
||||||
|
### 3.1 Planta e funcionalidade |
||||||
|
|
||||||
|
Avaliar: |
||||||
|
|
||||||
|
- distribuição dos cômodos; |
||||||
|
- privacidade dos quartos; |
||||||
|
- largura de circulação; |
||||||
|
- áreas mortas; |
||||||
|
- integração social; |
||||||
|
- capacidade de mobiliamento; |
||||||
|
- armazenamento; |
||||||
|
- número e localização de banheiros; |
||||||
|
- ventilação cruzada; |
||||||
|
- iluminação natural; |
||||||
|
- possibilidade de adaptação futura; |
||||||
|
- conflito entre portas, móveis e circulação; |
||||||
|
- uso real de varandas, quintais e áreas gourmet. |
||||||
|
|
||||||
|
### 3.2 Estado de conservação |
||||||
|
|
||||||
|
Registrar separadamente: |
||||||
|
|
||||||
|
- estrutura; |
||||||
|
- elétrica; |
||||||
|
- hidráulica; |
||||||
|
- revestimentos; |
||||||
|
- esquadrias; |
||||||
|
- telhado; |
||||||
|
- impermeabilização; |
||||||
|
- pintura; |
||||||
|
- armários; |
||||||
|
- climatização; |
||||||
|
- equipamentos; |
||||||
|
- sinais de reforma improvisada. |
||||||
|
|
||||||
|
Não resumir tudo como “reformado”. Uma reforma estética pode esconder sistemas antigos. |
||||||
|
|
||||||
|
### 3.3 Custos de adequação |
||||||
|
|
||||||
|
Estimar: |
||||||
|
|
||||||
|
- reparos imediatos; |
||||||
|
- intervenções em até dois anos; |
||||||
|
- mobiliário obrigatório; |
||||||
|
- eletrodomésticos; |
||||||
|
- mudança; |
||||||
|
- custo de desocupação, quando aplicável; |
||||||
|
- custo de tempo e transtorno; |
||||||
|
- contingência de obra. |
||||||
|
|
||||||
|
Aplicar margem de segurança para reformas. Não usar apenas o orçamento mais otimista. |
||||||
|
|
||||||
|
### 3.4 Conforto ambiental |
||||||
|
|
||||||
|
Avaliar em horários diferentes: |
||||||
|
|
||||||
|
- incidência solar; |
||||||
|
- calor; |
||||||
|
- ventilação; |
||||||
|
- iluminação; |
||||||
|
- ruído; |
||||||
|
- odores; |
||||||
|
- privacidade; |
||||||
|
- proximidade de vias; |
||||||
|
- fluxo de pedestres; |
||||||
|
- iluminação noturna; |
||||||
|
- segurança percebida; |
||||||
|
- drenagem e risco de alagamento. |
||||||
|
|
||||||
|
### 3.5 Liquidez e revenda |
||||||
|
|
||||||
|
Considerar: |
||||||
|
|
||||||
|
- tipologia; |
||||||
|
- padrão da região; |
||||||
|
- facilidade de financiamento; |
||||||
|
- regularidade documental; |
||||||
|
- garagem; |
||||||
|
- elevador; |
||||||
|
- posição; |
||||||
|
- conservação; |
||||||
|
- faixa de preço; |
||||||
|
- público comprador provável; |
||||||
|
- excesso de personalização; |
||||||
|
- limitações permanentes de planta. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 4. Cruzeiro Novo |
||||||
|
|
||||||
|
### 4.1 Pontos a registrar |
||||||
|
|
||||||
|
- quadra; |
||||||
|
- bloco; |
||||||
|
- posição no bloco; |
||||||
|
- andar; |
||||||
|
- orientação solar; |
||||||
|
- vista; |
||||||
|
- proximidade de vias e comércio; |
||||||
|
- ruído; |
||||||
|
- elevador; |
||||||
|
- garagem ou estacionamento; |
||||||
|
- estado das áreas comuns; |
||||||
|
- taxa de condomínio; |
||||||
|
- obras previstas; |
||||||
|
- idade e conservação do prédio; |
||||||
|
- prumadas; |
||||||
|
- elétrica; |
||||||
|
- fachada; |
||||||
|
- acessibilidade; |
||||||
|
- área privativa e área total; |
||||||
|
- número original de quartos; |
||||||
|
- alterações de planta; |
||||||
|
- ocupação atual; |
||||||
|
- situação documental. |
||||||
|
|
||||||
|
### 4.2 Dois versus três quartos |
||||||
|
|
||||||
|
No Cruzeiro, apartamentos anunciados como dois ou três quartos podem ter áreas semelhantes. |
||||||
|
|
||||||
|
A análise deve verificar: |
||||||
|
|
||||||
|
- planta original; |
||||||
|
- quarto revertido; |
||||||
|
- dependência transformada; |
||||||
|
- sala dividida; |
||||||
|
- ventilação do cômodo adicional; |
||||||
|
- dimensão real dos quartos; |
||||||
|
- impacto na circulação; |
||||||
|
- valor de revenda. |
||||||
|
|
||||||
|
Não classificar automaticamente três quartos como superior se o terceiro cômodo for inadequado. |
||||||
|
|
||||||
|
### 4.3 Elevador e garagem |
||||||
|
|
||||||
|
Ausência de elevador ou garagem é uma concessão relevante, não um detalhe. |
||||||
|
|
||||||
|
Registrar: |
||||||
|
|
||||||
|
- andar; |
||||||
|
- número de lances de escada; |
||||||
|
- acessibilidade futura; |
||||||
|
- facilidade de mudança; |
||||||
|
- uso por idosos; |
||||||
|
- disponibilidade real de estacionamento; |
||||||
|
- segurança ao estacionar; |
||||||
|
- regras do condomínio. |
||||||
|
|
||||||
|
O efeito no preço deve ser analisado com imóveis comparáveis. |
||||||
|
|
||||||
|
### 4.4 Reforma |
||||||
|
|
||||||
|
Imóveis antigos podem exigir modernização invisível no anúncio. |
||||||
|
|
||||||
|
Durante a visita, verificar: |
||||||
|
|
||||||
|
- quadro elétrico; |
||||||
|
- aterramento; |
||||||
|
- tomadas; |
||||||
|
- tubulações; |
||||||
|
- registros; |
||||||
|
- pressão de água; |
||||||
|
- vazamentos; |
||||||
|
- esquadrias; |
||||||
|
- ruído entre unidades; |
||||||
|
- estado da laje; |
||||||
|
- gás; |
||||||
|
- ventilação de banheiros; |
||||||
|
- infiltrações em fachadas; |
||||||
|
- histórico de obras do bloco. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 5. Jardins Mangueiral |
||||||
|
|
||||||
|
### 5.1 Filtro de tipologia |
||||||
|
|
||||||
|
Somente considerar casas com **planta de três quartos**. |
||||||
|
|
||||||
|
Casas originalmente de dois quartos devem ser excluídas, mesmo quando reformadas ou anunciadas como tendo três quartos. |
||||||
|
|
||||||
|
A validação pode usar: |
||||||
|
|
||||||
|
- planta original; |
||||||
|
- posição da escada; |
||||||
|
- dimensões; |
||||||
|
- fachada; |
||||||
|
- implantação; |
||||||
|
- documentos; |
||||||
|
- comparação com unidades padrão; |
||||||
|
- fotografias. |
||||||
|
|
||||||
|
### 5.2 Quintal fechado e ampliações |
||||||
|
|
||||||
|
A perda do quintal original não é automaticamente um problema. |
||||||
|
|
||||||
|
A conversão deve ser considerada positiva quando cria: |
||||||
|
|
||||||
|
- sala ampliada; |
||||||
|
- espaço de convivência; |
||||||
|
- ambiente de refeições; |
||||||
|
- escritório; |
||||||
|
- área social ventilada; |
||||||
|
- integração útil com a casa; |
||||||
|
- armazenamento funcional. |
||||||
|
|
||||||
|
Penalizar quando a conversão: |
||||||
|
|
||||||
|
- duplica cozinha ou cooktop sem necessidade; |
||||||
|
- cria circulação inútil; |
||||||
|
- reduz ventilação e iluminação; |
||||||
|
- aumenta calor; |
||||||
|
- usa material de baixa qualidade; |
||||||
|
- compromete impermeabilização; |
||||||
|
- dificulta manutenção; |
||||||
|
- cria ambiente sem função clara; |
||||||
|
- prejudica privacidade; |
||||||
|
- ocupa todo o espaço sem compensação funcional. |
||||||
|
|
||||||
|
### 5.3 Área gourmet |
||||||
|
|
||||||
|
Não atribuir valor automático ao rótulo “área gourmet”. |
||||||
|
|
||||||
|
Avaliar: |
||||||
|
|
||||||
|
- frequência provável de uso; |
||||||
|
- integração com a cozinha; |
||||||
|
- redundância de equipamentos; |
||||||
|
- exaustão; |
||||||
|
- ventilação; |
||||||
|
- limpeza; |
||||||
|
- circulação; |
||||||
|
- capacidade de receber pessoas; |
||||||
|
- qualidade da execução; |
||||||
|
- manutenção. |
||||||
|
|
||||||
|
Um segundo cooktop ao lado da cozinha principal pode ser redundante. |
||||||
|
|
||||||
|
### 5.4 QCs e acesso a serviços |
||||||
|
|
||||||
|
Relatos de moradores usados como referência anedótica: |
||||||
|
|
||||||
|
- QCs 8 e 9: percebidas como mais fortes para acesso prático a comércio e serviços; |
||||||
|
- QCs 6 e 7: alternativas consideradas; |
||||||
|
- QCs 10 e 11: também consideradas boas. |
||||||
|
|
||||||
|
Essas preferências não substituem verificação de campo. |
||||||
|
|
||||||
|
Para cada QC, medir ou registrar: |
||||||
|
|
||||||
|
- distância a comércio; |
||||||
|
- acesso a mercado, farmácia e serviços; |
||||||
|
- saída viária; |
||||||
|
- tempo em horários relevantes; |
||||||
|
- transporte; |
||||||
|
- topografia; |
||||||
|
- iluminação; |
||||||
|
- tráfego interno; |
||||||
|
- ruído; |
||||||
|
- estacionamento; |
||||||
|
- áreas comuns; |
||||||
|
- manutenção do condomínio. |
||||||
|
|
||||||
|
### 5.5 Ruído e vizinhança |
||||||
|
|
||||||
|
Avaliar explicitamente: |
||||||
|
|
||||||
|
- paredes compartilhadas; |
||||||
|
- ruído de passos e móveis; |
||||||
|
- música; |
||||||
|
- festas; |
||||||
|
- cães; |
||||||
|
- crianças; |
||||||
|
- áreas de convivência; |
||||||
|
- quadras esportivas; |
||||||
|
- comércio; |
||||||
|
- portarias; |
||||||
|
- vias internas; |
||||||
|
- motos; |
||||||
|
- coleta de lixo; |
||||||
|
- obras; |
||||||
|
- templos; |
||||||
|
- escolas; |
||||||
|
- bares. |
||||||
|
|
||||||
|
Fazer visitas em mais de um horário quando possível, incluindo noite ou fim de semana. |
||||||
|
|
||||||
|
Perguntar aos moradores sem depender apenas do corretor. |
||||||
|
|
||||||
|
### 5.6 Reformas e regularidade |
||||||
|
|
||||||
|
Verificar: |
||||||
|
|
||||||
|
- fechamento de área externa; |
||||||
|
- cobertura; |
||||||
|
- drenagem; |
||||||
|
- calhas; |
||||||
|
- telhado; |
||||||
|
- impermeabilização; |
||||||
|
- ventilação; |
||||||
|
- padrão elétrico; |
||||||
|
- hidráulica; |
||||||
|
- alterações estruturais; |
||||||
|
- aprovação condominial; |
||||||
|
- regularidade documental; |
||||||
|
- impacto em seguro e financiamento. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 6. Checklist de visita |
||||||
|
|
||||||
|
### Antes da visita |
||||||
|
|
||||||
|
- salvar anúncio e data; |
||||||
|
- registrar preço; |
||||||
|
- registrar condomínio e IPTU; |
||||||
|
- conferir área e quartos; |
||||||
|
- buscar histórico do anúncio; |
||||||
|
- verificar anúncios duplicados; |
||||||
|
- mapear entorno; |
||||||
|
- listar dúvidas. |
||||||
|
|
||||||
|
### Durante a visita |
||||||
|
|
||||||
|
- fotografar com autorização; |
||||||
|
- registrar ruído com janelas abertas e fechadas; |
||||||
|
- testar torneiras, descargas, chuveiros e registros; |
||||||
|
- observar umidade e odores; |
||||||
|
- verificar tomadas e quadro; |
||||||
|
- medir cômodos críticos; |
||||||
|
- conferir incidência solar; |
||||||
|
- observar circulação; |
||||||
|
- avaliar armazenamento; |
||||||
|
- verificar sinal de celular e internet; |
||||||
|
- examinar garagem ou estacionamento; |
||||||
|
- conversar com porteiro ou moradores quando apropriado; |
||||||
|
- perguntar motivo da venda; |
||||||
|
- perguntar tempo de anúncio; |
||||||
|
- perguntar reformas e documentação; |
||||||
|
- verificar taxas extraordinárias. |
||||||
|
|
||||||
|
### Depois da visita |
||||||
|
|
||||||
|
- registrar impressão no mesmo dia; |
||||||
|
- separar fatos de percepções; |
||||||
|
- estimar custos; |
||||||
|
- comparar com alternativas reais; |
||||||
|
- listar riscos; |
||||||
|
- definir preço máximo; |
||||||
|
- registrar pendências documentais; |
||||||
|
- evitar decisão motivada apenas por entusiasmo. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 7. Negociação |
||||||
|
|
||||||
|
O preço máximo deve considerar: |
||||||
|
|
||||||
|
- valor comparável; |
||||||
|
- estado; |
||||||
|
- custos de compra; |
||||||
|
- reforma; |
||||||
|
- liquidez; |
||||||
|
- concessões; |
||||||
|
- risco; |
||||||
|
- tempo de anúncio; |
||||||
|
- histórico de preço; |
||||||
|
- condição de pagamento; |
||||||
|
- custo de oportunidade. |
||||||
|
|
||||||
|
Não negociar com base apenas no percentual de desconto. |
||||||
|
|
||||||
|
O desconto nominal deve ser comparado ao custo real de corrigir as deficiências. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 8. Saída padrão por imóvel |
||||||
|
|
||||||
|
Cada ficha analítica deve conter: |
||||||
|
|
||||||
|
- URL e identificador; |
||||||
|
- data de coleta; |
||||||
|
- região, quadra, bloco ou QC; |
||||||
|
- tipologia original; |
||||||
|
- área; |
||||||
|
- quartos; |
||||||
|
- garagem; |
||||||
|
- elevador; |
||||||
|
- preço; |
||||||
|
- condomínio; |
||||||
|
- IPTU; |
||||||
|
- preço por m² válido ou não; |
||||||
|
- histórico de preço; |
||||||
|
- possíveis duplicatas; |
||||||
|
- pontos fortes; |
||||||
|
- pontos fracos; |
||||||
|
- reformas; |
||||||
|
- custo estimado de adequação; |
||||||
|
- riscos; |
||||||
|
- pendências; |
||||||
|
- posição relativa aos comparáveis; |
||||||
|
- preço máximo sugerido, quando houver dados; |
||||||
|
- decisão: excluir, monitorar, visitar, negociar ou candidato forte. |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
HTTP/2 403 |
||||||
|
date: Mon, 13 Jul 2026 12:29:34 GMT |
||||||
|
content-type: text/html; charset=UTF-8 |
||||||
|
content-length: 5753 |
||||||
|
accept-ch: Sec-CH-UA-Bitness, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA, UA-Bitness, UA-Arch, UA-Full-Version, UA-Mobile, UA-Model, UA-Platform-Version, UA-Platform, UA |
||||||
|
cf-mitigated: challenge |
||||||
|
content-security-policy: default-src 'none'; script-src 'nonce-NDFRcU4DySSBatDK11iuTs' 'unsafe-eval' https://challenges.cloudflare.com; script-src-attr 'none'; style-src 'unsafe-inline'; img-src 'self' https://challenges.cloudflare.com; connect-src 'self' https://challenges.cloudflare.com; frame-src 'self' https://challenges.cloudflare.com blob:; child-src 'self' https://challenges.cloudflare.com blob:; worker-src blob:; form-action http: https:; base-uri 'self' |
||||||
|
server: cloudflare |
||||||
|
critical-ch: Sec-CH-UA-Bitness, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA, UA-Bitness, UA-Arch, UA-Full-Version, UA-Mobile, UA-Model, UA-Platform-Version, UA-Platform, UA |
||||||
|
cross-origin-embedder-policy: require-corp |
||||||
|
cross-origin-opener-policy: same-origin |
||||||
|
cross-origin-resource-policy: same-origin |
||||||
|
origin-agent-cluster: ?1 |
||||||
|
permissions-policy: accelerometer=(),camera=(),clipboard-read=(),clipboard-write=(),geolocation=(),gyroscope=(),hid=(),magnetometer=(),microphone=(),payment=(),publickey-credentials-get=(),screen-wake-lock=(),serial=(),sync-xhr=(),usb=(),xr-spatial-tracking=* |
||||||
|
referrer-policy: same-origin |
||||||
|
server-timing: chlray;desc="a1a84b426c72f17f" |
||||||
|
x-content-type-options: nosniff |
||||||
|
x-frame-options: SAMEORIGIN |
||||||
|
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=RJh2QutRJQCG335sylJHf3W5WhCN3A6QHLF%2FYrIaRQuW0iICamlOrJMou8pb%2BZtSwNZ7YHwnR8mq2PmVZV8LhatfWgSaoMSttSNrqvk9gW5uhc7qag5fNebLVMYN7iMuRjh8b4z2"}]} |
||||||
|
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} |
||||||
|
cf-ray: a1a84b426c72f17f-GRU |
||||||
|
|
||||||
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"folders": [ |
||||||
|
{ |
||||||
|
"path": "." |
||||||
|
} |
||||||
|
], |
||||||
|
"settings": {} |
||||||
|
} |
||||||
@ -0,0 +1,172 @@ |
|||||||
|
# Ranking preliminar — apartamentos no Cruzeiro Novo |
||||||
|
|
||||||
|
**Data de referência do snapshot:** 17/07/2026 |
||||||
|
**Relatório revisado:** 17/07/2026 |
||||||
|
**Unidade principal:** imóvel físico provável, não snapshot nem anúncio isolado |
||||||
|
**Finalidade:** priorizar visitas; não estimar preço negociado nem substituir vistoria |
||||||
|
|
||||||
|
## Pergunta |
||||||
|
|
||||||
|
Quais são os dez imóveis do Cruzeiro Novo que mais merecem atenção segundo as preferências e regras do projeto, dando às fotografias importância comparável à dos dados estruturados? |
||||||
|
|
||||||
|
## Universo e método |
||||||
|
|
||||||
|
Foram avaliados os 65 anúncios encontrados pela busca: |
||||||
|
|
||||||
|
`https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos` |
||||||
|
|
||||||
|
A coleta cobre um único dia e um único portal. O banco foi aberto somente em leitura. Os campos foram complementados com os HTMLs brutos, pois localização e descrição no SQLite têm limitações já documentadas em `docs/ACTUAL_SCHEMA.md`. |
||||||
|
|
||||||
|
Na coleta concluída em 17/07, os 65 anúncios do Cruzeiro permaneceram ativos e nenhum anúncio novo entrou. O único preço alterado foi o do anúncio `1372539`, de R$ 572.500 para R$ 569.000; ele continua fora do ranking porque a descrição confirma planta de três quartos convertida em dois. Assim, a ordem foi mantida após revisão das mudanças materiais. |
||||||
|
|
||||||
|
Não foi criada pontuação numérica, pois os pesos não foram aprovados. A ordem é uma **prioridade de visita ajustada por risco**, aplicando: |
||||||
|
|
||||||
|
1. critérios eliminatórios e inconsistências; |
||||||
|
2. planta funcional de três quartos; |
||||||
|
3. indícios de reforma relevante ou custos escondidos; |
||||||
|
4. iluminação, ventilação, distribuição e conservação visíveis; |
||||||
|
5. andar, elevador e garagem; |
||||||
|
6. preço, área e condomínio anunciados; |
||||||
|
7. qualidade e completude da evidência. |
||||||
|
|
||||||
|
### Revisão fotográfica |
||||||
|
|
||||||
|
Em 17/07/2026, a primeira foto dos dez escolhidos foi auditada especificamente para marcas de “vendido” ou “negócio fechado”. Nenhuma das dez apresentou esse sinal. |
||||||
|
|
||||||
|
A primeira passagem examinou até 16 imagens de cada um dos 65 anúncios, totalizando 1.029 arquivos. Depois, foram examinadas todas as 532 fotos dos 17 finalistas. Descontada a sobreposição entre as etapas, 1.289 arquivos distintos foram efetivamente vistos. |
||||||
|
|
||||||
|
As imagens foram usadas para observar conservação, coerência da reforma, luz, ventilação aparente, tamanho relativo dos quartos, circulação, integração social, cozinha, banheiros, área de serviço, fachada e estacionamento. Fotografias não permitem validar elétrica, hidráulica, infiltração, ruído, estrutura ou documentação. |
||||||
|
|
||||||
|
Muitos anúncios incluem, depois das fotos do imóvel, imagens genéricas do portal, logos e até cartões de outros imóveis marcados como “vendido”. Esses ativos foram desconsiderados na avaliação visual do apartamento. |
||||||
|
|
||||||
|
## Ranking |
||||||
|
|
||||||
|
### 1. Anúncio 1345146 — SHCES 1207, bloco A |
||||||
|
|
||||||
|
- **Anunciado:** R$ 670.000; 65 m²; condomínio R$ 605; 3 quartos, 1 suíte; 4º andar. |
||||||
|
- **Por que lidera:** é o conjunto mais equilibrado entre reforma pronta, planta preservada e acessibilidade. O anúncio declara elevador, diferencial forte para uso de longo prazo. |
||||||
|
- **Fotos:** reforma coerente e bem conservada; cozinha planejada, área de serviço separada, dois banheiros atualizados, três quartos reconhecíveis e boa luz. Os dormitórios e a área de serviço parecem compactos, mas funcionais. |
||||||
|
- **Pendências:** confirmar funcionamento/obras do elevador, posição solar, área da matrícula, estacionamento/garagem e sistemas elétrico e hidráulico. |
||||||
|
- **Ação:** candidato forte; visitar primeiro. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1207-bloco-a-1345146 |
||||||
|
|
||||||
|
### 2. Anúncio 1232283 — SHCES 309, bloco B |
||||||
|
|
||||||
|
- **Anunciado:** R$ 600.000; 70 m²; condomínio R$ 497; 3 quartos, 1 suíte; 3º andar; 2 vagas; nascente e vazado. |
||||||
|
- **Por que está no topo:** maior área entre os primeiros colocados, preço competitivo, duas vagas anunciadas e reforma recente. O anúncio informa vista livre e ocupação imediata. |
||||||
|
- **Fotos:** ambientes claros, armários abundantes, dois banheiros modernos e reforma consistente. Um dos dormitórios parece estreito; a qualidade da circulação precisa ser sentida presencialmente. |
||||||
|
- **Pendências:** confirmar se as duas vagas são privativas/vinculadas e se há elevador — ele não é informado. No 3º andar, sua ausência é concessão forte. |
||||||
|
- **Ação:** candidato forte; visitar, condicionado à confirmação das vagas e acessibilidade. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-bloco-b-1232283 |
||||||
|
|
||||||
|
### 3. Anúncio 1357415 — SHCES 1109 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 630.000; 67,2 m²; condomínio R$ 350; 3 quartos, 1 suíte; 4º andar; nascente e vazado. |
||||||
|
- **Por que se destaca:** reforma visualmente mais homogênea que a maioria, bloco revitalizado, vista livre, boa ventilação e condomínio anunciado baixo. |
||||||
|
- **Fotos:** sala social bem resolvida, cozinha planejada, marcenaria útil, dois banheiros atualizados, quartos claros e conservação convincente. Não aparecem sinais evidentes de obra imediata. |
||||||
|
- **Pendências:** não há elevador informado e o imóvel está no 4º andar; confirmar isso antes da visita. Verificar estacionamento, escopo real da reforma e autenticidade do condomínio de R$ 350. |
||||||
|
- **Ação:** candidato forte se a escada for aceitável; caso contrário, rebaixar fortemente. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-1357415 |
||||||
|
|
||||||
|
### 4. Anúncio 1357633 — SHCES 309 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 600.000; 64 m²; 3 quartos; 2º andar; nascente, vazado e de canto. |
||||||
|
- **Por que se destaca:** o anúncio declara reforma de elétrica, hidráulica, pisos e acabamentos, além de documentação regular. O 2º andar reduz o impacto provável da ausência de elevador. |
||||||
|
- **Fotos:** acabamento moderno e uniforme, boa marcenaria, iluminação natural e três dormitórios aparentes. A planta permanece convencional e funcional; não há suíte anunciada, mas há banheiro de serviço. |
||||||
|
- **Pendências:** condomínio, IPTU, garagem e elevador estão ausentes. Confirmar notas/escopo da reforma e ventilação dos banheiros. |
||||||
|
- **Ação:** candidato forte; visitar. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1357633 |
||||||
|
|
||||||
|
### 5. Anúncio 1376958 — SHCES 911, bloco B |
||||||
|
|
||||||
|
- **Anunciado:** R$ 599.000; 62 m²; condomínio R$ 600; 3 quartos, 1 suíte; 2º andar; nascente e vazado. |
||||||
|
- **Por que se destaca:** combina preço baixo no grupo reformado, andar razoável, suíte e ventilação cruzada declarada. |
||||||
|
- **Fotos:** sala ampla para a metragem, cozinha e banheiros atualizados, boa vista e luz. O quarto com beliche aparenta ser pequeno, mas utilizável; a área de serviço é funcional, embora simples. |
||||||
|
- **Pendências:** prédio sem elevador segundo a descrição e sem garagem informada. Confirmar área privativa, ruído da via visível e o que está incluído no condomínio. |
||||||
|
- **Ação:** visitar. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-b-1376958 |
||||||
|
|
||||||
|
### 6. Anúncio 1345903 — SHCES 1303 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 565.000; 64,5 m²; condomínio R$ 529; 3 quartos, 1 suíte; 1º andar; vazado. |
||||||
|
- **Por que se destaca:** menor preço do top 10, acessibilidade melhor por estar no 1º andar, suíte e informação explícita de elétrica e esquadrias novas. |
||||||
|
- **Fotos:** imóvel cuidado e aparentemente habitável sem obra imediata. Banheiros estão atualizados, mas cozinha, pisos e parte da marcenaria têm linguagem mais antiga. A planta parece íntegra e os três quartos são visíveis. |
||||||
|
- **Pendências:** somente estacionamento público; confirmar ventilação, ruído do 1º andar, hidráulica e segurança das janelas. |
||||||
|
- **Ação:** visitar com foco em custo real de modernização opcional. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1303-1345903 |
||||||
|
|
||||||
|
### 7. Anúncio 1339324 — SHCES 703 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 665.000; 64 m²; condomínio R$ 390; 3 quartos, 1 suíte; 3º andar. |
||||||
|
- **Por que se destaca:** reforma pronta, climatização, condomínio anunciado baixo e excelente aproveitamento de marcenaria. |
||||||
|
- **Fotos:** uma das melhores apresentações: integração social coerente, cozinha funcional, dois banheiros bem executados, três quartos utilizáveis, boa iluminação e áreas comuns cuidadas. A circulação íntima parece estreita. |
||||||
|
- **Pendências:** elevador, garagem, posição solar e escopo técnico da reforma não são informados. O 3º andar sem elevador seria concessão relevante. |
||||||
|
- **Ação:** visitar após esclarecer acessibilidade e estacionamento. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-703-1339324 |
||||||
|
|
||||||
|
### 8. Imóvel provável dos anúncios 1370889 e 1375200 — SHCES 207 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 695.000; 62 m²; condomínio entre R$ 435 e R$ 453; 3 quartos, 1 suíte. Um anúncio informa 2º andar. |
||||||
|
- **Por que se destaca:** prédio reformado, planta social integrada, suíte e condição pronta para morar. |
||||||
|
- **Fotos:** os dois anúncios mostram o mesmo apartamento com alta confiança visual. Sala/cozinha integradas funcionam bem, há área de serviço independente, dois banheiros atualizados e três dormitórios. A reforma parece consistente, embora bastante personalizada. |
||||||
|
- **Pendências:** confirmar formalmente que os anúncios são da mesma unidade, andar, orientação solar, área, garagem e elevador. A duplicidade não deve inflar a oferta. |
||||||
|
- **Ação:** visitar uma única vez após confirmar o anúncio primário. |
||||||
|
- **URLs:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370889 e https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1375200 |
||||||
|
|
||||||
|
### 9. Anúncio 1372268 — SHCES 305, bloco I |
||||||
|
|
||||||
|
- **Anunciado:** R$ 595.000; 62,61 m²; condomínio R$ 460; 3 quartos; 1º andar; canto e vazado; 2 vagas. |
||||||
|
- **Por que entra:** duas vagas anunciadas e 1º andar são diferenciais duráveis. O preço deixa margem maior para atualização que os reformados mais caros. |
||||||
|
- **Fotos:** planta convencional, clara e aparentemente conservada; cozinha e banheiros são funcionais, porém mais antigos. Não há sinal visual de obra estrutural, mas a modernização seria maior do que nos primeiros colocados. |
||||||
|
- **Pendências:** confirmar documentalmente as duas vagas, ventilação, elétrica, hidráulica e orçamento real de adequação. |
||||||
|
- **Ação:** visitar como alternativa de valor; não assumir que o desconto cobre reforma sem orçamento. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-bloco-i-1372268 |
||||||
|
|
||||||
|
### 10. Anúncio 1373131 — SHCES 305 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 725.000; 65 m²; condomínio R$ 420; 3 quartos; 2º andar; nascente e vazado; sem elevador. |
||||||
|
- **Por que entra:** é a reforma tecnicamente mais bem descrita: elétrica e hidráulica renovadas, projeto de arquiteta, boa orientação e ventilação cruzada. |
||||||
|
- **Fotos:** execução visual limpa e consistente, cozinha e banheiro novos, três quartos preservados e boa luminosidade. Não possui suíte anunciada e a linguagem totalmente branca deve ser avaliada sem o efeito de grande-angular. |
||||||
|
- **Pendências:** não há garagem informada; preço é alto para unidade sem suíte, elevador ou vaga. Confirmar documentação da reforma e medidas reais dos ambientes. |
||||||
|
- **Ação:** visitar somente se a condição pronta justificar o prêmio pedido. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-1373131 |
||||||
|
|
||||||
|
## Quase entraram |
||||||
|
|
||||||
|
- **[1382466](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-101-bloco-c-1382466), R$ 650.000:** boa reforma aparente e uma vaga, mas faltam fotos da cozinha e informações de andar, elevador e orientação; aprofundar antes de substituir alguém do top 10. |
||||||
|
- **[1290510](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-a-1290510), R$ 640.000:** três quartos com suíte, vazado e reformado, mas fica no 4º andar sem elevador informado e os valores de condomínio/IPTU foram claramente extraídos como R$ 1; precisa de confirmação. |
||||||
|
- **[1371813](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-bloco-d-1371813), R$ 709.000:** reforma moderna e 68 m², mas o conjunto de fotos é curto e não esclarece bem três quartos, área de serviço, andar, garagem ou elevador. |
||||||
|
|
||||||
|
## Quarentenas e exclusões relevantes |
||||||
|
|
||||||
|
- **1373346:** é um imóvel no Noroeste capturado indevidamente pela busca; excluído. |
||||||
|
- **1212351, 1338088, 1259968, 1315328 e 1372539:** planta de três quartos convertida em dois ou sala/escritório; não priorizados para residência de longo prazo. |
||||||
|
- **1313649:** anunciado na descrição como dois quartos apesar do campo estruturado indicar três; condomínio de R$ 1.172; excluído até esclarecer planta. |
||||||
|
- **1370861:** três quartos originais transformados em suíte master e escritório; não atende à funcionalidade desejada. |
||||||
|
- **1365929, 1161400, 1278826 e similares:** fotos e descrição indicam reforma relevante, sem desconto claramente suficiente frente às alternativas prontas. |
||||||
|
- **1366910, 1372290 e 1383534:** 22–35 fotos visualmente similares indicam o mesmo imóvel provável, mas os anúncios divergem entre SHCES 207 e SHCES 303 e entre 63 e 74 m². O conjunto seria competitivo, porém permanece em quarentena até confirmar endereço, área e anúncio válido. |
||||||
|
- **1370948, 1371797 e 1372167:** grupo provável de anúncios duplicados, sem vantagem suficiente sobre o top 10. |
||||||
|
- **1357834 e 1358242:** duplicata provável; há ainda um terceiro anúncio visualmente semelhante, 1382413. |
||||||
|
- **1372907, 1348535 e 1380850:** elevador, garagem ou área podem ser atrativos, mas os preços de R$ 820 mil a R$ 990 mil não vêm acompanhados de vantagem funcional suficiente para superar as opções anteriores. A conclusão financeira depende de atualização dos saldos e limites do usuário. |
||||||
|
|
||||||
|
Uma imagem com selo “vendido” foi tratada como sinal de inconsistência, não como prova de venda, porque muitos anúncios contêm ativos genéricos de outros imóveis. |
||||||
|
|
||||||
|
## Limitações e verificações para visita |
||||||
|
|
||||||
|
O ranking é sensível a cinco informações ainda não confirmadas: tolerância pessoal a escadas, existência jurídica das vagas, conceito correto de área, qualidade invisível das reformas e limite financeiro atual. Antes de qualquer proposta: |
||||||
|
|
||||||
|
1. conferir matrícula, área privativa e vagas; |
||||||
|
2. testar elétrica, hidráulica, pressão, esquadrias e sinais de infiltração; |
||||||
|
3. medir os três quartos e validar que permanecem funcionais; |
||||||
|
4. visitar em horário de tráfego e à noite para ruído e estacionamento; |
||||||
|
5. verificar elevador, obras e atas do condomínio; |
||||||
|
6. confirmar condomínio, IPTU, taxas extras e orientação solar; |
||||||
|
7. comparar preço máximo somente depois de atualizar recursos, FGTS, reserva e prestação confortável. |
||||||
|
|
||||||
|
## Reprodutibilidade |
||||||
|
|
||||||
|
- `analysis.py`: extrai banco e HTMLs em leitura, produzindo TSV. |
||||||
|
- `photo_similarity.py`: sinaliza duplicatas por hash visual simples; os resultados exigem revisão humana. |
||||||
|
- `query.sql`: consultas principais de universo e qualidade. |
||||||
|
- `parameters.json`: parâmetros e fingerprint do snapshot. |
||||||
|
- `results.csv`: ranking em formato tabular. |
||||||
@ -0,0 +1,133 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
"""Read-only extraction helper for the Cruzeiro ranking. |
||||||
|
|
||||||
|
The database is opened in a consistent read transaction so WAL content is |
||||||
|
included. The script only prints a TSV and never updates the database. |
||||||
|
""" |
||||||
|
|
||||||
|
from __future__ import annotations |
||||||
|
|
||||||
|
import gzip |
||||||
|
import argparse |
||||||
|
import re |
||||||
|
import sqlite3 |
||||||
|
import sys |
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
from bs4 import BeautifulSoup |
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2] |
||||||
|
DB_PATH = ROOT / "dfimoveis_data" / "dfimoveis.sqlite3" |
||||||
|
DATA_ROOT = DB_PATH.parent |
||||||
|
SEARCH_FRAGMENT = "/cruzeiro/novo/" |
||||||
|
|
||||||
|
|
||||||
|
def clean(value: str | None) -> str: |
||||||
|
return re.sub(r"\s+", " ", value or "").strip() |
||||||
|
|
||||||
|
|
||||||
|
def sanitize_description(value: str) -> str: |
||||||
|
value = re.sub(r"\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b", "[email]", value) |
||||||
|
value = re.sub( |
||||||
|
r"(?:\(\d{2}\)|\b\d{2}\b)\s*(?:\d[\s.-]*){5,11}\d", |
||||||
|
"[telefone]", |
||||||
|
value, |
||||||
|
) |
||||||
|
value = re.sub(r"\bCRECI\b.{0,30}", "", value, flags=re.IGNORECASE) |
||||||
|
return clean(value) |
||||||
|
|
||||||
|
|
||||||
|
def parse_html(relative_path: str) -> tuple[str, str, str]: |
||||||
|
with gzip.open(DATA_ROOT / relative_path, "rt", encoding="utf-8", errors="replace") as fh: |
||||||
|
soup = BeautifulSoup(fh.read(), "lxml") |
||||||
|
|
||||||
|
description_node = soup.select_one("div.assined-imv") |
||||||
|
description = "" |
||||||
|
if description_node: |
||||||
|
for node in description_node.select("span, a, button"): |
||||||
|
node.decompose() |
||||||
|
description = sanitize_description(description_node.get_text(" ", strip=True)) |
||||||
|
|
||||||
|
details: list[str] = [] |
||||||
|
for item in soup.select("ul.details-text li"): |
||||||
|
text = clean(item.get_text(" ", strip=True)) |
||||||
|
if text and text not in details: |
||||||
|
details.append(text) |
||||||
|
|
||||||
|
address_node = soup.select_one('[itemprop="address"]') |
||||||
|
address = clean(address_node.get_text(" ", strip=True) if address_node else "") |
||||||
|
return description, " | ".join(details), address |
||||||
|
|
||||||
|
|
||||||
|
def main() -> int: |
||||||
|
parser = argparse.ArgumentParser() |
||||||
|
parser.add_argument("--ids-only", action="store_true") |
||||||
|
args = parser.parse_args() |
||||||
|
connection = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
connection.execute("BEGIN") |
||||||
|
rows = connection.execute( |
||||||
|
""" |
||||||
|
SELECT |
||||||
|
l.listing_id, l.url, l.price_brl, l.condominium_brl, l.iptu_brl, |
||||||
|
l.area_m2, l.bedrooms, l.suites, l.parking_spaces, |
||||||
|
l.raw_html_path, COUNT(p.source_url) AS photo_count |
||||||
|
FROM listings AS l |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
LEFT JOIN photos AS p USING (listing_id) |
||||||
|
WHERE instr(s.search_url, ?) > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
GROUP BY l.listing_id |
||||||
|
ORDER BY l.price_brl, l.listing_id |
||||||
|
LIMIT 100 |
||||||
|
""", |
||||||
|
(SEARCH_FRAGMENT,), |
||||||
|
).fetchall() |
||||||
|
connection.close() |
||||||
|
|
||||||
|
if args.ids_only: |
||||||
|
for row in rows: |
||||||
|
print(row[0]) |
||||||
|
return 0 |
||||||
|
|
||||||
|
print( |
||||||
|
"listing_id\tprice_brl\tcondominium_brl\tiptu_brl\tarea_m2\tbedrooms\t" |
||||||
|
"suites\tparking_spaces\tphoto_count\taddress\tdetails\tdescription\turl" |
||||||
|
) |
||||||
|
for row in rows: |
||||||
|
( |
||||||
|
listing_id, |
||||||
|
url, |
||||||
|
price, |
||||||
|
condominium, |
||||||
|
iptu, |
||||||
|
area, |
||||||
|
bedrooms, |
||||||
|
suites, |
||||||
|
parking, |
||||||
|
raw_path, |
||||||
|
photo_count, |
||||||
|
) = row |
||||||
|
description, details, address = parse_html(raw_path) |
||||||
|
values = ( |
||||||
|
listing_id, |
||||||
|
price, |
||||||
|
condominium, |
||||||
|
iptu, |
||||||
|
area, |
||||||
|
bedrooms, |
||||||
|
suites, |
||||||
|
parking, |
||||||
|
photo_count, |
||||||
|
address, |
||||||
|
details, |
||||||
|
description, |
||||||
|
url, |
||||||
|
) |
||||||
|
print("\t".join("" if value is None else clean(str(value)) for value in values)) |
||||||
|
return 0 |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
{ |
||||||
|
"analysis_date": "2026-07-17", |
||||||
|
"listing_data_reference_date": "2026-07-17", |
||||||
|
"database_path": "dfimoveis_data/dfimoveis.sqlite3", |
||||||
|
"database_main_file_sha256": "7a5102a62bc66a344bd9440fa36301d10a57711bcdd263b767b65e88f70957f4", |
||||||
|
"logical_snapshot_sha256": "fe604aab8af7f67c833e6de2a0fc81c7dc2950ca440ad7b23ebd03d0c2546188", |
||||||
|
"database_mode": "read_only_transaction_with_wal", |
||||||
|
"search_url_fragment": "/cruzeiro/novo/", |
||||||
|
"listing_limit": 100, |
||||||
|
"observed_listing_count": 65, |
||||||
|
"database_crawl_run_count": 16, |
||||||
|
"active_listing_count": 65, |
||||||
|
"new_listing_count_since_previous_report": 0, |
||||||
|
"material_price_change_count": 1, |
||||||
|
"ranking_unit": "probable_physical_property", |
||||||
|
"numeric_score_used": false, |
||||||
|
"photo_first_pass_limit_per_listing": 16, |
||||||
|
"photo_finalist_count": 17, |
||||||
|
"photo_files_reviewed_distinct": 1289, |
||||||
|
"first_photo_sale_marker_audit_count": 10, |
||||||
|
"visually_unavailable_listing_ids": [] |
||||||
|
} |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
"""Suggest duplicate Cruzeiro ads from visually similar local photos. |
||||||
|
|
||||||
|
Uses a simple difference hash as a screening signal. Results are candidates for |
||||||
|
manual review, never automatic proof that two ads describe the same property. |
||||||
|
""" |
||||||
|
|
||||||
|
from __future__ import annotations |
||||||
|
|
||||||
|
import sqlite3 |
||||||
|
from collections import defaultdict |
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
from PIL import Image, UnidentifiedImageError |
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2] |
||||||
|
DB_PATH = ROOT / "dfimoveis_data" / "dfimoveis.sqlite3" |
||||||
|
DATA_ROOT = DB_PATH.parent |
||||||
|
|
||||||
|
|
||||||
|
def difference_hash(path: Path) -> int | None: |
||||||
|
try: |
||||||
|
with Image.open(path) as image: |
||||||
|
pixels = list(image.convert("L").resize((9, 8)).getdata()) |
||||||
|
except (OSError, UnidentifiedImageError): |
||||||
|
return None |
||||||
|
value = 0 |
||||||
|
for row in range(8): |
||||||
|
offset = row * 9 |
||||||
|
for column in range(8): |
||||||
|
value = (value << 1) | (pixels[offset + column] > pixels[offset + column + 1]) |
||||||
|
return value |
||||||
|
|
||||||
|
|
||||||
|
def main() -> None: |
||||||
|
connection = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
connection.execute("BEGIN") |
||||||
|
rows = connection.execute( |
||||||
|
""" |
||||||
|
WITH cruzeiro AS ( |
||||||
|
SELECT s.listing_id |
||||||
|
FROM listing_searches AS s |
||||||
|
JOIN listings AS l USING (listing_id) |
||||||
|
WHERE instr(search_url, '/cruzeiro/novo/') > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
), uncommon AS ( |
||||||
|
SELECT sha256 |
||||||
|
FROM photos |
||||||
|
WHERE sha256 IS NOT NULL |
||||||
|
GROUP BY sha256 |
||||||
|
HAVING COUNT(DISTINCT listing_id) <= 5 |
||||||
|
) |
||||||
|
SELECT p.listing_id, p.local_path |
||||||
|
FROM photos AS p |
||||||
|
JOIN cruzeiro AS c USING (listing_id) |
||||||
|
JOIN uncommon AS u USING (sha256) |
||||||
|
WHERE p.local_path IS NOT NULL |
||||||
|
ORDER BY p.listing_id, p.ordinal |
||||||
|
""" |
||||||
|
).fetchall() |
||||||
|
connection.close() |
||||||
|
|
||||||
|
hashes: dict[str, list[int]] = defaultdict(list) |
||||||
|
for listing_id, relative_path in rows: |
||||||
|
value = difference_hash(DATA_ROOT / relative_path) |
||||||
|
if value is not None: |
||||||
|
hashes[listing_id].append(value) |
||||||
|
|
||||||
|
ids = sorted(hashes) |
||||||
|
print("listing_a\tlisting_b\tvisually_similar_photos") |
||||||
|
for index, first_id in enumerate(ids): |
||||||
|
for second_id in ids[index + 1 :]: |
||||||
|
available = list(hashes[second_id]) |
||||||
|
matches = 0 |
||||||
|
for first_hash in hashes[first_id]: |
||||||
|
distances = [(first_hash ^ second_hash).bit_count() for second_hash in available] |
||||||
|
if distances and min(distances) <= 6: |
||||||
|
available.pop(distances.index(min(distances))) |
||||||
|
matches += 1 |
||||||
|
if matches >= 3: |
||||||
|
print(f"{first_id}\t{second_id}\t{matches}") |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
main() |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
PRAGMA query_only = ON; |
||||||
|
|
||||||
|
-- Universo do Cruzeiro Novo. |
||||||
|
SELECT |
||||||
|
l.listing_id, |
||||||
|
l.url, |
||||||
|
l.price_brl, |
||||||
|
l.condominium_brl, |
||||||
|
l.iptu_brl, |
||||||
|
l.area_m2, |
||||||
|
l.bedrooms, |
||||||
|
l.suites, |
||||||
|
l.parking_spaces, |
||||||
|
l.first_seen_at, |
||||||
|
l.last_seen_at, |
||||||
|
l.raw_html_path |
||||||
|
FROM listings AS l |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/cruzeiro/novo/') > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
ORDER BY l.price_brl, l.listing_id |
||||||
|
LIMIT 100; |
||||||
|
|
||||||
|
-- Cardinalidade de fotos por anúncio. |
||||||
|
SELECT p.listing_id, COUNT(*) AS photo_count |
||||||
|
FROM photos AS p |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/cruzeiro/novo/') > 0 |
||||||
|
AND EXISTS ( |
||||||
|
SELECT 1 FROM listings AS l |
||||||
|
WHERE l.listing_id = p.listing_id AND l.inactive_at IS NULL |
||||||
|
) |
||||||
|
GROUP BY p.listing_id |
||||||
|
ORDER BY p.listing_id |
||||||
|
LIMIT 100; |
||||||
|
|
||||||
|
-- Número de estados históricos por anúncio. |
||||||
|
SELECT h.listing_id, COUNT(*) AS snapshot_count |
||||||
|
FROM listing_history AS h |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/cruzeiro/novo/') > 0 |
||||||
|
AND EXISTS ( |
||||||
|
SELECT 1 FROM listings AS l |
||||||
|
WHERE l.listing_id = h.listing_id AND l.inactive_at IS NULL |
||||||
|
) |
||||||
|
GROUP BY h.listing_id |
||||||
|
ORDER BY h.listing_id |
||||||
|
LIMIT 100; |
||||||
@ -0,0 +1,133 @@ |
|||||||
|
# Ranking reconstruído — Cruzeiro Novo x Jardins Mangueiral |
||||||
|
|
||||||
|
**Data do snapshot:** 17/07/2026 |
||||||
|
**Unidade:** imóvel físico provável |
||||||
|
**Finalidade:** priorizar investigação e visitas; não estimar preço de transação nem substituir vistoria |
||||||
|
|
||||||
|
## Resumo executivo |
||||||
|
|
||||||
|
O Jardins Mangueiral continua oferecendo os melhores candidatos por espaço, suíte, vagas e preço anunciado. O Cruzeiro Novo continua superior em centralidade, acesso ao Plano Piloto e menor dependência de carro. |
||||||
|
|
||||||
|
A casa `1279892`, QC 10, permanece em primeiro lugar geral pelo equilíbrio entre R$ 595 mil, condição visual, três quartos prováveis e quintal convertido sem eliminação total da área aberta. O apartamento `1345146`, SHCES 1207, é o melhor do Cruzeiro e fica em segundo: reforma coerente, suíte e elevador anunciado, mas com preço maior e vaga ainda incerta. |
||||||
|
|
||||||
|
Confiança do ranking: **moderada para priorização de visitas e baixa para decisão de compra**. Fotos e anúncios não confirmam documentação, estrutura, sistemas, ruído, conforto térmico ou preço negociável. |
||||||
|
|
||||||
|
## Universo e atualização |
||||||
|
|
||||||
|
- Fonte: DF Imóveis. |
||||||
|
- Banco: 159 anúncios, 295 snapshots e 6.272 mídias. |
||||||
|
- Cruzeiro Novo: 65 anúncios acumulados e ativos. |
||||||
|
- Jardins Mangueiral: 94 anúncios acumulados; 84 ativos no banco e 10 inativos. |
||||||
|
- Cobertura útil: coletas de 13 e 17/07/2026. |
||||||
|
- Histórico: ainda curto demais para tendência, liquidez ou tempo real de mercado. |
||||||
|
|
||||||
|
O banco foi aberto com `mode=ro`, `PRAGMA query_only = ON` e transação de leitura consistente para incluir o WAL. O `quick_check` retornou `ok`. |
||||||
|
|
||||||
|
## Método de reconstrução |
||||||
|
|
||||||
|
O comparativo foi refeito a partir dos rankings regionais corrigidos, preservando a ordem relativa dentro de cada região. Não foram usados preço por metro quadrado nem pontuação numérica, porque as áreas das casas e apartamentos não são semanticamente comparáveis e os pesos não foram aprovados. |
||||||
|
|
||||||
|
A ordem geral combina: |
||||||
|
|
||||||
|
1. funcionalidade de longo prazo e três quartos utilizáveis; |
||||||
|
2. condição visual e risco de reforma; |
||||||
|
3. iluminação, ventilação, circulação e armazenamento aparentes; |
||||||
|
4. preço, custos conhecidos e margem para adequação; |
||||||
|
5. acessibilidade, vagas e dependência de carro; |
||||||
|
6. riscos específicos da tipologia; |
||||||
|
7. qualidade e coerência da evidência fotográfica; |
||||||
|
8. compatibilidade da localização com a rotina registrada no projeto. |
||||||
|
|
||||||
|
No Cruzeiro, os principais ajustes de risco são escadas, ausência de garagem, sistemas antigos e obras do bloco. No Mangueiral, são planta original, regularização das ampliações, impermeabilização, calor/ventilação e logística. |
||||||
|
|
||||||
|
## Auditoria de disponibilidade pelas fotos |
||||||
|
|
||||||
|
A primeira foto dos 20 finalistas foi inspecionada especificamente para “vendido”, “negócio fechado” ou sinal equivalente. |
||||||
|
|
||||||
|
- Nenhum dos dez finalistas do Cruzeiro apresentou esse sinal. |
||||||
|
- No Mangueiral, quatro anúncios que apareceram em versões anteriores foram removidos e colocados em quarentena visual: `1346231`, `1349519`, `1351729` e `1366801`. |
||||||
|
- O banco ainda os tratava como ativos; para um ranking de visitas, a evidência visual prevaleceu como sinal de indisponibilidade operacional. |
||||||
|
- A marca não comprova registro, preço ou conclusão jurídica da transação. |
||||||
|
|
||||||
|
## Ranking geral |
||||||
|
|
||||||
|
| # | Região | Anúncio | Local | Preço | Motivo principal | Pendência decisiva | |
||||||
|
|---:|---|---|---|---:|---|---| |
||||||
|
| 1 | Mangueiral | [1279892](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-rua-f-1279892) | QC 10 | R$ 595.000 | Melhor equilíbrio entre preço, estado e espaço externo | Planta, áreas divergentes e regularização | |
||||||
|
| 2 | Cruzeiro | [1345146](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1207-bloco-a-1345146) | SHCES 1207 A | R$ 670.000 | Melhor alternativa urbana; reformado e com elevador anunciado | Vaga, área da matrícula e sistemas | |
||||||
|
| 3 | Mangueiral | [1350367](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-rua-c-1350367) | QC 6 | R$ 569.000 | Preço competitivo e ampliação social aparentemente funcional | Área, licenças, calor e drenagem | |
||||||
|
| 4 | Cruzeiro | [1232283](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-bloco-b-1232283) | SHCES 309 B | R$ 600.000 | 70 m², suíte, nascente/vazado e duas vagas anunciadas | Elevador e natureza jurídica das vagas | |
||||||
|
| 5 | Mangueiral | [1346017](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017) | QC 10 | R$ 577.000 | Clara, pouco personalizada e com quintal majoritariamente aberto | Taxas, orientação e regularização da cobertura | |
||||||
|
| 6 | Mangueiral | [1397378](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-8-rua-b-1397378) | QC 8 | R$ 600.000 | QC prática e implantação compatível com a planta de 68 m² | Manutenção, drenagem e galeria contaminada | |
||||||
|
| 7 | Cruzeiro | [1357415](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-1357415) | SHCES 1109 | R$ 630.000 | Boa reforma, suíte e posição nascente/vazada | Confirmar elevador antes de agendar | |
||||||
|
| 8 | Mangueiral | [1359960](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-7-1359960) | QC 7 | R$ 610.000 | Ampliação social flexível sem segunda cozinha completa evidente | Calor, ventilação e impermeabilização | |
||||||
|
| 9 | Cruzeiro | [1357633](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1357633) | SHCES 309 | R$ 600.000 | Reforma uniforme e sistemas declarados renovados | Elevador, garagem, condomínio e prova da reforma | |
||||||
|
| 10 | Cruzeiro | [1376958](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-b-1376958) | SHCES 911 B | R$ 599.000 | Reformado, nascente, vazado e com suíte | Sem elevador e garagem não informada | |
||||||
|
| 11 | Mangueiral | [1319996](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1319996) | QC 14 | R$ 649.000 | Reforma completa, marcenaria e placas solares | QC, cobertura, taxas e homologação solar | |
||||||
|
| 12 | Cruzeiro | [1345903](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1303-1345903) | SHCES 1303 | R$ 565.000 | Menor preço competitivo e primeiro andar | Sem garagem; ruído e segurança do térreo | |
||||||
|
| 13 | Mangueiral | [1324014](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014) | QC 12 | R$ 649.000 | Acabamento interno e marcenaria fortes | Vaga, redundância gourmet e ventilação | |
||||||
|
| 14 | Cruzeiro | [1339324](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-703-1339324) | SHCES 703 | R$ 665.000 | Integração social, suíte e boa apresentação visual | Terceiro andar, elevador e garagem desconhecidos | |
||||||
|
| 15 | Mangueiral | [1333569](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-1333569) | QC 6 | R$ 650.000 | Ampliação social em uso e QC alternativa prática | Conceito dos 90 m² e manutenção da cobertura | |
||||||
|
| 16 | Cruzeiro | [1370889](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370889) / [1375200](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1375200) | SHCES 207 | R$ 695.000 | Apartamento reformado com suíte | Duplicata, andar, elevador, vaga e área | |
||||||
|
| 17 | Mangueiral | [1359480](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1359480) | QC 4 | R$ 535.000 | Referência de menor preço habitável | QC, planta, documentação e acabamento externo | |
||||||
|
| 18 | Cruzeiro | [1372268](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-bloco-i-1372268) | SHCES 305 I | R$ 595.000 | Primeiro andar e duas vagas anunciadas | Provar vagas e orçar modernização | |
||||||
|
| 19 | Mangueiral | [1356197](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197) | QC 4 | R$ 649.000 | Acabamento interno forte e casa vazia | Espaço coberto parece garagem; regularização | |
||||||
|
| 20 | Cruzeiro | [1373131](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-1373131) | SHCES 305 | R$ 725.000 | Reforma tecnicamente bem descrita | Sem elevador, sem garagem e prêmio de preço | |
||||||
|
|
||||||
|
## Comparação regional |
||||||
|
|
||||||
|
| Dimensão | Cruzeiro Novo | Jardins Mangueiral | |
||||||
|
|---|---|---| |
||||||
|
| Faixa do top 10 | R$ 565 mil–R$ 725 mil | R$ 535 mil–R$ 650 mil | |
||||||
|
| Mediana do top 10 | R$ 615 mil | R$ 610 mil | |
||||||
|
| Espaço | 62–70 m²; conceito de área mais homogêneo | 68–90 m² informados; ampliações pouco comparáveis | |
||||||
|
| Vagas | Frequentemente ausentes ou incertas | Uma ou duas na maioria dos finalistas | |
||||||
|
| Acessibilidade | Depende fortemente de andar e elevador | Escada interna permanente em todos os sobrados padrão | |
||||||
|
| Reforma | Elétrica, hidráulica e prédio antigos | Cobertura, drenagem, impermeabilização e ampliação | |
||||||
|
| Mobilidade | Centralidade e menor dependência de carro | Maior dependência logística; proximidade de São Sebastião | |
||||||
|
| Espaço social | Compacto | Quintal e ampliações mais flexíveis | |
||||||
|
| Documentação crítica | Vaga, área, obras do bloco | Planta original e regularização das ampliações | |
||||||
|
|
||||||
|
## Sensibilidade à prioridade pessoal |
||||||
|
|
||||||
|
| Prioridade dominante | Região favorecida | Primeiro candidato | |
||||||
|
|---|---|---| |
||||||
|
| Centralidade e acesso ao Plano | Cruzeiro | 1345146 | |
||||||
|
| Espaço, suíte, vagas e custo-benefício | Mangueiral | 1279892 | |
||||||
|
| Menor preço habitável | Mangueiral | 1359480, com concessões | |
||||||
|
| Apartamento próximo de R$ 600 mil | Cruzeiro | 1232283 | |
||||||
|
| Menor risco de ampliação irregular | Cruzeiro | 1345146 ou 1357633 | |
||||||
|
| Menor risco de sistemas prediais antigos | Mangueiral | 1346017 ou 1397378, sujeito a vistoria | |
||||||
|
| Proximidade de São Sebastião e rede familiar | Mangueiral | 1279892 | |
||||||
|
|
||||||
|
## Imóveis imediatamente fora do corte |
||||||
|
|
||||||
|
- **Cruzeiro:** [1382466](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-101-bloco-c-1382466), [1290510](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-a-1290510) e [1371813](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-bloco-d-1371813). |
||||||
|
- **Mangueiral:** [1368562](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-5-1368562). |
||||||
|
|
||||||
|
## Próxima ação recomendada |
||||||
|
|
||||||
|
As quatro visitas mais informativas continuam sendo: |
||||||
|
|
||||||
|
1. `1279892`, para testar a melhor hipótese de casa equilibrada; |
||||||
|
2. `1345146`, para testar a melhor hipótese urbana com elevador; |
||||||
|
3. `1350367`, para testar custo-benefício e ampliação no Mangueiral; |
||||||
|
4. `1232283`, para testar apartamento maior, suíte e vagas anunciadas. |
||||||
|
|
||||||
|
Registrar após cada visita: tempo real de deslocamento, sensação de espaço, ruído, estacionamento, escadas, ventilação, manutenção esperada e vontade de permanecer por 10–20 anos. Essas quatro visitas devem definir qual região recebe a rodada seguinte. |
||||||
|
|
||||||
|
## Limitações |
||||||
|
|
||||||
|
- Preços são pedidos, não transações. |
||||||
|
- A cobertura histórica é curta e de um único portal. |
||||||
|
- A situação financeira do projeto precisa ser confirmada antes de proposta ou preço máximo. |
||||||
|
- Fotos não validam estrutura, sistemas, ruído, conforto térmico ou documentação. |
||||||
|
- A planta original das casas e vagas/elevadores dos apartamentos continuam pendentes. |
||||||
|
- A escolha regional ainda depende de uma preferência não consolidada: centralidade versus casa, espaço e proximidade familiar. |
||||||
|
|
||||||
|
## Reprodutibilidade |
||||||
|
|
||||||
|
- `results.csv`: ranking reconstruído. |
||||||
|
- `parameters.json`: universo, método e fingerprint lógico do snapshot. |
||||||
|
- `reports/cruzeiro_top10/`: fontes do ranking regional do Cruzeiro. |
||||||
|
- `reports/mangueiral_top10/`: fontes do ranking regional do Mangueiral. |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
{ |
||||||
|
"analysis_date": "2026-07-17", |
||||||
|
"listing_data_reference_date": "2026-07-17", |
||||||
|
"database_path": "dfimoveis_data/dfimoveis.sqlite3", |
||||||
|
"database_main_file_sha256": "046e0a2fef3eb3c2e5ebdf07d30bd04a31db0bb1ea315ccc536c6bf8ba96577e", |
||||||
|
"logical_snapshot_sha256": "fe604aab8af7f67c833e6de2a0fc81c7dc2950ca440ad7b23ebd03d0c2546188", |
||||||
|
"database_mode": "read_only_transaction_with_wal", |
||||||
|
"source_reports": [ |
||||||
|
"reports/cruzeiro_top10/README.md", |
||||||
|
"reports/mangueiral_top10/README.md" |
||||||
|
], |
||||||
|
"cruzeiro_listing_count": 65, |
||||||
|
"mangueiral_listing_count": 94, |
||||||
|
"mangueiral_active_listing_count": 84, |
||||||
|
"mangueiral_inactive_listing_count": 10, |
||||||
|
"total_listing_count": 159, |
||||||
|
"database_snapshot_count": 295, |
||||||
|
"database_photo_count": 6272, |
||||||
|
"database_crawl_run_count": 16, |
||||||
|
"cruzeiro_photo_files_reviewed_distinct": 1289, |
||||||
|
"mangueiral_photo_files_reviewed_distinct_cumulative": 1907, |
||||||
|
"total_photo_files_reviewed_distinct_cumulative": 3196, |
||||||
|
"regional_finalists_compared": 20, |
||||||
|
"ranking_merge_method": "preserve_regional_order_then_interleave_by_cross_typology_visit_priority", |
||||||
|
"report_rebuilt_from_regional_results": true, |
||||||
|
"ranking_unit": "probable_physical_property", |
||||||
|
"numeric_score_used": false, |
||||||
|
"cross_typology_price_per_m2_used": false, |
||||||
|
"first_photo_sale_marker_audit_count": 20, |
||||||
|
"visually_unavailable_listing_ids": ["1346231", "1349519", "1351729", "1366801"] |
||||||
|
} |
||||||
@ -0,0 +1,303 @@ |
|||||||
|
# Avaliação inicial do banco de dados |
||||||
|
|
||||||
|
**Data da avaliação:** 13 de julho de 2026 |
||||||
|
**Banco:** `dfimoveis_data/dfimoveis.sqlite3` |
||||||
|
**Escopo:** diagnóstico estrutural e de qualidade anterior a qualquer análise dos imóveis |
||||||
|
|
||||||
|
## 1. Pergunta respondida |
||||||
|
|
||||||
|
O banco já representa de forma segura anúncios, snapshots históricos e imóveis físicos, com cobertura e qualidade suficientes para iniciar análises imobiliárias? |
||||||
|
|
||||||
|
**Resposta curta:** ele representa anúncios e estados de conteúdo, mas ainda não representa imóveis físicos deduplicados. A coleta cobre apenas um dia e contém exatamente um snapshot por anúncio. Preços, áreas e quartos podem ser usados somente após validações específicas; localização e dados de anunciante estão fortemente contaminados e não devem ser usados como estão. Portanto, o banco ainda não sustenta tendência histórica, tempo de mercado, preço por m² ou comparáveis por localização. |
||||||
|
|
||||||
|
## 2. Segurança e método |
||||||
|
|
||||||
|
A análise foi feita exclusivamente com consultas agregadas ou amostras pequenas. O banco foi aberto com `sqlite3 -readonly` ou URI `mode=ro&immutable=1`, sempre com `PRAGMA query_only = ON`. Nenhum scraper foi executado e nenhuma instrução SQL de escrita foi enviada ao banco. |
||||||
|
|
||||||
|
Identificação do arquivo analisado: |
||||||
|
|
||||||
|
- tamanho: 5.017.600 bytes; |
||||||
|
- modificação: `2026-07-13 14:20:34.601285975 -03:00`; |
||||||
|
- SHA-256 inicial: `1ce3128b488c50c993d7328ac6e24b5905f083a7e04e3f636407eea1edd02b9d`; |
||||||
|
- sem arquivos `-wal` ou `-shm` e sem processo com o arquivo aberto na inspeção inicial; |
||||||
|
- `PRAGMA quick_check`: `ok`; |
||||||
|
- `PRAGMA foreign_key_check`: nenhuma violação. |
||||||
|
|
||||||
|
Na validação final, o arquivo principal continuava com o mesmo tamanho, `mtime` e SHA-256. Como seu modo persistido é WAL, o cliente SQLite havia criado dois sidecars de runtime: `dfimoveis.sqlite3-shm`, com 32.768 bytes, e `dfimoveis.sqlite3-wal`, vazio. Eles não foram removidos, conforme a regra explícita do projeto. |
||||||
|
|
||||||
|
O esquema completo e as decisões de interpretação estão em `docs/ACTUAL_SCHEMA.md`. |
||||||
|
|
||||||
|
## 3. Universo, período e cobertura |
||||||
|
|
||||||
|
Não houve filtro ou exclusão de registros para este diagnóstico. O universo físico contém: |
||||||
|
|
||||||
|
| Unidade | Quantidade | |
||||||
|
|---|---:| |
||||||
|
| Execuções do coletor | 4 | |
||||||
|
| Anúncios únicos por `listing_id` | 156 | |
||||||
|
| URLs de anúncio únicas | 156 | |
||||||
|
| Snapshots de conteúdo | 156 | |
||||||
|
| Vínculos anúncio–busca | 156 | |
||||||
|
| Fotos registradas | 5.386 | |
||||||
|
| Anúncios com pelo menos uma foto | 156 | |
||||||
|
|
||||||
|
### Cobertura temporal |
||||||
|
|
||||||
|
Todas as datas técnicas válidas estão em ISO 8601. A coleta cobre somente **13/07/2026**: |
||||||
|
|
||||||
|
| Evento | UTC | Horário de Brasília (UTC−3) | |
||||||
|
|---|---|---| |
||||||
|
| Primeira execução iniciada | 12:20:58 | 09:20:58 | |
||||||
|
| Primeira observação de anúncio | 12:56:32 | 09:56:32 | |
||||||
|
| Última observação de anúncio | 13:25:51 | 10:25:51 | |
||||||
|
| Última execução concluída | 17:20:34 | 14:20:34 | |
||||||
|
|
||||||
|
As duas primeiras execuções terminaram com `status = ok`, mas registraram zero páginas, URLs e detalhes. As duas execuções com coleta efetiva somaram: |
||||||
|
|
||||||
|
- 9 páginas de busca; |
||||||
|
- 156 URLs encontradas; |
||||||
|
- 156 detalhes processados com sucesso e zero falhas; |
||||||
|
- 5.383 fotos baixadas e 3 falhas; |
||||||
|
- zero anúncios marcados como inativos. |
||||||
|
|
||||||
|
As buscas cobertas foram: |
||||||
|
|
||||||
|
1. `https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos` — 65 anúncios; |
||||||
|
2. `https://www.dfimoveis.com.br/venda/df/brasilia/jardins-mangueiral/casa/3,4-quartos` — 91 anúncios. |
||||||
|
|
||||||
|
O universo é, portanto, intencionalmente limitado a um portal e a duas consultas. A busca do Jardins Mangueiral aceita três **ou quatro** quartos: 89 casas foram extraídas com três quartos anunciados e 2 com quatro. Mesmo os 89 registros de três quartos não comprovam a planta original exigida pelo projeto. |
||||||
|
|
||||||
|
## 4. Como as entidades são representadas |
||||||
|
|
||||||
|
### Anúncio |
||||||
|
|
||||||
|
`listings` guarda uma linha por `listing_id` do DF Imóveis, com URL única e o estado corrente. Os 156 IDs, URLs e hashes correntes são distintos. Essa é uma identidade de publicação no portal, não de unidade residencial. |
||||||
|
|
||||||
|
### Snapshot ou observação histórica |
||||||
|
|
||||||
|
`listing_history` guarda uma linha por par único `(listing_id, content_hash)`. Isso é um histórico de **mudanças de conteúdo**: uma nova observação idêntica não cria novo snapshot. `listings.first_seen_at` e `last_seen_at` condensam a janela observada. |
||||||
|
|
||||||
|
No snapshot atual: |
||||||
|
|
||||||
|
- todos os 156 anúncios têm exatamente um registro histórico; |
||||||
|
- o hash histórico é igual ao hash corrente em todos os casos; |
||||||
|
- `captured_at = first_seen_at` em todos os casos; |
||||||
|
- `first_seen_at = last_seen_at` em todos os casos. |
||||||
|
|
||||||
|
Assim, não há série histórica real ainda. Não é possível medir mudança de preço, republicação, duração, saída ou retorno do estoque. |
||||||
|
|
||||||
|
### Execução do scraper |
||||||
|
|
||||||
|
`crawl_runs` registra horários, estado e contadores JSON, mas não existe `run_id` em `listings`, `listing_history` ou `listing_searches`. A execução geradora só pode ser inferida por horário. Isso limita auditoria, cobertura por execução e diagnóstico de ausências. |
||||||
|
|
||||||
|
### Imóvel físico |
||||||
|
|
||||||
|
Não existe tabela ou chave para imóvel físico, unidade, endereço normalizado ou relação de duplicidade. Anúncios de corretores diferentes permanecem independentes. A base também não contém hashes perceptuais; possui apenas SHA-256 exato das imagens. |
||||||
|
|
||||||
|
Logo, as unidades válidas hoje são: |
||||||
|
|
||||||
|
- anúncio único: suportado por `listing_id`; |
||||||
|
- estado alterado do anúncio: suportado por `listing_history`; |
||||||
|
- imóvel físico provável/confirmado: **não suportado sem derivação e revisão**. |
||||||
|
|
||||||
|
## 5. Qualidade dos dados |
||||||
|
|
||||||
|
### 5.1 Integridade estrutural |
||||||
|
|
||||||
|
Pontos positivos: |
||||||
|
|
||||||
|
- arquivo íntegro segundo `quick_check`; |
||||||
|
- nenhuma FK órfã em histórico, buscas ou fotos; |
||||||
|
- todos os JSONs são sintaticamente válidos; |
||||||
|
- todos os IDs de anúncio têm 6–7 caracteres numéricos; |
||||||
|
- todas as URLs pertencem ao domínio e padrão esperados; |
||||||
|
- todos os 156 HTMLs brutos referenciados existem; |
||||||
|
- todos os hashes de conteúdo têm 64 caracteres; |
||||||
|
- não há URL, hash corrente ou caminho de HTML repetido. |
||||||
|
|
||||||
|
Limitações estruturais: |
||||||
|
|
||||||
|
- nenhuma relação entre execução e item; |
||||||
|
- fonte implícita, sem `source_id`; |
||||||
|
- área armazenada em um único conceito; |
||||||
|
- mídia ligada ao anúncio, não ao snapshot; |
||||||
|
- ausência de entidade de imóvel físico e de localização normalizada; |
||||||
|
- apenas autoíndices; não há índices por datas ou atributos analíticos. |
||||||
|
|
||||||
|
### 5.2 Completude dos principais campos |
||||||
|
|
||||||
|
| Campo | Ausentes | Percentual | |
||||||
|
|---|---:|---:| |
||||||
|
| preço | 0 | 0,0% | |
||||||
|
| área | 0 | 0,0% | |
||||||
|
| quartos | 0 | 0,0% | |
||||||
|
| condomínio | 59 | 37,8% | |
||||||
|
| IPTU | 144 | 92,3% | |
||||||
|
| suítes | 41 | 26,3% | |
||||||
|
| vagas | 57 | 36,5% | |
||||||
|
| cidade | 156 | 100,0% | |
||||||
|
| UF | 156 | 100,0% | |
||||||
|
| nome do anunciante | 156 | 100,0% | |
||||||
|
| latitude/longitude | 156 / 156 | 100,0% / 100,0% | |
||||||
|
| data de publicação | 156 | 100,0% | |
||||||
|
| data de inativação | 156 | 100,0% | |
||||||
|
|
||||||
|
Esses percentuais medem apenas nulos. Para campos textuais, a taxa de preenchimento é enganosa porque o parser capturou conteúdo da página em vez do valor pretendido. |
||||||
|
|
||||||
|
### 5.3 Contaminação de texto |
||||||
|
|
||||||
|
Os problemas mais graves são: |
||||||
|
|
||||||
|
- 146 endereços (93,6%) têm mais de 300 caracteres; |
||||||
|
- 154 bairros (98,7%) têm mais de 100 caracteres; |
||||||
|
- os dois valores curtos de bairro são `.` e um trecho de descrição, também inválidos; |
||||||
|
- 98 códigos de anunciante (62,8%) têm mais de 100 caracteres; |
||||||
|
- 153 valores de CRECI (98,1%) têm mais de 100 caracteres. |
||||||
|
|
||||||
|
Os textos longos incluem navegação, formulários, política de privacidade e outros fragmentos da página. Eles podem conter dados de contato exibidos pelo portal; por isso, não foram reproduzidos neste relatório. Esses campos devem ser considerados **indisponíveis**, não apenas ruidosos. |
||||||
|
|
||||||
|
Os títulos têm somente quatro valores distintos e funcionam como títulos genéricos por tipologia. As 156 descrições são distintas, mas têm apenas 156–161 caracteres, compatíveis com resumos/metadados truncados, não com a descrição completa. `extra_json` contém um array `json_ld` vazio nos 156 registros e não resolve a reextração. |
||||||
|
|
||||||
|
### 5.4 Valores estruturados e outliers |
||||||
|
|
||||||
|
| Tipo anunciado | N | Preço mínimo | Preço máximo | Área mínima | Área máxima | Quartos | |
||||||
|
|---|---:|---:|---:|---:|---:|---:| |
||||||
|
| Apartamento | 65 | R$ 480.000 | R$ 2.030.598 | 61 m² | 113 m² | 3 | |
||||||
|
| Casa | 91 | R$ 450.000 | R$ 570.000.000 | 0,11 m² | 131,46 m² | 3–4 | |
||||||
|
|
||||||
|
Não há valores nulos ou não positivos em preço e área, mas isso não significa validade: |
||||||
|
|
||||||
|
- anúncio `1351663`: R$ 570.000.000, forte indício de erro de escala ou parsing; |
||||||
|
- anúncio `1370918`: 0,11 m², área fisicamente implausível. |
||||||
|
|
||||||
|
Não foi aplicado corte nem correção silenciosa. Também não se calculou preço por m², pois `area_m2` não distingue área útil, privativa, construída, total ou terreno. |
||||||
|
|
||||||
|
### 5.5 Fotos e arquivos brutos |
||||||
|
|
||||||
|
Das 5.386 linhas em `photos`: |
||||||
|
|
||||||
|
- 5.383 têm arquivo local, e o tamanho em disco coincide com `bytes`; |
||||||
|
- 3 não têm hash, MIME, tamanho ou caminho, correspondendo às três falhas registradas pelo coletor; |
||||||
|
- há 4.514 WebP, 558 JPEG, 311 PNG e 3 sem MIME; |
||||||
|
- os anúncios possuem de 5 a 61 fotos; |
||||||
|
- não há caminho local nem ordinal duplicado dentro de anúncio. |
||||||
|
|
||||||
|
Há 3.307 hashes distintos. Cento e quatorze hashes aparecem em mais de um anúncio, e um deles aparece nos 156 anúncios. Isso revela logos, selos, placeholders ou outros ativos comuns. Hash idêntico pode ser sinal de anúncio duplicado somente depois de excluir ativos genéricos e combinar outros sinais. |
||||||
|
|
||||||
|
## 6. Duplicidades e imóveis físicos prováveis |
||||||
|
|
||||||
|
Não há duplicidade técnica imediata por `listing_id`, URL ou `content_hash`. Uma assinatura exploratória composta por bairro, tipo, preço, área, quartos e vagas encontrou 13 grupos, equivalentes a 18 linhas excedentes. Esse resultado **não é evidência de 18 imóveis duplicados**, porque: |
||||||
|
|
||||||
|
- o bairro está contaminado; |
||||||
|
- preço, área e quartos podem coincidir entre unidades distintas; |
||||||
|
- fotos compartilhadas incluem ativos genéricos; |
||||||
|
- não há endereço/unidade confiável nem telefone normalizado; |
||||||
|
- SHA-256 só detecta arquivos idênticos, não versões redimensionadas da mesma foto. |
||||||
|
|
||||||
|
Consequentemente, o método de deduplicação desta avaliação foi apenas técnico: unicidade de anúncio por `listing_id`. Nenhuma fusão de anúncios nem contagem de imóveis físicos foi produzida. |
||||||
|
|
||||||
|
Uma futura camada derivada deve combinar, com confiança e revisão manual: |
||||||
|
|
||||||
|
- endereço/QC/quadra/bloco/unidade reextraídos; |
||||||
|
- imagens após remoção de ativos comuns, idealmente com hash perceptual; |
||||||
|
- preço, área, planta, quartos e vagas validados; |
||||||
|
- anunciante e descrição normalizados; |
||||||
|
- estados `unreviewed`, `probable`, `confirmed`, `rejected` e `ambiguous`. |
||||||
|
|
||||||
|
## 7. Possíveis quebras de coleta |
||||||
|
|
||||||
|
1. **Localização:** `address` e `neighborhood` capturaram grandes trechos da página; cidade, UF e coordenadas ficaram vazias. |
||||||
|
2. **Anunciante:** `advertiser_code` e `creci` frequentemente capturaram texto extenso; `advertiser_name` ficou vazio. |
||||||
|
3. **Dados estruturados:** o `json_ld` foi preservado como array vazio em todos os anúncios. |
||||||
|
4. **Escala numérica:** pelo menos um preço e uma área são claramente suspeitos. |
||||||
|
5. **Fotos:** três falhas foram registradas de modo consistente, sem arquivo local. |
||||||
|
6. **Execuções vazias:** duas execuções `ok` não coletaram páginas. Sem motivo/configuração persistidos, não é possível diferenciar teste, filtro vazio ou falha silenciosa. |
||||||
|
7. **Filtro do Mangueiral:** a URL coletada inclui casas de quatro quartos e não identifica planta original. |
||||||
|
|
||||||
|
## 8. Limitações para qualquer conclusão imobiliária |
||||||
|
|
||||||
|
- Um único dia não permite análise histórica ou tendência. |
||||||
|
- A amostra cobre apenas DF Imóveis e duas buscas específicas. |
||||||
|
- Preço anunciado não é preço negociado. |
||||||
|
- Não há transações, status comprovado de venda ou evidência de fechamento. |
||||||
|
- Não há imóveis físicos deduplicados. |
||||||
|
- Não há localização analítica confiável por QC, quadra ou bloco. |
||||||
|
- Não há conceito comparável de área. |
||||||
|
- O estado de reforma, planta original, elevador, garagem, andar e qualidade funcional não está modelado de forma suficiente. |
||||||
|
- Para Jardins Mangueiral, `bedrooms = 3` não valida planta original de três quartos. |
||||||
|
|
||||||
|
Portanto, ainda não é apropriado produzir medianas regionais, comparáveis, preço por m², tempo de anúncio, liquidez ou recomendações de visita com base apenas neste banco. |
||||||
|
|
||||||
|
## 9. Próximas ações recomendadas |
||||||
|
|
||||||
|
Antes da análise dos imóveis: |
||||||
|
|
||||||
|
1. corrigir e testar a extração de endereço, bairro, anunciante e CRECI usando os HTMLs já salvos, sem recrawling; |
||||||
|
2. criar uma derivação reproduzível fora do banco original e preservar os valores brutos; |
||||||
|
3. separar áreas privativa, útil, construída, total e de terreno, deixando nulo o conceito não comprovado; |
||||||
|
4. criar regras explícitas de validação para preço e área, mantendo os outliers identificados em quarentena; |
||||||
|
5. filtrar o Mangueiral por planta original de três quartos, não apenas pelo número anunciado; |
||||||
|
6. coletar múltiplas datas e auditar a cobertura por busca antes de estimar permanência, entradas ou saídas; |
||||||
|
7. numa evolução aprovada do coletor, adicionar `source_id`, `run_id` nos snapshots, referência ao HTML bruto histórico e versão/configuração da coleta; |
||||||
|
8. construir `property_candidates` apenas em uma camada derivada, com confiança e revisão, sem fundir registros no banco original. |
||||||
|
|
||||||
|
## 10. Consultas de reprodução |
||||||
|
|
||||||
|
Todas devem ser executadas com `sqlite3 -readonly dfimoveis_data/dfimoveis.sqlite3` ou conexão Python URI em `mode=ro` e `PRAGMA query_only = ON`. |
||||||
|
|
||||||
|
```sql |
||||||
|
-- Objetos do esquema |
||||||
|
SELECT type, name, tbl_name, sql |
||||||
|
FROM sqlite_master |
||||||
|
ORDER BY type, name; |
||||||
|
|
||||||
|
-- Contagens (executar uma tabela por vez após validar os nomes) |
||||||
|
SELECT COUNT(*) FROM crawl_runs; |
||||||
|
SELECT COUNT(*) FROM listings; |
||||||
|
SELECT COUNT(*) FROM listing_history; |
||||||
|
SELECT COUNT(*) FROM listing_searches; |
||||||
|
SELECT COUNT(*) FROM photos; |
||||||
|
|
||||||
|
-- Cobertura |
||||||
|
SELECT MIN(started_at), MAX(finished_at), COUNT(*) |
||||||
|
FROM crawl_runs; |
||||||
|
|
||||||
|
SELECT MIN(first_seen_at), MAX(last_seen_at), |
||||||
|
COUNT(DISTINCT date(first_seen_at)) |
||||||
|
FROM listings; |
||||||
|
|
||||||
|
-- Número de snapshots por anúncio |
||||||
|
SELECT snapshots, COUNT(*) AS listings |
||||||
|
FROM ( |
||||||
|
SELECT listing_id, COUNT(*) AS snapshots |
||||||
|
FROM listing_history |
||||||
|
GROUP BY listing_id |
||||||
|
) |
||||||
|
GROUP BY snapshots; |
||||||
|
|
||||||
|
-- Completude de campos críticos |
||||||
|
SELECT |
||||||
|
COUNT(*) AS total, |
||||||
|
SUM(price_brl IS NULL) AS missing_price, |
||||||
|
SUM(area_m2 IS NULL) AS missing_area, |
||||||
|
SUM(condominium_brl IS NULL) AS missing_condominium, |
||||||
|
SUM(iptu_brl IS NULL) AS missing_iptu, |
||||||
|
SUM(latitude IS NULL OR longitude IS NULL) AS missing_coordinates |
||||||
|
FROM listings; |
||||||
|
|
||||||
|
-- Contaminação textual sem imprimir os textos |
||||||
|
SELECT |
||||||
|
SUM(length(address) > 300) AS long_address, |
||||||
|
SUM(length(neighborhood) > 100) AS long_neighborhood, |
||||||
|
SUM(length(advertiser_code) > 100) AS long_advertiser_code, |
||||||
|
SUM(length(creci) > 100) AS long_creci |
||||||
|
FROM listings; |
||||||
|
|
||||||
|
-- Integridade referencial e física |
||||||
|
PRAGMA quick_check; |
||||||
|
PRAGMA foreign_key_check; |
||||||
|
``` |
||||||
|
|
||||||
|
## 11. Implicação para a decisão de compra |
||||||
|
|
||||||
|
Esta avaliação reduz o risco de transformar uma coleta tecnicamente bem-sucedida em falsa precisão de mercado. O próximo ganho real não virá de comparar preços imediatamente, mas de corrigir localização e semântica de área, acumular histórico e construir uma deduplicação reversível. Até lá, os registros são úteis como inventário bruto de anúncios e arquivos, não como avaliação consolidada de imóveis físicos. |
||||||
@ -0,0 +1,184 @@ |
|||||||
|
# Ranking preliminar — casas no Jardins Mangueiral |
||||||
|
|
||||||
|
**Data de referência do snapshot:** 17/07/2026 |
||||||
|
**Relatório revisado:** 17/07/2026 |
||||||
|
**Unidade principal:** imóvel físico provável, não snapshot nem anúncio isolado |
||||||
|
**Finalidade:** priorizar visitas; não estimar preço negociado nem substituir vistoria ou validação documental |
||||||
|
|
||||||
|
## Pergunta |
||||||
|
|
||||||
|
Quais são as dez casas do Jardins Mangueiral que mais merecem atenção segundo as preferências e regras do projeto, dando às fotografias importância comparável à dos dados estruturados? |
||||||
|
|
||||||
|
## Universo e método |
||||||
|
|
||||||
|
Foram avaliados os 94 anúncios acumulados encontrados pela busca de casas no Jardins Mangueiral: 84 estavam ativos e 10 inativos no snapshot consistente de 17/07/2026. A base cobre um único portal. Há 159 anúncios nas duas regiões, 295 snapshots de conteúdo e 6.272 mídias; 133 anúncios têm dois snapshots, mas quatro mudanças foram materialmente relevantes nos campos imobiliários e apenas uma delas foi preço. |
||||||
|
|
||||||
|
Como o banco estava em WAL, ele foi aberto com `mode=ro`, sem `immutable=1`, dentro de uma transação de leitura consistente e com `PRAGMA query_only = ON`. Entre os anúncios ativos, 80 informam três quartos e preço plausível entre R$ 450 mil e R$ 1,15 milhão. O anúncio de R$ 1,15 milhão descreve outra implantação, no condomínio American Garden, e não a casa-padrão de 68 m² das QCs; não é comparável ao universo prioritário. Esses números descrevem anúncios, não imóveis únicos nem preços negociados. |
||||||
|
|
||||||
|
Não foi calculado preço por metro quadrado: os campos e descrições misturam área privativa, construída, total e de terreno. Tampouco foi criada pontuação numérica, porque os pesos não foram aprovados. A ordem é uma **prioridade de visita ajustada por risco**, considerando em conjunto: |
||||||
|
|
||||||
|
1. compatibilidade provável com a planta original de três quartos; |
||||||
|
2. funcionalidade, circulação, armazenamento e flexibilidade de longo prazo; |
||||||
|
3. qualidade real da conversão do quintal, sem penalizar fechamento automaticamente; |
||||||
|
4. luz e ventilação aparentes, conservação e coerência dos acabamentos; |
||||||
|
5. risco e transtorno de adequação; |
||||||
|
6. preço, condomínio, IPTU, vagas e área anunciados; |
||||||
|
7. QCs citadas nos relatos de moradores, apenas como evidência anedótica; |
||||||
|
8. completude, coerência e confiabilidade das fotos. |
||||||
|
|
||||||
|
### Filtro da planta original |
||||||
|
|
||||||
|
Os dez imóveis abaixo têm fachada, posição de escada, distribuição e dormitórios fotografados compatíveis com a tipologia padrão de três quartos, e nenhum deles é descrito como conversão de dois para três quartos. Isso sustenta apenas a classificação **planta original provável**. A confirmação exige planta, matrícula ou documentação da unidade. O anúncio 1374012, por exemplo, foi retirado da disputa apesar do preço baixo porque a própria descrição informa “3 quartos sendo 1 adaptado”. |
||||||
|
|
||||||
|
### Revisão fotográfica |
||||||
|
|
||||||
|
Em 17/07/2026, a primeira foto de todos os escolhidos foi auditada especificamente para marcas de “vendido” ou “negócio fechado”. Quatro anúncios que chegaram a integrar versões do ranking foram removidos por esse critério: `1346231`, `1349519`, `1351729` e `1366801`. |
||||||
|
|
||||||
|
A revisão anterior havia examinado 1.892 arquivos distintos. Nesta atualização foram revisadas as imagens iniciais dos novos anúncios elegíveis e, em detalhe, 15 imagens coerentes do novo finalista `1397378`; imagens posteriores da galeria que pertencem visualmente a outros imóveis foram descartadas como contaminação. As plantas oficiais de duas e três unidades de três quartos e os mapas locais em `resources/` também foram confrontados com a galeria e a QC. |
||||||
|
|
||||||
|
As fotos foram usadas para avaliar coerência da planta, presença dos três dormitórios, circulação, luz, ventilação aparente, conservação, cozinha, banheiros, lavanderia, quintal, integração social e qualidade das ampliações. Elas não validam elétrica, hidráulica, impermeabilização, estrutura, calor, ruído ou regularização. |
||||||
|
|
||||||
|
As galerias baixadas frequentemente continuam, depois das imagens do imóvel, com recomendações do portal, fotos de outras casas, selos de “vendido”, publicidade e logos. Esses arquivos foram vistos, identificados pela quebra de coerência visual e desconsiderados. Por isso, quantidade bruta de fotos não foi tratada como qualidade de evidência, e hashes compartilhados foram usados apenas para triagem manual de duplicidade. |
||||||
|
|
||||||
|
## Ranking |
||||||
|
|
||||||
|
### 1. Anúncio 1279892 — QC 10 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 595.000; 68 m² no campo estruturado e 107 m² na descrição; condomínio R$ 383; IPTU R$ 1.050; 3 quartos, 1 suíte; 1 vaga informada no banco. |
||||||
|
- **Por que lidera:** oferece o melhor equilíbrio entre preço, localização considerada boa nos relatos, reforma pronta e planta visualmente coerente. A diferença para casas mais caras parece estar mais na ausência de mobiliário do que em obra obrigatória. |
||||||
|
- **Fotos:** ambientes claros, sala ampla, cozinha planejada bem executada, banheiros atualizados e três dormitórios reconhecíveis. O fundo combina área coberta e trechos abertos, preservando luz e ventilação; não aparece uma segunda cozinha completa redundante. |
||||||
|
- **Pendências:** esclarecer os conceitos de 68 e 107 m², a DCE citada, regularização das ampliações, número jurídico de vagas e impermeabilização da cobertura translúcida. |
||||||
|
- **Ação:** candidato forte; visitar primeiro. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-rua-f-1279892 |
||||||
|
|
||||||
|
### 2. Anúncio 1350367 — QC 6 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 569.000; 68 m² no banco, 107 m² totais e terreno de 131 m² na descrição; condomínio R$ 320; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que está no topo:** preço baixo para o grupo final, QC considerada alternativa prática e ampliação social que parece efetivamente utilizável. |
||||||
|
- **Fotos:** casa vazia, clara e aparentemente reformada; banheiros e pisos coerentes; três dormitórios aparentes. O quintal foi convertido em uma varanda ampla com churrasqueira e bancada, mas mantém abertura lateral e circulação, em vez de virar cômodo escuro ou segunda cozinha fechada. |
||||||
|
- **Pendências:** confirmar área construída, licenças, ventilação e calor sob a cobertura, drenagem, elétrica da ampliação e o que falta de marcenaria/mobiliário. |
||||||
|
- **Ação:** candidato forte; visitar. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-rua-c-1350367 |
||||||
|
|
||||||
|
### 3. Anúncio 1346017 — QC 10 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 577.000; 68 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que se destaca:** casa pronta, luminosa e pouco personalizada por preço competitivo, em QC considerada boa nos relatos. |
||||||
|
- **Fotos:** sala e jantar funcionais, cozinha planejada, três quartos claros e banheiros conservados. O quintal permanece majoritariamente aberto, com cobertura leve e uso social simples; é uma das soluções menos arriscadas para ventilação e manutenção. |
||||||
|
- **Pendências:** confirmar proteção contra chuva, privacidade, área de serviço, taxas, orientação solar e regularização de qualquer cobertura. |
||||||
|
- **Ação:** candidato forte; visitar. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017 |
||||||
|
|
||||||
|
### 4. Anúncio 1397378 — QC 8 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 600.000; 68 m²; condomínio R$ 316,14; IPTU R$ 1.119,67; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que se destaca:** QC 8 tem acesso prático a comércio e serviços nos mapas fornecidos, e a casa combina preço intermediário com implantação próxima da planta original de três quartos. |
||||||
|
- **Fotos:** sala e cozinha claras, marcenaria utilizável, três dormitórios aparentes e escada/andar superior compatíveis com a planta de 68 m². O fundo mantém trecho aberto, corredor lateral e cobertura parcial, sem transformar todo o quintal em segunda cozinha. Há sinais de acabamento simples e manutenção pontual, mas não de reforma integral imediata. |
||||||
|
- **Pendências:** confirmar planta e documentos, origem das marcas/acabamentos, drenagem linear, impermeabilização da cobertura, orientação solar e itens de marcenaria que permanecem. Parte final da galeria está contaminada por fotos de outros imóveis e foi desconsiderada. |
||||||
|
- **Ação:** candidato forte; visitar depois dos três primeiros. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-8-rua-b-1397378 |
||||||
|
|
||||||
|
### 5. Anúncio 1359960 — QC 7 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 610.000; 68 m²; condomínio R$ 339; IPTU R$ 1.128,19; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que entra:** QC considerada alternativa prática e conversão do quintal que cria uma sala social ampla, em vez de apenas instalar equipamentos com rótulo “gourmet”. |
||||||
|
- **Fotos:** boa sala frontal, cozinha planejada, três quartos aparentes e grande espaço coberto nos fundos, hoje usado para descanso e convivência. Não foi identificada segunda cozinha completa; a ampliação parece flexível e mobiliável. |
||||||
|
- **Pendências:** o fechamento é extenso; testar calor, ventilação cruzada, iluminação noturna, exaustão, acústica e impermeabilização antes de valorizá-lo. |
||||||
|
- **Ação:** visitar com foco no conforto ambiental da ampliação. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-7-1359960 |
||||||
|
|
||||||
|
### 6. Anúncio 1319996 — QC 14 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 649.000; 68 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que se destaca:** uma das reformas mais completas e coerentes, com placas solares e boa separação entre convivência e lavanderia. |
||||||
|
- **Fotos:** acabamento uniforme, marcenaria abundante, três dormitórios aparentes, banheiros atualizados e área externa coberta com bancada, churrasqueira, abertura lateral e circulação suficiente para receber pessoas. |
||||||
|
- **Pendências:** QC 14 não está entre as QCs priorizadas pelos relatos; confirmar taxas, homologação das placas, ventilação/exaustão, manutenção da cobertura e escopo técnico da reforma. |
||||||
|
- **Ação:** visitar como referência de imóvel pronto. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1319996 |
||||||
|
|
||||||
|
### 7. Anúncio 1324014 — QC 12 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 649.000; 68 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; vaga não extraída. |
||||||
|
- **Por que entra:** acabamento interno e marcenaria fortes, com área social coberta bem executada e condição aparente de ocupação sem reforma relevante. |
||||||
|
- **Fotos:** cozinha, sala e banheiros atualizados; a área gourmet é ampla e iluminada, mas concentra churrasqueira, bancada e apoio muito próximos da cozinha principal. |
||||||
|
- **Pendências:** confirmar vaga, planta original, ventilação, exaustão, conforto térmico, drenagem, regularização da cobertura e se a duplicação de equipamentos melhora de fato a rotina. |
||||||
|
- **Ação:** visitar depois dos candidatos mais baratos e das QCs prioritárias. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014 |
||||||
|
|
||||||
|
### 8. Anúncio 1333569 — QC 6 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 650.000; 90 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que entra:** QC considerada alternativa prática e ampliação social já em uso, com abertura superior e lateral que pode mitigar o fechamento do quintal. |
||||||
|
- **Fotos:** casa ocupada, bem conservada e funcional; a área externa reúne jantar, estar e churrasqueira, com cobertura aparentemente translúcida/retrátil. Os cômodos íntimos parecem utilizáveis, embora menos bem documentados que nos primeiros colocados. |
||||||
|
- **Pendências:** confirmar se os 90 m² são comparáveis, funcionamento e manutenção da cobertura, exaustão, calor, iluminação, taxas e regularização. |
||||||
|
- **Ação:** visitar depois dos oito primeiros. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-1333569 |
||||||
|
|
||||||
|
### 9. Anúncio 1359480 — QC 4 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 535.000; 70 m²; condomínio R$ 330; IPTU ausente; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que entra:** é a alternativa de menor preço entre os finalistas habitáveis e preserva margem financeira para custos de aquisição e adequações. |
||||||
|
- **Fotos:** sala integrada utilizável, cozinha e banheiros funcionais e três dormitórios aparentes. O quintal/lavanderia têm acabamento mais simples que os líderes, mas não sugerem obra estrutural imediata nas imagens. |
||||||
|
- **Pendências:** QC 4 precisa provar conveniência logística; confirmar planta original, área de 70 m², documentação, drenagem, impermeabilização e orçamento de acabamento externo. |
||||||
|
- **Ação:** visitar como referência de valor e limite inferior de preço. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1359480 |
||||||
|
|
||||||
|
### 10. Anúncio 1356197 — QC 4 |
||||||
|
|
||||||
|
- **Anunciado:** R$ 649.000; 68 m²; condomínio R$ 316; IPTU ausente; 3 quartos, 1 suíte; 2 vagas. |
||||||
|
- **Por que entra:** casa vazia com acabamento interno forte e condição aparente de ocupação sem reforma relevante. |
||||||
|
- **Fotos:** sala, cozinha, banheiros e marcenaria bem conservados; boa parte do espaço coberto parece funcionar como garagem, com menor evidência de quintal social útil. |
||||||
|
- **Pendências:** confirmar planta original, uso e ventilação do espaço coberto, drenagem, impermeabilização, regularização e conveniência da QC 4. |
||||||
|
- **Ação:** visitar depois dos nove primeiros. |
||||||
|
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197 |
||||||
|
|
||||||
|
## Quase entraram |
||||||
|
|
||||||
|
- **[1368562](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-5-1368562), QC 5, R$ 720.000:** ampliação de aproximadamente 110 m² bem executada e ventilada por cobertura translúcida, mas o prêmio de preço e a QC menos prioritária reduzem a urgência da visita. |
||||||
|
|
||||||
|
## Quarentenas e exclusões relevantes |
||||||
|
|
||||||
|
- **1351729:** a primeira foto declara “VENDIDO” e “negócio fechado”. Isso não comprova registro ou preço da transação, mas é evidência suficiente de indisponibilidade operacional; removido do ranking de visitas por correção do usuário em 17/07/2026. |
||||||
|
- **1349519:** a primeira foto também declara “VENDIDO” e “negócio fechado”, com o código correspondente ao anúncio. Removido do ranking de visitas por correção do usuário em 17/07/2026. |
||||||
|
- **1366801:** a primeira foto declara “VENDIDO” e “negócio fechado”, com o código 1468 correspondente ao anúncio. Removido do ranking de visitas por correção do usuário em 17/07/2026. |
||||||
|
- **1346231:** a primeira foto declara “VENDIDO” e “negócio fechado”, com o código 1428 correspondente ao anúncio. Removido após auditoria sistemática das primeiras fotos em 17/07/2026. |
||||||
|
- **1350829 e 1358463:** anunciados com quatro quartos; fora do filtro. |
||||||
|
- **1374012:** a descrição declara que um dos três quartos é adaptado; não há prova suficiente de planta original de três quartos. |
||||||
|
- **1351663:** preço estruturado de R$ 570 milhões, erro evidente de escala; excluído até correção. |
||||||
|
- **1369473 e 1336887:** galerias dominadas por material genérico ou de outros anúncios, sem evidência suficiente do imóvel. |
||||||
|
- **1301400 e 1201070:** fotos com marcação de vendido/permuta; tratadas como inconsistência, não prova de venda. |
||||||
|
- **1041635 e 1383513:** mesmo imóvel provável pelas fotos; a repetição não foi contada como duas opções. |
||||||
|
- **1239730, 1242337, 1294027, 1359192, 1359662 e 1367341:** grupo de republicações prováveis da mesma unidade; a oferta não foi inflada. |
||||||
|
- **1354016 e 1377683:** duplicata provável após revisão visual; seria uma única opção, não duas. |
||||||
|
- **1356153, 1358854 e 1358920:** grupo provável de uma mesma casa vazia. |
||||||
|
- **1348449 e 1351461:** descrições e composição praticamente idênticas na QC 13; tratar como mesmo imóvel até confirmação. |
||||||
|
|
||||||
|
## Qualidade e limitações |
||||||
|
|
||||||
|
- O banco contém anúncios e snapshots de conteúdo, não imóveis físicos confirmados. Os dois pontos observados ainda são insuficientes para tendência ou tempo real de mercado. |
||||||
|
- Entre os 84 anúncios ativos do Mangueiral, condomínio falta em 40 e IPTU em 74; não foi possível comparar custo mensal com consistência. |
||||||
|
- Área existe em todos os registros, mas não é semanticamente comparável. Os próprios finalistas exibem divergências entre banco e descrição. |
||||||
|
- A busca e as galerias contêm material contaminante do portal. Duplicidades foram classificadas manualmente e permanecem probabilísticas. |
||||||
|
- Anúncio, foto e descrição não confirmam planta original, regularização da ampliação, documentação, estrutura ou sistemas. |
||||||
|
- As preferências por QC vêm de relatos anedóticos e precisam ser testadas em deslocamentos e visitas reais. |
||||||
|
- A referência financeira de R$ 650 mil a R$ 750 mil não foi assumida como atual. O ranking prioriza visitas, não define capacidade de compra ou preço máximo. |
||||||
|
|
||||||
|
## Verificações obrigatórias nas visitas |
||||||
|
|
||||||
|
1. solicitar planta original, matrícula, habite-se e documentos das ampliações; |
||||||
|
2. medir os três quartos e conferir posição da escada, fachada e implantação; |
||||||
|
3. testar calor, ventilação, luz e ruído com portas e janelas fechadas e abertas; |
||||||
|
4. inspecionar telhado, rufos, calhas, impermeabilização, drenagem e sinais de infiltração; |
||||||
|
5. testar quadro elétrico, aterramento, tomadas, hidráulica, pressão e escoamento; |
||||||
|
6. verificar se a área gourmet duplica a cozinha ou realmente melhora a rotina; |
||||||
|
7. confirmar condomínio, IPTU, vagas, taxas extras, regras de obra e atas; |
||||||
|
8. percorrer a QC em horários relevantes para comércio, trânsito, segurança, ruído e estacionamento; |
||||||
|
9. só depois estimar adequação e definir preço máximo de negociação. |
||||||
|
|
||||||
|
## Reprodutibilidade |
||||||
|
|
||||||
|
- `analysis.py`: extrai banco e HTMLs em leitura, produzindo TSV. |
||||||
|
- `photo_similarity.py`: sinaliza pares por hash visual simples; exige revisão humana e sofre com imagens genéricas do portal. |
||||||
|
- `query.sql`: consultas principais de universo e qualidade. |
||||||
|
- `parameters.json`: parâmetros e fingerprint do snapshot. |
||||||
|
- `results.csv`: ranking em formato tabular. |
||||||
@ -0,0 +1,130 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
"""Read-only extraction helper for the Jardins Mangueiral ranking.""" |
||||||
|
|
||||||
|
from __future__ import annotations |
||||||
|
|
||||||
|
import argparse |
||||||
|
import gzip |
||||||
|
import re |
||||||
|
import sqlite3 |
||||||
|
import sys |
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
from bs4 import BeautifulSoup |
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2] |
||||||
|
DB_PATH = ROOT / "dfimoveis_data" / "dfimoveis.sqlite3" |
||||||
|
DATA_ROOT = DB_PATH.parent |
||||||
|
SEARCH_FRAGMENT = "/jardins-mangueiral/" |
||||||
|
|
||||||
|
|
||||||
|
def clean(value: str | None) -> str: |
||||||
|
return re.sub(r"\s+", " ", value or "").strip() |
||||||
|
|
||||||
|
|
||||||
|
def sanitize_description(value: str) -> str: |
||||||
|
value = re.sub(r"\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b", "[email]", value) |
||||||
|
value = re.sub( |
||||||
|
r"(?:\(\d{2}\)|\b\d{2}\b)\s*(?:\d[\s.-]*){5,11}\d", |
||||||
|
"[telefone]", |
||||||
|
value, |
||||||
|
) |
||||||
|
value = re.sub(r"\bCRECI\b.{0,30}", "", value, flags=re.IGNORECASE) |
||||||
|
return clean(value) |
||||||
|
|
||||||
|
|
||||||
|
def parse_html(relative_path: str) -> tuple[str, str, str]: |
||||||
|
with gzip.open(DATA_ROOT / relative_path, "rt", encoding="utf-8", errors="replace") as fh: |
||||||
|
soup = BeautifulSoup(fh.read(), "lxml") |
||||||
|
|
||||||
|
description_node = soup.select_one("div.assined-imv") |
||||||
|
description = "" |
||||||
|
if description_node: |
||||||
|
for node in description_node.select("span, a, button"): |
||||||
|
node.decompose() |
||||||
|
description = sanitize_description(description_node.get_text(" ", strip=True)) |
||||||
|
|
||||||
|
details: list[str] = [] |
||||||
|
for item in soup.select("ul.details-text li"): |
||||||
|
text = clean(item.get_text(" ", strip=True)) |
||||||
|
if text and text not in details: |
||||||
|
details.append(text) |
||||||
|
|
||||||
|
address_node = soup.select_one('[itemprop="address"]') |
||||||
|
address = clean(address_node.get_text(" ", strip=True) if address_node else "") |
||||||
|
return description, " | ".join(details), address |
||||||
|
|
||||||
|
|
||||||
|
def main() -> int: |
||||||
|
parser = argparse.ArgumentParser() |
||||||
|
parser.add_argument("--ids-only", action="store_true") |
||||||
|
args = parser.parse_args() |
||||||
|
|
||||||
|
connection = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
connection.execute("BEGIN") |
||||||
|
rows = connection.execute( |
||||||
|
""" |
||||||
|
SELECT |
||||||
|
l.listing_id, l.url, l.price_brl, l.condominium_brl, l.iptu_brl, |
||||||
|
l.area_m2, l.bedrooms, l.suites, l.parking_spaces, |
||||||
|
l.raw_html_path, COUNT(p.source_url) AS photo_count |
||||||
|
FROM listings AS l |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
LEFT JOIN photos AS p USING (listing_id) |
||||||
|
WHERE instr(s.search_url, ?) > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
GROUP BY l.listing_id |
||||||
|
ORDER BY l.price_brl, l.listing_id |
||||||
|
LIMIT 150 |
||||||
|
""", |
||||||
|
(SEARCH_FRAGMENT,), |
||||||
|
).fetchall() |
||||||
|
connection.close() |
||||||
|
|
||||||
|
if args.ids_only: |
||||||
|
for row in rows: |
||||||
|
print(row[0]) |
||||||
|
return 0 |
||||||
|
|
||||||
|
print( |
||||||
|
"listing_id\tprice_brl\tcondominium_brl\tiptu_brl\tarea_m2\tbedrooms\t" |
||||||
|
"suites\tparking_spaces\tphoto_count\taddress\tdetails\tdescription\turl" |
||||||
|
) |
||||||
|
for row in rows: |
||||||
|
( |
||||||
|
listing_id, |
||||||
|
url, |
||||||
|
price, |
||||||
|
condominium, |
||||||
|
iptu, |
||||||
|
area, |
||||||
|
bedrooms, |
||||||
|
suites, |
||||||
|
parking, |
||||||
|
raw_path, |
||||||
|
photo_count, |
||||||
|
) = row |
||||||
|
description, details, address = parse_html(raw_path) |
||||||
|
values = ( |
||||||
|
listing_id, |
||||||
|
price, |
||||||
|
condominium, |
||||||
|
iptu, |
||||||
|
area, |
||||||
|
bedrooms, |
||||||
|
suites, |
||||||
|
parking, |
||||||
|
photo_count, |
||||||
|
address, |
||||||
|
details, |
||||||
|
description, |
||||||
|
url, |
||||||
|
) |
||||||
|
print("\t".join("" if value is None else clean(str(value)) for value in values)) |
||||||
|
return 0 |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
sys.exit(main()) |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
{ |
||||||
|
"analysis_date": "2026-07-17", |
||||||
|
"listing_data_reference_date": "2026-07-17", |
||||||
|
"database_path": "dfimoveis_data/dfimoveis.sqlite3", |
||||||
|
"database_main_file_sha256": "7a5102a62bc66a344bd9440fa36301d10a57711bcdd263b767b65e88f70957f4", |
||||||
|
"logical_snapshot_sha256": "fe604aab8af7f67c833e6de2a0fc81c7dc2950ca440ad7b23ebd03d0c2546188", |
||||||
|
"database_mode": "read_only_transaction_with_wal", |
||||||
|
"search_url_fragment": "/jardins-mangueiral/", |
||||||
|
"listing_limit": 150, |
||||||
|
"observed_listing_count": 94, |
||||||
|
"active_listing_count": 84, |
||||||
|
"inactive_listing_count": 10, |
||||||
|
"database_crawl_run_count": 16, |
||||||
|
"new_listing_count_since_previous_report": 3, |
||||||
|
"active_plausible_price_three_bedroom_count": 80, |
||||||
|
"ranking_unit": "probable_physical_property", |
||||||
|
"numeric_score_used": false, |
||||||
|
"original_three_bedroom_requires_document_confirmation": true, |
||||||
|
"photo_first_pass_limit_per_listing": 16, |
||||||
|
"photo_first_pass_files": 1449, |
||||||
|
"photo_finalist_count": 19, |
||||||
|
"photo_finalist_files": 747, |
||||||
|
"photo_files_reviewed_distinct_cumulative": 1907, |
||||||
|
"new_finalist_photos_reviewed": 15, |
||||||
|
"plans_reviewed": 2, |
||||||
|
"map_captures_reviewed": 6, |
||||||
|
"first_photo_sale_marker_audit_count": 10, |
||||||
|
"visually_unavailable_listing_ids": ["1346231", "1349519", "1351729", "1366801"] |
||||||
|
} |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
"""Suggest duplicate Mangueiral ads from visually similar local photos. |
||||||
|
|
||||||
|
Uses a simple difference hash as a screening signal. Results are candidates for |
||||||
|
manual review, never automatic proof that two ads describe the same property. |
||||||
|
""" |
||||||
|
|
||||||
|
from __future__ import annotations |
||||||
|
|
||||||
|
import sqlite3 |
||||||
|
from collections import defaultdict |
||||||
|
from pathlib import Path |
||||||
|
|
||||||
|
from PIL import Image, UnidentifiedImageError |
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2] |
||||||
|
DB_PATH = ROOT / "dfimoveis_data" / "dfimoveis.sqlite3" |
||||||
|
DATA_ROOT = DB_PATH.parent |
||||||
|
|
||||||
|
|
||||||
|
def difference_hash(path: Path) -> int | None: |
||||||
|
try: |
||||||
|
with Image.open(path) as image: |
||||||
|
pixels = list(image.convert("L").resize((9, 8)).getdata()) |
||||||
|
except (OSError, UnidentifiedImageError): |
||||||
|
return None |
||||||
|
value = 0 |
||||||
|
for row in range(8): |
||||||
|
offset = row * 9 |
||||||
|
for column in range(8): |
||||||
|
value = (value << 1) | (pixels[offset + column] > pixels[offset + column + 1]) |
||||||
|
return value |
||||||
|
|
||||||
|
|
||||||
|
def main() -> None: |
||||||
|
connection = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True) |
||||||
|
connection.execute("PRAGMA query_only = ON") |
||||||
|
connection.execute("BEGIN") |
||||||
|
rows = connection.execute( |
||||||
|
""" |
||||||
|
WITH mangueiral AS ( |
||||||
|
SELECT s.listing_id |
||||||
|
FROM listing_searches AS s |
||||||
|
JOIN listings AS l USING (listing_id) |
||||||
|
WHERE instr(search_url, '/jardins-mangueiral/') > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
), uncommon AS ( |
||||||
|
SELECT sha256 |
||||||
|
FROM photos |
||||||
|
WHERE sha256 IS NOT NULL |
||||||
|
GROUP BY sha256 |
||||||
|
HAVING COUNT(DISTINCT listing_id) <= 5 |
||||||
|
) |
||||||
|
SELECT p.listing_id, p.local_path |
||||||
|
FROM photos AS p |
||||||
|
JOIN mangueiral AS m USING (listing_id) |
||||||
|
JOIN uncommon AS u USING (sha256) |
||||||
|
WHERE p.local_path IS NOT NULL |
||||||
|
ORDER BY p.listing_id, p.ordinal |
||||||
|
""" |
||||||
|
).fetchall() |
||||||
|
connection.close() |
||||||
|
|
||||||
|
hashes: dict[str, list[int]] = defaultdict(list) |
||||||
|
for listing_id, relative_path in rows: |
||||||
|
value = difference_hash(DATA_ROOT / relative_path) |
||||||
|
if value is not None: |
||||||
|
hashes[listing_id].append(value) |
||||||
|
|
||||||
|
ids = sorted(hashes) |
||||||
|
print("listing_a\tlisting_b\tvisually_similar_photos") |
||||||
|
for index, first_id in enumerate(ids): |
||||||
|
for second_id in ids[index + 1 :]: |
||||||
|
available = list(hashes[second_id]) |
||||||
|
matches = 0 |
||||||
|
for first_hash in hashes[first_id]: |
||||||
|
distances = [(first_hash ^ second_hash).bit_count() for second_hash in available] |
||||||
|
if distances and min(distances) <= 6: |
||||||
|
available.pop(distances.index(min(distances))) |
||||||
|
matches += 1 |
||||||
|
if matches >= 3: |
||||||
|
print(f"{first_id}\t{second_id}\t{matches}") |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
main() |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
PRAGMA query_only = ON; |
||||||
|
|
||||||
|
-- Universo capturado pela busca do Jardins Mangueiral. |
||||||
|
SELECT |
||||||
|
l.listing_id, |
||||||
|
l.url, |
||||||
|
l.price_brl, |
||||||
|
l.condominium_brl, |
||||||
|
l.iptu_brl, |
||||||
|
l.area_m2, |
||||||
|
l.bedrooms, |
||||||
|
l.suites, |
||||||
|
l.parking_spaces, |
||||||
|
l.first_seen_at, |
||||||
|
l.last_seen_at, |
||||||
|
l.raw_html_path |
||||||
|
FROM listings AS l |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/jardins-mangueiral/') > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
ORDER BY l.price_brl, l.listing_id |
||||||
|
LIMIT 150; |
||||||
|
|
||||||
|
-- Cardinalidade de fotos por anúncio. A contagem inclui material genérico do portal. |
||||||
|
SELECT p.listing_id, COUNT(*) AS photo_count |
||||||
|
FROM photos AS p |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/jardins-mangueiral/') > 0 |
||||||
|
AND EXISTS ( |
||||||
|
SELECT 1 FROM listings AS l |
||||||
|
WHERE l.listing_id = p.listing_id AND l.inactive_at IS NULL |
||||||
|
) |
||||||
|
GROUP BY p.listing_id |
||||||
|
ORDER BY p.listing_id |
||||||
|
LIMIT 150; |
||||||
|
|
||||||
|
-- Número de snapshots por anúncio e cobertura temporal. |
||||||
|
SELECT |
||||||
|
h.listing_id, |
||||||
|
COUNT(*) AS snapshot_count, |
||||||
|
MIN(h.captured_at) AS first_capture, |
||||||
|
MAX(h.captured_at) AS last_capture |
||||||
|
FROM listing_history AS h |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/jardins-mangueiral/') > 0 |
||||||
|
AND EXISTS ( |
||||||
|
SELECT 1 FROM listings AS l |
||||||
|
WHERE l.listing_id = h.listing_id AND l.inactive_at IS NULL |
||||||
|
) |
||||||
|
GROUP BY h.listing_id |
||||||
|
ORDER BY h.listing_id |
||||||
|
LIMIT 150; |
||||||
|
|
||||||
|
-- Campos ausentes no universo. |
||||||
|
SELECT |
||||||
|
COUNT(*) AS listing_count, |
||||||
|
SUM(l.condominium_brl IS NULL) AS missing_condominium, |
||||||
|
SUM(l.iptu_brl IS NULL) AS missing_iptu, |
||||||
|
SUM(l.area_m2 IS NULL) AS missing_area, |
||||||
|
SUM(l.parking_spaces IS NULL) AS missing_parking |
||||||
|
FROM listings AS l |
||||||
|
JOIN listing_searches AS s USING (listing_id) |
||||||
|
WHERE instr(s.search_url, '/jardins-mangueiral/') > 0 |
||||||
|
AND l.inactive_at IS NULL |
||||||
|
LIMIT 1; |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
httpx[http2]>=0.27,<1 |
||||||
|
beautifulsoup4>=4.12,<5 |
||||||
|
lxml>=5,<7 |
||||||
|
# Used by the report photo-similarity screening scripts. |
||||||
|
Pillow>=10,<13 |
||||||
|
# Default browser automation (uses the locally installed Microsoft Edge): |
||||||
|
playwright>=1.49,<2 |
||||||
|
cloudscraper>=1.2,<2 |
||||||
|
patchright>=1.0,<2 |
||||||
|
After Width: | Height: | Size: 632 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 950 KiB |
|
After Width: | Height: | Size: 548 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 197 KiB |
@ -0,0 +1,3 @@ |
|||||||
|
# One complete DFImoveis search URL per line |
||||||
|
https://www.dfimoveis.com.br/venda/df/brasilia/jardins-mangueiral/casa/3,4-quartos |
||||||
|
https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# change working directory |
||||||
|
Set-Location E:\forge\python\house-quest |
||||||
|
|
||||||
|
# install dependencies |
||||||
|
.\.venv-scraper\Scripts\python.exe -m pip install -r requirements-dfimoveis.txt |
||||||
|
|
||||||
|
# clean up browser profile |
||||||
|
Remove-Item -Recurse -Force .\dfimoveis_data\browser-profile |
||||||
|
|
||||||
|
# run Chrome with remote debugging enabled |
||||||
|
& "C:\Program Files\Google\Chrome\Application\chrome.exe"` |
||||||
|
--remote-debugging-port=9222 ` |
||||||
|
--user-data-dir="E:\forge\python\house-quest\dfimoveis_data\browser-profile" |
||||||
|
|
||||||
|
# scraper execution command |
||||||
|
.\.venv-scraper\Scripts\python.exe .\dfimoveis_scraper.py ` |
||||||
|
--search-file .\search_list.txt ` |
||||||
|
--output .\dfimoveis_data ` |
||||||
|
--attach-chrome |
||||||