@ -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 |
||||