Compare commits

...

13 Commits

Author SHA1 Message Date
e9856861d5 ⬆️ upgrade LBC parser
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-16 08:55:22 +01:00
0221064b1a 🚑 Repair Safti parser
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-14 14:32:09 +01:00
c0d61cafdd 🚑 Repair after upgrade
Some checks failed
continuous-integration/drone/push Build is failing
2022-11-30 14:07:02 +01:00
d982682a54 ⬆️ Upgrade from Laravel 7 to 8 for PHP 8
Some checks failed
continuous-integration/drone/push Build is failing
2022-11-30 14:01:29 +01:00
516cb44c86 💚 Ajoute la génération de clef pour les tests
Some checks failed
continuous-integration/drone/push Build is failing
2021-06-22 17:35:33 +02:00
37db1ff3bc 💚 Utilise le bon nom de fichier
Some checks failed
continuous-integration/drone/push Build is failing
Ne jamais copier/coller une doc sans TOUT vérifier !
2021-06-22 17:18:08 +02:00
749de36e2f 💚 Tente de fixer le problème de version de PHP de Drone
Some checks failed
continuous-integration/drone/push Build is failing
2021-06-22 17:13:58 +02:00
b1fc84abab 👷 Ajoute la CI par Drone
Some checks failed
continuous-integration/drone/push Build is failing
2021-06-22 16:55:16 +02:00
fc9bbd9e4d 🐛 Corrige le parsing du prix OuestFrance
Fix #23
2021-05-06 11:04:30 +02:00
a9ee66a2d4 🐛 Ne télécharge pas les images quand il n'y en a pas 2021-02-12 16:17:43 +01:00
3a5a9e93c8 ⬆️ Met à jour les dépendances PHP 2021-02-12 16:16:53 +01:00
6704dd5ff6 💚 Fix la dernière CI 2021-02-03 16:45:56 +01:00
25d18c7184 👽 Ajoute le scraping de SeLoger car l'API ne fonctionne plus
Fix #22
2021-02-03 16:38:01 +01:00
13 changed files with 3400 additions and 1958 deletions

16
.drone.yml Normal file
View File

@ -0,0 +1,16 @@
kind: pipeline
name: default
steps:
- name: install
pull: if-not-exists
image: composer
commands:
- cp .env.example .env
- composer install --ignore-platform-reqs --prefer-dist
- php artisan key:generate
- name: test
image: php:7
commands:
- vendor/bin/phpunit --configuration phpunit.xml

View File

@ -2,6 +2,8 @@
namespace App\Exceptions;
use Exception;
class UnknownParser extends Exception
{

View File

@ -73,20 +73,22 @@ class Home extends Model implements Feedable
{
// Download images on creation
static::created(static function ($home) {
Artisan::call('app:downloadimage ' . $home->id);
if (!empty($home->pictures)) {
Artisan::call('app:downloadimage ' . $home->id);
}
});
}
/**
* @inheritDoc
*/
public function toFeedItem()
public function toFeedItem(): FeedItem
{
return FeedItem::create()
->id($this->id)
->title($this->title)
->summary($this->excerpt())
->author('Shikiryu')
->authorName('Shikiryu')
->updated($this->created_at)
->link(\route('public.view', ['slug' => $this->slug]))
;

View File

@ -27,6 +27,7 @@ abstract class Parser
* @param string $url
*
* @return \App\Parser|null
* @throws \App\Exceptions\UnknownParser
*/
public static function factory(string $url): ?Parser
{
@ -48,4 +49,75 @@ abstract class Parser
* @return \App\ParsedHome
*/
abstract public function parse(): ParsedHome;
/**
* @param int $score
*
* @return string
*/
protected function calculateDPE($score)
{
if (empty($score)) {
return 'Inconnu';
}
if ($score <= 50) {
return 'A';
}
if ($score >= 51 && $score <= 90) {
return 'B';
}
if ($score >= 91 && $score <= 150) {
return 'C';
}
if ($score >= 151 && $score <= 230) {
return 'D';
}
if ($score >= 231 && $score <= 330) {
return 'E';
}
if ($score >= 331 && $score <= 450) {
return 'F';
}
if ($score > 450) {
return 'G';
}
return 'Inconnu';
}
/**
* @param $score
*
* @return string
*/
protected function calculateGES($score)
{
if (empty($score)) {
return 'Inconnu';
}
if ($score <= 5) {
return 'A';
}
if ($score >= 6 && $score <= 10) {
return 'B';
}
if ($score >= 11 && $score <= 20) {
return 'C';
}
if ($score >= 21 && $score <= 35) {
return 'D';
}
if ($score >= 36 && $score <= 55) {
return 'E';
}
if ($score >= 56 && $score <= 80) {
return 'F';
}
if ($score > 80) {
return 'G';
}
return 'Inconnu';
}
}

View File

@ -36,76 +36,6 @@ class Orpi extends Parser
return $this->parseHTML($parsedHome, $crawler);
}
/**
* @param int $score
*
* @return string
*/
private function calculateDPE($score)
{
if (empty($score)) {
return 'Inconnu';
}
if ($score <= 50) {
return 'A';
}
if ($score >= 51 && $score <= 90) {
return 'B';
}
if ($score >= 91 && $score <= 150) {
return 'C';
}
if ($score >= 151 && $score <= 230) {
return 'D';
}
if ($score >= 231 && $score <= 330) {
return 'E';
}
if ($score >= 331 && $score <= 450) {
return 'F';
}
if ($score > 450) {
return 'G';
}
return 'Inconnu';
}
/**
* @param $score
*
* @return string
*/
private function calculateGES($score)
{
if (empty($score)) {
return 'Inconnu';
}
if ($score <= 5) {
return 'A';
}
if ($score >= 6 && $score <= 10) {
return 'B';
}
if ($score >= 11 && $score <= 20) {
return 'C';
}
if ($score >= 21 && $score <= 35) {
return 'D';
}
if ($score >= 36 && $score <= 55) {
return 'E';
}
if ($score >= 56 && $score <= 80) {
return 'F';
}
if ($score > 80) {
return 'G';
}
return 'Inconnu';
}
/**
* @param \App\ParsedHome $parsed_home
* @param \Symfony\Component\DomCrawler\Crawler $crawler

View File

@ -18,9 +18,13 @@ class OuestFrance extends Parser
$crawler = new Crawler($body);
$parsedHome = new ParsedHome();
$parsedHome->title = $crawler->filter('h1')->text();
$parsedHome->price = (int)str_replace(' ', '', $crawler->filter('#strongPrix')->text());
$parsedHome->description = $crawler->filter('#blockonDescriptif')->text();
$details = $crawler->filter('#blocCaractAnn > ul > li');
$price = $crawler->filter('#strongPrix');
if (!$price instanceof Crawler) {
$price = $details->eq(0)->filter('strong')->text();
}
$parsedHome->price = (int)str_replace(' ', '', $price->text());
$parsedHome->description = $crawler->filter('#blockonDescriptif')->text();
$parsedHome->surface = (int)str_replace(' ', '', $details->eq(2)->filter('strong')->text());
$parsedHome->garden_surface = (int)str_replace(' ', '', $details->eq(3)->filter('strong')->text());
$parsedHome->rooms = (int)str_replace(' ', '', $details->eq(4)->filter('strong')->text());

View File

@ -27,14 +27,17 @@ class Safti extends Parser
$currency = 'EUR';
$parsed_home->title = $property_single->filter('h1')->text();
$parsed_home->price = $currency_formatter->parseCurrency($property_single->filter('.property__price')->text(), $currency);
$parsed_home->city = $property_single->children()->children('div')->eq(1)->filter('p.h4')->text();
$parsed_home->price = $currency_formatter
->parseCurrency($property_single->filter('.property__price')->text(), $currency);
$parsed_home->city = $property_single?->children()?->children('div')?->eq(0)?->filter('p.h4')?->text();
$parsed_home->description = $crawler->filter('[data-testid="real-estate-annonce-single-description"]')->text();
$property__additionals = $crawler->filter('.property__additionals');
$energies = $property__additionals->filter('.energetic-indicator');
if ($energies->count() > 0) {
if ($energies->count() >= 1) {
$parsed_home->energy = substr($energies->eq(0)->text(), 0, 1);
}
if ($energies->count() >= 2) {
$parsed_home->ges = substr($energies->eq(1)->text(), 0, 1);
}
@ -58,9 +61,12 @@ class Safti extends Parser
}
});
$parsed_home->pictures = $crawler->filter('[data-testid="real-estate-mosaic-photo"]')->filter('img')->each(static function($img) {
return $img->attr('src');
});
$parsed_home->pictures = $crawler
->filter('[data-testid="real-estate-mosaic-photo"]')
->filter('img')
->each(static function ($img) {
return $img->attr('src');
});
return $parsed_home;
}

View File

@ -4,6 +4,8 @@ namespace App\Parser;
use App\ParsedHome;
use App\Parser;
use GuzzleHttp\Exception\GuzzleException;
use Symfony\Component\DomCrawler\Crawler;
/**
* Thanks to https://github.com/axeleroy/untoitpourcaramel/issues/3
@ -25,15 +27,19 @@ class SeLoger extends Parser
$token = $this->retrieveToken();
$idAnnonce = $this->getIdAnnonceFromUrl($this->url);
$url = sprintf('%s%s', self::LISTING_URL, $idAnnonce);
$request = $this->client->request(
'GET',
$url,
[
'headers' => [
'AppToken' => $token,
],
]
);
try {
$request = $this->client->request(
'GET',
$url,
[
'headers' => [
'AppToken' => $token,
],
]
);
} catch (GuzzleException $e) {
return $this->parseHTML();
}
$annonce = json_decode($request->getBody()->getContents(), true);
$parsedHome = new ParsedHome();
@ -83,4 +89,61 @@ class SeLoger extends Parser
return trim($request->getBody()->getContents(), '"');
}
/**
* @return \App\ParsedHome
*/
private function parseHTML()
{
$request = $this->client->get(
$this->url,
[
'headers' => [
'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding' => 'gzip, deflate, br',
'Accept-Language' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
]
]
);
$body = $request->getBody()->getContents();
$crawler = new Crawler($body);
$parsed_home = new ParsedHome();
$parsed_home->title = $crawler->filter('h1')->text();
$parsed_home->description = $crawler->filter('#showcase-description > div')->first()->text();
$parsed_home->price = (int) str_replace(' ', '', $crawler->filter('[class^=Summarystyled__PriceText]')->text());
$parsed_home->city = $crawler->filter('[class^=Summarystyled__Address]')->text();
$parsed_home->energy = $this->calculateDPE(
(int)$crawler->filter('[class^=Preview__PreviewTooltipValue]')->first()->text()
);
$parsed_home->ges = $this->calculateGES(
(int)$crawler->filter('[class^=Preview__PreviewTooltipValue]')->eq(1)->text()
);
$crawler
->filter('[class^=Summarystyled__TagsWrapper] > div')
->each(static function (Crawler $property____information, $i) use (&$parsed_home) {
$element = $property____information->children()->eq(1)->text();
if ('pièces' === mb_substr($element, -6)) {
$parsed_home->rooms = (int) $element;
}
if ('m²' === mb_substr($element, -2) && strpos($element, '/') === false) {
$parsed_home->surface = (int) $element;
}
});
$parsed_home->pictures = $crawler
->filter('.swiper-wrapper')
->first()
->filter('[data-background]')
->each(static function ($img) {
return $img->attr('data-background');
});
return $parsed_home;
}
}

View File

@ -8,27 +8,28 @@
],
"license": "MIT",
"require": {
"php": "^7.2.5",
"php": "^8.0",
"ext-json": "*",
"artesaos/seotools": "^1.0",
"absmoca/leboncoin": "dev-master",
"artesaos/seotools": "^0.18.0",
"emanueleminotto/simple-html-dom": "^1.5",
"fabpot/goutte": "^3.1",
"fabpot/goutte": "^4.0",
"fideloper/proxy": "^4.2",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^6.3",
"laravel/framework": "^7.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.0",
"laravel/tinker": "^2.0",
"laravel/ui": "^2.1",
"spatie/laravel-feed": "^2.7",
"spatie/laravel-query-builder": "^2.8"
"laravel/ui": "^3.0",
"spatie/laravel-feed": "^4.0",
"spatie/laravel-query-builder": "^4.0",
"ext-intl": "*"
},
"require-dev": {
"facade/ignition": "^2.0",
"facade/ignition": "^2.3.6",
"fzaninotto/faker": "^1.9.1",
"mockery/mockery": "^1.3.1",
"nunomaduro/collision": "^4.1",
"phpunit/phpunit": "^8.5"
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.0"
},
"config": {
"optimize-autoloader": true,

5003
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,18 @@ return [
'description' => 'La sélection',
'language' => 'fr-FR',
/*
* The image to display for the feed. For Atom feeds, this is displayed as
* a banner/logo; for RSS and JSON feeds, it's displayed as an icon.
* An empty value omits the image attribute from the feed.
*/
'image' => '',
/*
* The format of the feed. Acceptable values are 'rss', 'atom', or 'json'.
*/
'format' => 'atom',
/*
* The view that will render the feed.
*/
@ -31,6 +43,12 @@ return [
* The type to be used in the <link> tag
*/
'type' => 'application/atom+xml',
/*
* The content type for the feed response. Set to an empty string to automatically
* determine the correct value.
*/
'contentType' => '',
],
],
];

View File

@ -1,21 +0,0 @@
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}